From d1b67f1ef6470a0b69133679cdb0fe080a835b91 Mon Sep 17 00:00:00 2001 From: Chris Barber Date: Mon, 16 Feb 2026 22:21:05 -0600 Subject: [PATCH 01/16] Move tisdk, request, and tiapp code to node-titanium-sdk --- src/commands/sdk.js | 43 ++------- src/util/detect.js | 2 +- src/util/request.js | 28 ------ src/util/setup-screens.js | 7 +- src/util/tiapp.js | 51 ---------- src/util/tisdk.js | 196 +++----------------------------------- test/util/request.test.js | 51 ---------- test/util/tiapp.test.js | 69 -------------- test/util/tisdk.test.js | 40 -------- 9 files changed, 22 insertions(+), 465 deletions(-) delete mode 100644 src/util/request.js delete mode 100644 src/util/tiapp.js delete mode 100644 test/util/request.test.js delete mode 100644 test/util/tiapp.test.js delete mode 100644 test/util/tisdk.test.js diff --git a/src/commands/sdk.js b/src/commands/sdk.js index 07f60d4ba..6d4265187 100644 --- a/src/commands/sdk.js +++ b/src/commands/sdk.js @@ -2,7 +2,6 @@ import chalk from 'chalk'; import { TiError } from '../util/tierror.js'; import { expand } from '../util/expand.js'; import * as version from '../util/version.js'; -import { request } from '../util/request.js'; import { BusyIndicator } from '../util/busyindicator.js'; import fs from 'fs-extra'; import { mkdir } from 'node:fs/promises'; @@ -15,7 +14,10 @@ import { Transform } from 'node:stream'; import { pipeline } from 'node:stream/promises'; import { extractZip } from '../util/extract-zip.js'; import { prompt } from '../util/prompt.js'; -import { getReleases } from '../util/tisdk.js'; +import { request } from 'node-titanium-sdk/util'; +import { getTitaniumReleases } from 'node-titanium-sdk/titanium'; +import { getTitaniumBranches } from 'node-titanium-sdk/titanium'; +import { getTitaniumBranchBuilds } from 'node-titanium-sdk/titanium'; import prettyBytes from 'pretty-bytes'; import wrapAnsi from 'wrap-ansi'; @@ -141,9 +143,9 @@ SdkSubcommands.list = { const os = cli.env.os.name; const [releases, branches, branchBuilds] = (await Promise.allSettled([ - (cli.argv.releases || cli.argv.unstable) && getReleases(cli.argv.unstable), - cli.argv.branches && getBranches(), - cli.argv.branch && getBranchBuilds(cli.argv.branch, os) + (cli.argv.releases || cli.argv.unstable) && getTitaniumReleases(cli.argv.unstable), + cli.argv.branches && getTitaniumBranches(), + cli.argv.branch && getTitaniumBranchBuilds(cli.argv.branch, os) ])).map(r => { return r.status === 'fulfilled' ? r.value : new TiError(r.reason); }); @@ -891,34 +893,3 @@ SdkSubcommands.uninstall = { } } }; - -/** - * Retrieves the list of branches. - * @returns {Promise} - */ -async function getBranches() { - const res = await request('https://downloads.titaniumsdk.com/registry/branches.json', { - responseType: 'json' - }); - return Object - .entries(await res.body.json()) - .filter(([, count]) => count) - .map(([name]) => name); -} - -/** - * Retrieves the list of builds for a given branch. - * @param {String} branch - The name of the branch - * @param {String} os - The name of the current OS (osx, linux, win32) - * @returns {Promise} - */ -async function getBranchBuilds(branch, os) { - const res = await request(`https://downloads.titaniumsdk.com/registry/${branch}.json`, { - responseType: 'json' - }); - const now = Date.now(); - const results = await res.body.json(); - return results.filter(b => { - return (!b.expires || Date.parse(b.expires) > now) && b.assets.some(a => a.os === os); - }); -} diff --git a/src/util/detect.js b/src/util/detect.js index 088daddf8..500befdea 100644 --- a/src/util/detect.js +++ b/src/util/detect.js @@ -1,7 +1,7 @@ import { existsSync } from 'node:fs'; import os from 'node:os'; import { detect as jdkInfo } from './jdk.js'; -import { detectTitaniumSDKs } from './tisdk.js'; +import { detectTitaniumSDKs } from 'node-titanium-sdk/titanium'; import { join } from 'node:path'; import { pathToFileURL } from 'node:url'; import chalk from 'chalk'; diff --git a/src/util/request.js b/src/util/request.js deleted file mode 100644 index 54a8585e1..000000000 --- a/src/util/request.js +++ /dev/null @@ -1,28 +0,0 @@ -import { ticonfig } from './ticonfig.js'; - -export async function request(url, opts = {}) { - const { Agent, ProxyAgent, request: req } = await import('undici'); - const proxyUrl = ticonfig.get('cli.httpProxyServer'); - const requestTls = { - connect: { - rejectUnauthorized: ticonfig.get('cli.rejectUnauthorized', true) - } - }; - - const dispatcher = proxyUrl - ? new ProxyAgent({ - uri: proxyUrl, - requestTls - }) - : new Agent(requestTls); - - return await req(url, { - dispatcher, - reset: true, - ...opts, - headers: { - Connection: 'close', - ...opts.headers - } - }); -} diff --git a/src/util/setup-screens.js b/src/util/setup-screens.js index 7bf2a5d77..ec8890d68 100644 --- a/src/util/setup-screens.js +++ b/src/util/setup-screens.js @@ -5,9 +5,8 @@ import { expand } from './expand.js'; import { existsSync, unlinkSync, utimesSync, writeFileSync } from 'node:fs'; import { BusyIndicator } from './busyindicator.js'; import { detect } from './detect.js'; -import { request } from './request.js'; -import * as version from './version.js'; -import { detectTitaniumSDKs, getReleases } from './tisdk.js'; +import { request, version } from 'node-titanium-sdk/util'; +import { detectTitaniumSDKs, getTitaniumReleases } from 'node-titanium-sdk/titanium'; import dns from 'node:dns/promises'; import os from 'node:os'; import { join } from 'node:path'; @@ -217,7 +216,7 @@ export class SetupScreens { data.titaniumSDK = { installed: await detectTitaniumSDKs(this.config), - latest: online && (await getReleases())?.[0] || null + latest: online && (await getTitaniumReleases())?.[0] || null }; data.network = { diff --git a/src/util/tiapp.js b/src/util/tiapp.js deleted file mode 100644 index 7d5c45931..000000000 --- a/src/util/tiapp.js +++ /dev/null @@ -1,51 +0,0 @@ -import { existsSync } from 'node:fs'; -import { readFile } from 'node:fs/promises'; - -export class Tiapp { - async select1(expr, defaultValue) { - if (!this.doc) { - throw new Error('No tiapp.xml loaded'); - } - const { select1 } = await import('xpath'); - const nodes = select1(expr, this.doc); - return nodes?.firstChild?.nodeValue ?? defaultValue; - } - - async load(file) { - if (!existsSync(file)) { - throw new Error(`File not found: ${file}`); - } - - let errorMsg; - const { default: xmldom } = await import('@xmldom/xmldom'); - const parser = new xmldom.DOMParser({ - onError(type, err) { - if (type === 'fatalError') { - errorMsg = err; - } - } - }); - const str = await readFile(file, 'utf8'); - const doc = parser.parseFromString(str, 'text/xml'); - if (errorMsg) { - throw new Error(errorMsg); - } - - let foundPIN = false; - let child = doc.firstChild; - for (; child; child = child.nextSibling) { - if (child.nodeType === doc.PROCESSING_INSTRUCTION_NODE) { - foundPIN = true; - break; - } - } - if (!foundPIN) { - const pin = doc.createProcessingInstruction('xml', 'version="1.0" encoding="UTF-8"'); - doc.insertBefore(doc.createTextNode('\n'), doc.firstChild); - doc.insertBefore(pin, doc.firstChild); - } - - this.doc = doc; - return this; - } -} diff --git a/src/util/tisdk.js b/src/util/tisdk.js index 158ffbd2d..e41520729 100644 --- a/src/util/tisdk.js +++ b/src/util/tisdk.js @@ -1,135 +1,10 @@ -import { readdir } from 'node:fs/promises'; -import { basename, dirname, join } from 'node:path'; -import fs from 'fs-extra'; -import { expand } from './expand.js'; +import { join } from 'node:path'; import { arrayify } from './arrayify.js'; -import * as version from './version.js'; -import { Tiapp } from './tiapp.js'; -import { request } from './request.js'; import { prompt } from './prompt.js'; - -export const locations = { - linux: [ - '~/.titanium' - ], - osx: [ - '~/Library/Application Support/Titanium', - '/Library/Application Support/Titanium' - ], - win32: [ - '%ProgramData%\\Titanium', - '%APPDATA%\\Titanium' - ] -}; +import { detectTitaniumSDKs, TiappXML } from 'node-titanium-sdk/titanium'; const os = process.platform === 'darwin' ? 'osx' : process.platform; -export async function getTitaniumSDKPaths(config) { - const sdkPaths = new Set(); - - if (!process.env.TI_CLI_SKIP_ENV_PATHS) { - for (const p of locations[os]) { - sdkPaths.add(expand(p)); - } - } - - const defaultInstallLocation = config.get('sdk.defaultInstallLocation'); - if (defaultInstallLocation) { - sdkPaths.add(expand(defaultInstallLocation)); - } - - let configSdkPaths = config.get('paths.sdks'); - if (configSdkPaths) { - for (const p of arrayify(configSdkPaths, true)) { - sdkPaths.add(expand(p)); - } - } - - return { - defaultInstallLocation, - sdkPaths: Array.from(sdkPaths) - }; -} - -let cache; - -export async function detectTitaniumSDKs(config) { - if (cache) { - return cache; - } - - let { - defaultInstallLocation, - sdkPaths - } = await getTitaniumSDKPaths(config); - const sdks = []; - - await Promise.all( - sdkPaths.map(async sdkPath => { - if (basename(sdkPath) !== os && basename(dirname(sdkPath)) !== 'mobilesdk') { - sdkPath = join(sdkPath, 'mobilesdk', os); - } - - try { - const dirs = await readdir(sdkPath); - - return await Promise.all(dirs.map(async name => { - const path = join(sdkPath, name); - try { - const manifest = await fs.readJson(join(path, 'manifest.json')); - sdks.push({ - name: manifest.name, - manifest, - path, - platforms: Array.isArray(manifest.platforms) - ? manifest.platforms.reduce((platforms, name) => { - platforms[name] = { - path: join(path, name) - }; - return platforms; - }, {}) - : {}, - type: getSDKType(manifest.name), - version: manifest.version - }); - } catch { - // no manifest (too old) - } - })); - } catch { - // directory probably does not exist, ignore - } - }) - ); - - sdks.sort((a, b) => version.compare(a.name, b.name)).reverse(); - - cache = { - installPath: defaultInstallLocation || sdkPaths[0], - latest: sdks.find(s => /.GA$/.test(s.name))?.name || sdks[0]?.name || null, - sdks, - sdkPaths - }; - - return cache; -} - -function getSDKType(name) { - if (/\.ga$/i.test(name)) { - return 'ga'; - } - if (/\.rc$/i.test(name)) { - return 'rc'; - } - if (/\.beta$/i.test(name)) { - return 'beta'; - } - if (/\.v\d+$/i.test(name)) { - return 'nightly'; - } - return 'local'; -} - const sortTypes = ['local', 'nightly', 'beta', 'rc', 'ga']; export const typeLabels = { @@ -145,7 +20,7 @@ export async function initSDK({ config, cwd, debugLogger, logger, promptingEnabl let tiappSdkVersion; // try to read the tiapp.xml - let tiapp = new Tiapp(); + const tiapp = new TiappXML(); try { const tiappFile = join(cwd, 'tiapp.xml'); await tiapp.load(tiappFile); @@ -156,13 +31,20 @@ export async function initSDK({ config, cwd, debugLogger, logger, promptingEnabl // might not be a project dir or bad tiapp.xml } + const configSdkPaths = config.get('paths.sdks'); + // detect SDKs const { installPath, latest, sdks, sdkPaths - } = await detectTitaniumSDKs(config); + } = await detectTitaniumSDKs({ + searchPaths: [ + config.get('sdk.defaultInstallLocation'), + ...(Array.isArray(configSdkPaths) ? arrayify(configSdkPaths, true) : []) + ] + }); let sdk = null; if (sdks.length) { @@ -221,59 +103,3 @@ export async function initSDK({ config, cwd, debugLogger, logger, promptingEnabl tiappSdkVersion }; } - -/** - * Retrieves the list of releases. - * @param {String} os - The name of the OS (osx, linux, win32) - * @param {Boolean} [unstable] - When `true`, returns beta and rc releases along with ga releases. - * @returns {Promise} - */ -export async function getReleases(unstable) { - const releaseRE = /^(\d+)\.(\d+)\.(\d+)\.(\w+)$/; - - const fetches = [ - unstable && request('https://downloads.titaniumsdk.com/registry/beta.json', { - responseType: 'json' - }).then(async res => ({ type: 'beta', releases: await res.body.json() })), - - unstable && request('https://downloads.titaniumsdk.com/registry/rc.json', { - responseType: 'json' - }).then(async res => ({ type: 'rc', releases: await res.body.json() })), - - request('https://downloads.titaniumsdk.com/registry/ga.json', { - responseType: 'json' - }).then(async res => ({ type: 'ga', releases: await res.body.json() })) - ]; - - const results = await Promise.all(fetches); - - return results - .flatMap(value => { - return value ? value.releases.map(rel => { - rel.type = value.type; - return rel; - }) : []; - }) - .filter(r => r.assets.some(a => a.os === os)) - .sort((a, b) => { - const [, amajor, aminor, apatch, atag] = a.name.toLowerCase().match(releaseRE); - const [, bmajor, bminor, bpatch, btag] = b.name.toLowerCase().match(releaseRE); - - let n = parseInt(bmajor) - parseInt(amajor); - if (n !== 0) { - return n; - } - - n = parseInt(bminor) - parseInt(aminor); - if (n !== 0) { - return n; - } - - n = parseInt(bpatch) - parseInt(apatch); - if (n !== 0) { - return n; - } - - return sortTypes.indexOf(btag) - sortTypes.indexOf(atag); - }); -} diff --git a/test/util/request.test.js b/test/util/request.test.js deleted file mode 100644 index 096a40c89..000000000 --- a/test/util/request.test.js +++ /dev/null @@ -1,51 +0,0 @@ -import { afterEach, beforeEach, describe, it } from 'node:test'; -import assert from 'node:assert'; -import { request } from '../../src/util/request.js'; -import { ticonfig } from '../../src/util/ticonfig.js'; -import { createServer } from 'node:http'; -import { createProxy } from 'proxy'; - -let origProxyUrl; - -describe('request', () => { - beforeEach(() => { - origProxyUrl = ticonfig.get('cli.httpProxyServer'); - ticonfig.set('cli.httpProxyServer', undefined); - }); - - afterEach(() => { - ticonfig.set('cli.httpProxyServer', origProxyUrl); - }); - - it('should fetch TiDev page', async () => { - const res = await request('https://github.com'); - await res.body.text(); - assert.strictEqual(res.statusCode, 200); - }); - - it('should fetch TiDev page via proxy', async () => { - const connections = {}; - const server = createServer(); - server.on('connection', function (conn) { - const key = `${conn.remoteAddress}:${conn.remotePort}`; - connections[key] = conn; - conn.on('close', () => { - delete connections[key]; - }); - }); - createProxy(server).listen(9999); - - try { - ticonfig.set('cli.httpProxyServer', 'http://localhost:9999'); - - const res = await request('https://github.com'); - await res.body.text(); - assert.strictEqual(res.statusCode, 200); - } finally { - for (const conn of Object.values(connections)) { - conn.destroy(); - } - await new Promise(resolve => server.close(resolve)); - } - }, 10000); -}); diff --git a/test/util/tiapp.test.js b/test/util/tiapp.test.js deleted file mode 100644 index 970429d39..000000000 --- a/test/util/tiapp.test.js +++ /dev/null @@ -1,69 +0,0 @@ -import { describe, it } from 'node:test'; -import assert from 'node:assert'; -import { Tiapp } from '../../src/util/tiapp.js'; -import { join } from 'node:path'; -import { fileURLToPath } from 'node:url'; - -const fixturesDir = join(fileURLToPath(import.meta.url), '../fixtures/tiapp'); - -describe('Tiapp', () => { - it('should load a tiapp with an SDK version', async () => { - const tiapp = new Tiapp(); - await tiapp.load(join(fixturesDir, 'hassdk.xml')); - - const value = await tiapp.select1('//sdk-version', '0.0.0'); - assert.strictEqual(value, '1.2.3'); - }); - - it('should load a tiapp without an SDK version', async () => { - const tiapp = new Tiapp(); - await tiapp.load(join(fixturesDir, 'nosdk.xml')); - - let value = await tiapp.select1('//sdk-version', '0.0.0'); - assert.strictEqual(value, '0.0.0'); - - value = await tiapp.select1('//sdk-version'); - assert.strictEqual(value, undefined); - }); - - it('should load a tiapp without a pin', async () => { - const tiapp = new Tiapp(); - await tiapp.load(join(fixturesDir, 'nopin.xml')); - - const value = await tiapp.select1('//sdk-version', '0.0.0'); - assert.strictEqual(value, '1.2.3'); - }); - - it('should error if file does not exist', async () => { - const tiapp = new Tiapp(); - await assert.rejects( - tiapp.load(join(fixturesDir, 'does_not_exist')), - { - name: 'Error', - message: /^File not found:/ - } - ); - }); - - it('should error selecting if no file loaded', async () => { - const tiapp = new Tiapp(); - await assert.rejects( - tiapp.select1(), - { - name: 'Error', - message: 'No tiapp.xml loaded' - } - ); - }); - - it('should error loading if file is malformed', async () => { - const tiapp = new Tiapp(); - await assert.rejects( - tiapp.load(join(fixturesDir, 'bad.xml')), - { - name: 'ParseError', - message: 'unclosed xml tag(s): ti:app' - } - ); - }); -}); diff --git a/test/util/tisdk.test.js b/test/util/tisdk.test.js deleted file mode 100644 index e47e8d6fa..000000000 --- a/test/util/tisdk.test.js +++ /dev/null @@ -1,40 +0,0 @@ -import { describe, it } from 'node:test'; -import assert from 'node:assert'; -import { getTitaniumSDKPaths } from '../../src/util/tisdk.js'; -import { join } from 'node:path'; -import { fileURLToPath } from 'node:url'; -import { TiConfig } from '../../src/util/ticonfig.js'; -import { tmpDirName } from '../helpers/tmp-dir-name.js'; -import fs from 'fs-extra'; - -const goodConfig = join(fileURLToPath(import.meta.url), '../fixtures/ticonfig/good.json'); - -describe('tisdk', () => { - it('should get user SDK paths', async () => { - const tmpSDKDir = tmpDirName(); - const config = new TiConfig(goodConfig); - - try { - let result = await getTitaniumSDKPaths(config); - assert.deepStrictEqual(result, { - defaultInstallLocation: undefined, - sdkPaths: [] - }); - - config.set('paths.sdks', tmpSDKDir); - config.set('sdk.defaultInstallLocation', tmpSDKDir); - - result = await getTitaniumSDKPaths(config); - assert.deepStrictEqual(result, { - defaultInstallLocation: tmpSDKDir, - sdkPaths: [tmpSDKDir] - }); - } finally { - await fs.remove(tmpSDKDir); - } - }); - - // detectTitaniumSDKs - - // initSDK -}); From f0b4e1544f97cf3ae0098c403dd8c32950bca690 Mon Sep 17 00:00:00 2001 From: Chris Barber Date: Tue, 17 Feb 2026 09:33:48 -0600 Subject: [PATCH 02/16] Fix tests --- src/commands/sdk.js | 8 +++----- src/util/tisdk.js | 2 +- test/commands/ti-sdk.test.js | 25 +++++++++++++------------ 3 files changed, 17 insertions(+), 18 deletions(-) diff --git a/src/commands/sdk.js b/src/commands/sdk.js index 6d4265187..f2cf591ed 100644 --- a/src/commands/sdk.js +++ b/src/commands/sdk.js @@ -15,9 +15,7 @@ import { pipeline } from 'node:stream/promises'; import { extractZip } from '../util/extract-zip.js'; import { prompt } from '../util/prompt.js'; import { request } from 'node-titanium-sdk/util'; -import { getTitaniumReleases } from 'node-titanium-sdk/titanium'; -import { getTitaniumBranches } from 'node-titanium-sdk/titanium'; -import { getTitaniumBranchBuilds } from 'node-titanium-sdk/titanium'; +import { getTitaniumBranchBuilds, getTitaniumBranches, getTitaniumReleases } from 'node-titanium-sdk/titanium'; import prettyBytes from 'pretty-bytes'; import wrapAnsi from 'wrap-ansi'; @@ -525,7 +523,7 @@ async function getInstallFile({ branch, config, logger, osName, showProgress, su // try to find the release by name let release = null; - const releases = await getReleases(true); + const releases = await getTitaniumReleases(true); if (uri === 'latest') { release = releases.find(r => r.type === 'ga'); } else if (uri === 'latest-rc') { @@ -708,7 +706,7 @@ async function checkSDKFile({ force, logger, _filename, name, noPrompt, _osName, } // already installed - const releases = await getReleases(false); + const releases = await getTitaniumReleases(false); const latest = releases[0]; const tip = `Run '${cyan(`titanium sdk install ${latest.name} --force`)}' to re-install`; diff --git a/src/util/tisdk.js b/src/util/tisdk.js index e41520729..55669ab97 100644 --- a/src/util/tisdk.js +++ b/src/util/tisdk.js @@ -93,7 +93,7 @@ export async function initSDK({ config, cwd, debugLogger, logger, promptingEnabl } return { - installPath, + installPath: config.get('sdk.defaultInstallLocation') || installPath, sdk, sdkPaths, sdks: sdks.reduce((obj, sdk) => { diff --git a/test/commands/ti-sdk.test.js b/test/commands/ti-sdk.test.js index 091359327..429b485b6 100644 --- a/test/commands/ti-sdk.test.js +++ b/test/commands/ti-sdk.test.js @@ -80,12 +80,13 @@ describe('ti sdk', () => { let { exitCode, stdout, stderr } = await run(['sdk']); // no `ls` to test default subcommand let output = stripColor(stdout); assert.match(output, /Titanium Command-Line Interface/); - assert.match(output, new RegExp(`SDK Install Locations:\n\\s*${tmpSDKDir.replace(/\\/g, '\\\\')}`)); + assert.match(output, new RegExp(`SDK Install Locations:\n[\\s\\S]*${tmpSDKDir.replace(/\\/g, '\\\\')}`)); assert.match(output, /No Titanium SDKs found/); // list SDKs as JSON (no SDKs installed) ({ exitCode, stdout } = await run(['sdk', 'ls', '--json'])); let json = JSON.parse(stdout); + assert(json.installLocations.includes(tmpSDKDir)); assert.deepStrictEqual(json, { branch: {}, branches: { @@ -93,7 +94,7 @@ describe('ti sdk', () => { branches: [] }, defaultInstallLocation: tmpSDKDir, - installLocations: [tmpSDKDir], + installLocations: json.installLocations, installed: {}, releases: {}, sdks: {} @@ -117,7 +118,7 @@ describe('ti sdk', () => { ({ exitCode, stdout } = await run(['sdk', 'ls'])); output = stripColor(stdout); assert.match(output, /Titanium Command-Line Interface/); - assert.match(output, new RegExp(`SDK Install Locations:\n\\s*${tmpSDKDir.replace(/\\/g, '\\\\')}`)); + assert.match(output, new RegExp(`SDK Install Locations:\n[\\s\\S]*${tmpSDKDir.replace(/\\/g, '\\\\')}`)); assert.match(output, new RegExp(`Installed SDKs:\n\\s*${sdkName}\\s+${sdkVersion}\\s+${ sdkPath.replace(/\\/g, '\\\\') }`)); @@ -133,7 +134,7 @@ describe('ti sdk', () => { branches: [] }, defaultInstallLocation: tmpSDKDir, - installLocations: [tmpSDKDir], + installLocations: json.installLocations, installed: { [sdkName]: sdkPath }, @@ -175,7 +176,7 @@ describe('ti sdk', () => { branches: [] }, defaultInstallLocation: tmpSDKDir, - installLocations: [tmpSDKDir], + installLocations: json.installLocations, installed: {}, releases: {}, sdks: {} @@ -200,7 +201,7 @@ describe('ti sdk', () => { branches: [] }, defaultInstallLocation: tmpSDKDir, - installLocations: [tmpSDKDir], + installLocations: json.installLocations, installed: { [sdkName]: sdkPath }, @@ -269,14 +270,14 @@ describe('ti sdk', () => { let { exitCode, stdout } = await run(['sdk', 'list', '-b']); let output = stripColor(stdout); assert.match(output, /Titanium Command-Line Interface/); - assert.match(output, new RegExp(`SDK Install Locations:\n\\s*${tmpSDKDir.replace(/\\/g, '\\\\')}`)); + assert.match(output, new RegExp(`SDK Install Locations:\n[\\s\\S]*${tmpSDKDir.replace(/\\/g, '\\\\')}`)); assert.match(output, /Branches:\n\s*(main|master)/); // list stable releases ({ exitCode, stdout } = await run(['sdk', 'list', '-r'])); output = stripColor(stdout); assert.match(output, /Titanium Command-Line Interface/); - assert.match(output, new RegExp(`SDK Install Locations:\n\\s*${tmpSDKDir.replace(/\\/g, '\\\\')}`)); + assert.match(output, new RegExp(`SDK Install Locations:\n[\\s\\S]*${tmpSDKDir.replace(/\\/g, '\\\\')}`)); assert.match(output, /Releases:/); assert.match(output, /12\.2\.0\.GA\s+9\/15\/23/); @@ -284,7 +285,7 @@ describe('ti sdk', () => { ({ exitCode, stdout } = await run(['sdk', 'list', '-u'])); output = stripColor(stdout); assert.match(output, /Titanium Command-Line Interface/); - assert.match(output, new RegExp(`SDK Install Locations:\n\\s*${tmpSDKDir.replace(/\\/g, '\\\\')}`)); + assert.match(output, new RegExp(`SDK Install Locations:\n[\\s\\S]*${tmpSDKDir.replace(/\\/g, '\\\\')}`)); assert.match(output, /Releases:/); assert.match(output, /12\.2\.0\.GA\s+9\/15\/23/); assert.match(output, /12\.2\.0\.RC\s+8\/11\/23/); @@ -293,7 +294,7 @@ describe('ti sdk', () => { ({ exitCode, stdout } = await run(['sdk', 'list', '--branch', 'main'])); output = stripColor(stdout); assert.match(output, /Titanium Command-Line Interface/); - assert.match(output, new RegExp(`SDK Install Locations:\n\\s*${tmpSDKDir.replace(/\\/g, '\\\\')}`)); + assert.match(output, new RegExp(`SDK Install Locations:\n[\\s\\S]*${tmpSDKDir.replace(/\\/g, '\\\\')}`)); assert.match(output, /'main' Branch Builds:/); // assert.match(output, /\d+\.\d+\.\d+\.v\d+\s+\d+\/\d+\/\d+\s+\d+(\.\d+)? .B \[unstable\]/); @@ -310,7 +311,7 @@ describe('ti sdk', () => { it('should not find any SDKs in empty SDK home directory', initSDKHome(async ({ run, tmpSDKDir }) => { const { exitCode, stdout } = await run(['sdk', 'list']); const output = stripColor(stdout); - assert.match(output, new RegExp(`SDK Install Locations:\n\\s*${tmpSDKDir.replace(/\\/g, '\\\\')}`)); + assert.match(output, new RegExp(`SDK Install Locations:\n[\\s\\S]*${tmpSDKDir.replace(/\\/g, '\\\\')}`)); assert.match(output, /No Titanium SDKs found/); assert.strictEqual(exitCode, 0); }), 60000); @@ -318,7 +319,7 @@ describe('ti sdk', () => { it('should list SDKs in SDK home directory', initMockSDKHome(async ({ run, tmpSDKDir }) => { const { exitCode, stdout } = await run(['sdk', 'list']); const output = stripColor(stdout); - assert.match(output, new RegExp(`SDK Install Locations:\n\\s*${tmpSDKDir.replace(/\\/g, '\\\\')}`)); + assert.match(output, new RegExp(`SDK Install Locations:\n[\\s\\S]*${tmpSDKDir.replace(/\\/g, '\\\\')}`)); assert.match(output, /Installed SDKs:/); assert.match(output, new RegExp(`0.0.0.GA\\s+0.0.0\\s+${join(tmpSDKDir, 'mobilesdk', os, '0.0.0.GA').replace(/\\/g, '\\\\')}`)); assert.strictEqual(exitCode, 0); From 83ddec68d6e45b72a371b26c3f3a6e8f77e57464 Mon Sep 17 00:00:00 2001 From: Chris Barber Date: Wed, 4 Mar 2026 22:20:53 -0600 Subject: [PATCH 03/16] Switch files over to node-titanium-sdk, add oxfmt --- .github/dependabot.yml | 38 +- .github/workflows/cla.yaml | 6 +- .github/workflows/publish.yml | 100 +- .github/workflows/test.yml | 32 +- .oxfmtrc.json | 39 + .oxlintrc.json | 5 +- CHANGELOG.md | 882 +++++++------- README.md | 1 - package.json | 129 ++- pnpm-lock.yaml | 1032 +++++++++-------- scripts/test.js | 31 +- src/cli.js | 560 +++++---- src/commands/config.js | 41 +- src/commands/info.js | 211 ++-- src/commands/module.js | 43 +- src/commands/sdk.js | 357 +++--- src/commands/setup.js | 6 +- src/main.js | 14 +- src/util/apply-command-config.js | 24 +- src/util/arrayify.js | 20 +- src/util/columns.js | 7 +- src/util/detect.js | 63 +- src/util/expand.js | 13 +- src/util/extract-zip.js | 119 -- src/util/jdk.js | 145 ++- src/util/logger.js | 12 +- src/util/progress.js | 4 +- src/util/prompt.js | 2 +- src/util/proxy.js | 12 +- src/util/setup-screens.js | 297 ++--- src/util/suggest.js | 43 - src/util/ticonfig.js | 17 +- src/util/tihelp.js | 47 +- src/util/timodule.js | 264 ----- src/util/tisdk.js | 54 +- src/util/version.js | 220 ---- test/cli/hook.test.js | 26 +- test/commands/custom.test.js | 80 +- test/commands/ti-build.test.js | 29 +- test/commands/ti-clean.test.js | 29 +- test/commands/ti-config.test.js | 542 +++++---- test/commands/ti-create.test.js | 36 +- test/commands/ti-info.test.js | 347 +++--- test/commands/ti-module.test.js | 432 ++++--- test/commands/ti-project.test.js | 31 +- test/commands/ti-sdk.test.js | 704 ++++++----- test/commands/ti-setup.test.js | 27 +- test/commands/ti.test.js | 57 +- test/helpers/init-cli.js | 10 +- test/helpers/init-sdk-home.js | 4 +- test/mock-sdk/android/cli/commands/_build.js | 526 +++++---- .../android/cli/commands/_buildModule.js | 47 +- test/mock-sdk/android/cli/hooks/package.js | 2 +- test/mock-sdk/android/cli/hooks/run.js | 16 +- test/mock-sdk/android/cli/lib/detect.js | 21 +- test/mock-sdk/android/cli/lib/info.js | 442 +++++-- test/mock-sdk/android/package.json | 51 +- test/mock-sdk/cli/commands/build.js | 393 ++++--- test/mock-sdk/cli/commands/clean.js | 342 +++--- test/mock-sdk/cli/commands/create.js | 148 +-- test/mock-sdk/cli/commands/project.js | 57 +- test/mock-sdk/cli/hooks/hyperloop-fix.js | 2 +- test/mock-sdk/cli/lib/creator.js | 168 ++- test/mock-sdk/cli/lib/creators/app.js | 14 +- test/mock-sdk/cli/lib/creators/module.js | 51 +- test/mock-sdk/cli/lib/info.js | 110 +- .../cli/lib/node-titanium-sdk/builder.js | 15 +- test/mock-sdk/cli/lib/node-titanium-sdk/ti.js | 199 ++-- test/mock-sdk/manifest.json | 9 +- test/mock-sdk/package.json | 8 +- test/util/arrayify.test.js | 33 +- test/util/busyindicator.test.js | 6 +- test/util/capitalize.test.js | 4 +- test/util/columns.test.js | 67 +- test/util/detect.test.js | 8 +- test/util/expand.test.js | 8 +- test/util/fixtures/ticonfig/good.json | 12 +- test/util/logger.test.js | 93 +- test/util/progress.test.js | 123 +- test/util/proxy.test.js | 4 +- test/util/suggest.test.js | 14 +- test/util/ticonfig.test.js | 23 +- test/util/tierror.test.js | 4 +- test/util/timodule.test.js | 37 +- test/util/unique.test.js | 26 +- test/util/version.test.js | 257 ---- 86 files changed, 5410 insertions(+), 5174 deletions(-) create mode 100644 .oxfmtrc.json delete mode 100644 src/util/extract-zip.js delete mode 100644 src/util/suggest.js delete mode 100644 src/util/timodule.js delete mode 100644 src/util/version.js delete mode 100644 test/util/version.test.js diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 3f745a65a..521f1eb48 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,32 +1,32 @@ version: 2 updates: - - package-ecosystem: "npm" - directory: "/" + - package-ecosystem: 'npm' + directory: '/' labels: - - "dependencies" + - 'dependencies' schedule: - interval: "weekly" - day: "wednesday" - time: "02:34" + interval: 'weekly' + day: 'wednesday' + time: '02:34' groups: production: - dependency-type: "production" + dependency-type: 'production' patterns: - - "*" + - '*' update-types: - - "major" - - "minor" - - "patch" + - 'major' + - 'minor' + - 'patch' development: - dependency-type: "development" + dependency-type: 'development' patterns: - - "*" + - '*' update-types: - - "major" - - "minor" - - "patch" + - 'major' + - 'minor' + - 'patch' commit-message: - include: "scope" - prefix: "chore(deps): " + include: 'scope' + prefix: 'chore(deps): ' allow: - - dependency-type: "all" + - dependency-type: 'all' diff --git a/.github/workflows/cla.yaml b/.github/workflows/cla.yaml index bbfcc73f8..d660e1dac 100644 --- a/.github/workflows/cla.yaml +++ b/.github/workflows/cla.yaml @@ -8,6 +8,6 @@ jobs: name: Verify contributor steps: - - uses: tidev/tidev-cla-action@v2 - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} + - uses: tidev/tidev-cla-action@v2 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 201706e52..640afc8b4 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -14,62 +14,62 @@ jobs: name: Publish steps: - - name: Checkout repository - uses: actions/checkout@v6 + - name: Checkout repository + uses: actions/checkout@v6 - - name: Setup node - uses: actions/setup-node@v6 - with: - node-version: 24 - registry-url: 'https://registry.npmjs.org/' + - name: Setup node + uses: actions/setup-node@v6 + with: + node-version: 24 + registry-url: 'https://registry.npmjs.org/' - - name: Update npm - run: npm install -g npm@latest + - name: Update npm + run: npm install -g npm@latest - - name: Install pnpm - uses: pnpm/action-setup@v4 - with: - version: latest + - name: Install pnpm + uses: pnpm/action-setup@v4 + with: + version: latest - - name: Install dependencies - run: pnpm install + - name: Install dependencies + run: pnpm install - - name: Lint - run: pnpm lint + - name: Lint + run: pnpm lint - - name: Publish to npm - run: npm publish --provenance --tag ${{ github.event.release.prerelease && 'next' || 'latest' }} + - name: Publish to npm + run: npm publish --provenance --tag ${{ github.event.release.prerelease && 'next' || 'latest' }} - - name: Get package.json info - id: get-package-info - run: | - echo "name=$(node -p "require('./package.json').name")" >> $GITHUB_OUTPUT - echo "homepage=$(node -p "require('./package.json').homepage")" >> $GITHUB_OUTPUT - echo "version=$(node -p "require('./package.json').version")" >> $GITHUB_OUTPUT + - name: Get package.json info + id: get-package-info + run: | + echo "name=$(node -p "require('./package.json').name")" >> $GITHUB_OUTPUT + echo "homepage=$(node -p "require('./package.json').homepage")" >> $GITHUB_OUTPUT + echo "version=$(node -p "require('./package.json').version")" >> $GITHUB_OUTPUT - - name: Send Slack notification - uses: slackapi/slack-github-action@v2.1.1 - with: - method: chat.postMessage - token: ${{ secrets.SLACK_BOT_TOKEN }} - payload: | - { - "channel": "${{ secrets.SLACK_CHANNEL_ID }}", - "text": "Published ${{ steps.get-package-info.outputs.name }}@${{ steps.get-package-info.outputs.version }}", - "blocks": [ - { - "type": "header", - "text": { - "type": "plain_text", - "text": "Published ${{ steps.get-package-info.outputs.name }}@${{ steps.get-package-info.outputs.version }}" + - name: Send Slack notification + uses: slackapi/slack-github-action@v2.1.1 + with: + method: chat.postMessage + token: ${{ secrets.SLACK_BOT_TOKEN }} + payload: | + { + "channel": "${{ secrets.SLACK_CHANNEL_ID }}", + "text": "Published ${{ steps.get-package-info.outputs.name }}@${{ steps.get-package-info.outputs.version }}", + "blocks": [ + { + "type": "header", + "text": { + "type": "plain_text", + "text": "Published ${{ steps.get-package-info.outputs.name }}@${{ steps.get-package-info.outputs.version }}" + } + }, + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "${{ steps.get-package-info.outputs.homepage }}/releases/tag/v${{ steps.get-package-info.outputs.version }}" + } } - }, - { - "type": "section", - "text": { - "type": "mrkdwn", - "text": "${{ steps.get-package-info.outputs.homepage }}/releases/tag/v${{ steps.get-package-info.outputs.version }}" - } - } - ] - } + ] + } diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2485e50a8..ed5894924 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,24 +17,24 @@ jobs: os: [ubuntu-latest, windows-latest, macOS-latest] steps: - - name: Checkout repository - uses: actions/checkout@v6 + - name: Checkout repository + uses: actions/checkout@v6 - - name: Use Node.js ${{ matrix.node }} - uses: actions/setup-node@v6 - with: - node-version: ${{ matrix.node }} + - name: Use Node.js ${{ matrix.node }} + uses: actions/setup-node@v6 + with: + node-version: ${{ matrix.node }} - - name: Install pnpm - uses: pnpm/action-setup@v4 - with: - version: latest + - name: Install pnpm + uses: pnpm/action-setup@v4 + with: + version: latest - - name: Install dependencies - run: pnpm install + - name: Install dependencies + run: pnpm install - - name: Lint - run: pnpm lint + - name: Lint + run: pnpm lint - - name: Run tests - run: pnpm coverage + - name: Run tests + run: pnpm coverage diff --git a/.oxfmtrc.json b/.oxfmtrc.json new file mode 100644 index 000000000..0ec170426 --- /dev/null +++ b/.oxfmtrc.json @@ -0,0 +1,39 @@ +{ + "$schema": "./node_modules/oxfmt/configuration_schema.json", + "arrowParens": "always", + "bracketSameLine": false, + "bracketSpacing": true, + "embeddedLanguageFormatting": "auto", + "endOfLine": "lf", + "experimentalSortImports": { + "customGroups": [], + "groups": [], + "ignoreCase": true, + "internalPattern": ["~/", "@/"], + "newlinesBetween": true, + "order": "asc", + "partitionByComment": false, + "partitionByNewline": false, + "sortSideEffects": false + }, + "experimentalSortPackageJson": true, + "ignorePatterns": ["test/**/fixtures/**"], + "insertFinalNewline": true, + "objectWrap": "preserve", + "overrides": [ + { + "files": ["**/*.json"], + "options": { + "useTabs": false, + "tabWidth": 2 + } + } + ], + "printWidth": 100, + "proseWrap": "preserve", + "quoteProps": "as-needed", + "semi": true, + "singleQuote": true, + "trailingComma": "es5", + "useTabs": true +} diff --git a/.oxlintrc.json b/.oxlintrc.json index db33a1bfd..d2a3c8440 100644 --- a/.oxlintrc.json +++ b/.oxlintrc.json @@ -1,6 +1,3 @@ { - "ignorePatterns": [ - "test/commands/fixtures/**", - "test/mock-sdk/**" - ] + "ignorePatterns": ["test/commands/fixtures/**", "test/mock-sdk/**"] } diff --git a/CHANGELOG.md b/CHANGELOG.md index b89dabe20..d9b2eca5b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,441 +1,441 @@ -8.1.5 (2/2/2026) -------------------- - * fix: Scan custom hooks path after loading the Titanium SDK - -8.1.4 (1/18/2026) -------------------- - * fix: Allow `colors` shim methods to be redefined by the real `colors` - -8.1.3 (1/17/2026) -------------------- - * fix: Define `colors` shim for `info` command - * fix: Default value ignored when getting an empty config value - -8.1.2 (10/28/2025) -------------------- - * fix: Add support for SDK `main` branch builds - -8.1.1 (10/21/2025) -------------------- - * fix: Emit `post-execute` hook for commands with async `run()` - * fix: Display SDK name, then the actual version - * fix: Skip run if `--sdk` version mismatches `` in `tiapp.xml` - and allow correct command to be forked - -8.1.0 (9/15/2025) -------------------- - * fix: On Windows call `powershell` instead of deprecated `wmic` command to - get the OS name and version. - * chore: Updated dependencies - -8.0.0 (8/1/2025) -------------------- - * BREAKING CHANGE: Require Node.js 20.18.1 or newer - * chore: Updated dependencies - -7.1.7 (4/3/2025) -------------------- - * fix: Fixed issue installing Hyperloop module where same name and version, - but different platform. - -7.1.6 (2/6/2025) -------------------- - * fix: Fixed command path for Node 22.13.1. - -7.1.5 (10/4/2024) -------------------- - * fix: Improve tiapp sdk-version handling - * chore: Update dependencies - -7.1.4 (8/4/2024) -------------------- - * fix: Load iOS hooks from `iphone` directory in the SDK - -7.1.3 (8/1/2024) -------------------- - * fix: Invalid `--platform` would cause crash because the platform - validation was being bypassed - -7.1.2 (7/31/2024) -------------------- - * fix: Force `--platform` to `ios`; the SDK converts `ios` to `iphone`, but - the CLI needs to reverse this to be able to look up the platform config - -7.1.1 (7/30/2024) -------------------- - * fix: Rename platform config `iphone` to `ios` to align with - `ti.targetPlatforms` - -7.1.0 (5/25/2024) -------------------- - * feat: Support async hook `init()` functions - * fix: Surface sdk install errors - * fix: `ti sdk rm ` treats confirm prompt as false - * fix: Assert required Node.js version - * fix: Clear out undefined command args which fixes `ti project` - * fix: `ti sdk install` no longer silently fails when installing new modules - * fix: When reinstalling an SDK, choosing "Overwrite" will force modules to - also be reinstalled - * fix: Properly handle result from `ti sdk install` overwrite prompt, - `ti sdk uninstall` version prompt, and `ti setup user` name prompt - -7.0.0 (5/10/2024) -------------------- - * Require Node.js 18 or newer - * Refactored entire codebase using ESM - * Removed `login`, `logout`, `plugin`, and `status` commands - * Removed all analytics/telemetry code - * Removed i18n logic; all output rendered in English - * Removed incomplete tab completion code - * `--sdk ` is now a global option - - It assumes the latest installed SDK version... - - If executing the `create` command without any options, it will prompt for - the SDK version to use - * Removed "default" SDK; `` in tiapp.xml is source of truth, - override with `--sdk ` arg - * Replaced custom CLI arg parser (based on `optimist`) with Commander.js - - Order of arguments matter; command options must come after command - * `-d`, `--project-dir` is now a global option - - Used to be `build`, `clean`, and `project` commands only, but needed so - the CLI can read the `tiapp.xml` and determine the ``; - defaults to the current working directory - - Since this conflicts with the `create` command's `-d`, `--workspace-dir` - option, special handling treats these two options the same - * Added a new `--debug` global flag to output CLI debug logging - * `ti config` changes: - - Added `--json` flag - - Replaced `--output json` with `--output json-object` output - * `ti info` changes: - - Added `--json` flag - - Removed `haxm` info - - Removed `genymotion` and VirtualBox info - - Removed macOS info including Xcode CLI Tools - - Removed `jarsigner` from JDK detection; no longer used thanks to Gradle - - Removed `nodeAppcVer` from Titanium CLI and Titanium SDKs in info - * `ti module` changes: - - Removed global `iphone` modules as it was just a copy of `ios` modules - - Modules with `platform` other than `android`, `commonjs`, `ios`, and - `iphone` will be ignored - - Modules with invalid semantic version will be ignored - * `ti sdk` changes: - - Added `--json` flag - - Removed `activeSDK` from JSON result - - `select` subcommand is a no-op - - `install` subcommand `--default` flag is a no-op - * `ti setup` changes: - - Removed Windows Store publishing info - - Removed `haxm` info - - Removed `sdk` setup - - Removed user locale setup - - Removed Xcode CLI tools detection - - Removed Titanium CLI Dependencies section from check - - Removed Java-based connection test from check - * Logger changes: - - Replaced "winston" based logger with lighter weight logger - - Trace and debug log messages written to `stderr` instead of `stdout` - - Added total run time trace message at end - - `--timestamp` option enabled only when command has `--log-level` option - * Performance improvements: - - Lazy load modules as much as possible, within reason - - Removed tons of old dead code - - Use smaller alternative dependencies - * Updated dependencies, replaced deprecated dependencies - - Replaced `colors` with `chalk` - - Replaced `fields` with `prompts` - - Replaced `humanize` with `pretty-bytes` - - Replaced `request` with `undici` - -6.1.1 (7/13/2022) -------------------- - * Allow SDK directories to contain any version suffix - -6.1.0 (6/28/2022) -------------------- - * Re-enabled installing CI builds - * Updated minor/patch dependencies - -6.0.2 (5/24/2022) -------------------- - * Disabled retrieving CI build branches and builds since it didn't work anyways - -6.0.1 (5/23/2022) -------------------- - * Updated banner copyright and links - -6.0.0 (5/23/2022) -------------------- - * BREAKING CHANGE: Dropped support for Node 12 and older - * feat(sdk): handle an sdk zip downloaded as an artifact from github - * feat(sdk): add timestamp flag - * fix: optimize error for unknown deviceid - -5.2.1 (2/15/2019) -------------------- - * Fix selection of SDK when it is already installed [TIMOB-25179] - * Update dependencies - -5.2.0 (8/7/2018) -------------------- - * Fix failure due to bad URL on `ti setup check` [TIMOB-26206] - * Fix incorrect dependencies being reported when running `appc ti setup check` [TIMOB-24892] - * Update dependencies - -5.1.1 (6/5/2018) -------------------- - * Added flag to disable analytics [TIMOB-26083] - * Removed email from analytics payloads [TIMOB-26083] - -5.1.0 (3/12/2018) -------------------- - * Fix typo in Android NDK path prompt - * Support detection of JDK 9 [TIMOB-25429] - -5.0.14 (12/5/2017) -------------------- - * Fix forking of correct SDK during `ti build` [TIMOB-24690] - -5.0.13 (4/26/2017) -------------------- - * Error thrown in CLI command plugin errors on load [TIMOB-24546] - * Removed hook that fixed some Titanium SDK 3.x versions (which are no longer supported) - * Avoid re-install of SDK from zipfile if already installed - * Fix NDK validation, fix google.com network test #185 - -5.0.10 (9/22/2016) -------------------- - * Fixed bug when running `ti setup sdk` and entering "latest" when the selected SDK no longer exists [TIMOB-23941] - -5.0.4 (9/17/2015) -------------------- - * Added actual SDK version to `ti sdk list` output as well as SDK details to `ti sdk list -o json` [TIMOB-19541] - * Updated NPM dependencies - -5.0.3 (9/9/2015) -------------------- - * No longer display latest Node.js and NPM version as it confusing may imply the Titanium CLI supports them [TIMOB-19470] - * Updated NPM dependencies - -5.0.2 (9/9/2015) -------------------- - * Fixed bug where 'ti setup check' was reporting the latest NPM version, not the latest stable version [TIMOB-19470] - -5.0.1 (9/3/2015) -------------------- - * Updated to node-appc 0.2.31 which fixes a bug with the JDK detection - -5.0.0 (9/3/2015) -------------------- - * Updated to node-appc 0.2.30 - -4.1.5 (8/18/2015) -------------------- - * Fixed console colors when running in PowerShell [TIMOB-19126] - -4.1.4 (8/4/2015) -------------------- - * Fixed bug where the command line args weren't being parsed again after handling a branching option [TIMOB-19281] - -4.0.1 (5/29/2015) -------------------- - * Fixed bug with building an app that had a different Titanium SDK version than the selected SDK and abbreviated option names were being used [TIMOB-18826] - -4.0.0 (5/20/2015) -------------------- - * Added support for generic Titanium SDK-level info [TIMOB-17836] - * Allow the colors to be controlled explicitly regardless if attached or detached from a TTY - * Fixed bug where abbreviated options without values passed in before the last argument being treated as a boolean [TIMOB-18067] - * Ripped out all authentication requirements. Analytics are now anonymous. Login and logout are no ops. [TIMOB-18711] - * Capped SDK version monkey patch for which --password is converted to --store-password for Android [TIMOB-18783] - -3.4.2 (3/6/2015) -------------------- - * Fixed compatibility issues with Node 0.12 [TIMOB-18538] - * Added Node.js version check when using a Titanium SDK [TIMOB-18629] - * Drop support for Node.js 0.8 [TIMOB-18414] - -3.4.1 (11/14/14) -------------------- - * Fixed bug in Titanium SDK 3 bug fix hook where Xcode should only be removed when running on OS X [TIMOB-17808] - -3.4.0 (9/29/14) -------------------- - * Added support for selecting latest stable SDK [TIMOB-17378] - * Fixed issues with config.json being overwritten at the same time [TIMOB-17346] - * Improved handling of corrupt a config.json file [TIMOB-17346] - * Fixed backwards compatibility with Titanium SDK 3.3.0 and older when building an iOS app and Xcode 6 or newer is installed - * Added support for a "helpNoPrompt()" callback when a missing option is encountered - * Fixed bug with abbreviated options that don't have a value being set to true - * Fixed bug where Xcode 6 was not being removed from Titanium SDK 3.3.0 and older from the "ti info" results [TIMOB-17649] - * Fixed bug where "ti info" failed when an invalid Titanium SDK is selected [TIMOB-17666] - * Added support for paths.xcode in "ti config" [TIMOB-17696] - -3.3.0 (7/17/14) -------------------- - * Fixed bug with 'ti setup' when the selected Titanium SDK does not exist [TIMOB-12268] - * Added Genymotion environment information to 'ti info' command [TIMOB-16349] - * Fixed bug where OS and Node.js info was always being returned in 'ti info' - * Added wp8 publisher guid to 'ti setup' [TIMOB-16748] - * Added conflicting hook detection and improved hook error reporting [TIMOB-13847] - * Added support for an array of hook events to emit; needed for [TIMOB-10752] - * Updated Appcelerator API URLs to api.appcelerator.com [TIMOB-16282] - * Added support for Titanium SDKs that can be named anything [TIMOB-16052] - * Improved error handling when sdk command requests list of releases [TIMOB-16917] - * Fixed bug with prompting for missing or invalid options that use generic prompting - * Fixed sorting of Titanium SDKs in the 'sdk select' command to only list valid SDKs and order by their actual version number [TIMOB-16974] - * Fixed bug where integer config values were being saved as strings instead of numbers [TIMOB-17000] - * Fixed 'setup check' command when fetching available Node.js and NPM releases [TIMOB-16996] - * Fixed bug with boolean config setting values being saved as integers [TIMOB-17087] - * Moved the sending of analytics from the 'exit' event to the command finished callback [TIMOB-17046] - * Fixed bug where the CLI would wait for analytics to send [TIMOB-17206] - * Fixed formatting of multiline issues in 'ti info' [TIMOB-17221] - * Fixed display of Android tools that are newer than the maximum supported version [TIMOB-17221] - -3.2.3 (5/1/2014) -------------------- - * When installing a Titanium SDK using the CI version name, but omitting the branch, it now automatically scans all branches [TIMOB-15899] - * Fixed 'sdk' command to display custom Titanium SDK paths in SDK Install Locations [TIMOB-16141] - * Fixed bug where the password was not being discarded after logging in and ends up confusing the Android build [TIMOB-16422] - * Fixed list of options being displayed in help screen when colors are enabled on Windows [TIMOB-12759] - * Added temp directory checking to the 'setup check' command [TIMOB-16671] - * Fixed disabling of colors for 'setup' command. Also fixed --no-color flag [TIMOB-16853] - -3.2.2 -------------------- - * Version skipped, no changes - -3.2.1 (2/10/2014) -------------------- - * Fixed bug where npm version was not being displayed due to race condition [TIMOB-15962] - * Fixed bug where if the node executable is not called "node", the CLI would error because argv[0] != process.execPath [TIMOB-15804] - * Fixed bug when running "ti help" for a command that doesn't have 'platforms' [TIMOB-16233] - * Fixed bug where CLI commands that require a minimum version of the Titanium CLI were not being checked properly [TIMOB-16361] - * Fixed command and hook loading when comparing the minimum required CLI version in which the version has a -beta suffix [TIMOB-16365] - * Fixed bug when a SDK >=3.2.0 build is run with --password instead of --store-password [TIMOB-16354] - -3.2.0 (12/20/2013) -------------------- - * Completely overhauled entire CLI architecture - * Brand new setup command with 'check environment' and 'quick setup' sections - * Added better error handling with logging in and out of the Appc network; saving cli config [TIMOB-13908] - * Added ID for each installed Android SDK and add-on to info command output [TIMOB-13797] - * Fixed bug with CLI argument parser not properly removing the node process from the arguments [TIMOB-14074] - * Added CLI hook to fix deploy type bug in Titanium SDK 3.0.0-3.1.X where deploy type not correctly set for iOS dist builds [TIMOB-14961] - * Updated all afs.exists() calls to fs.existsSync() calls since we no longer support Node.js 0.6 - * Fixed bug when running quick setup when no SDKs installed [TIMOB-14965] - * Adjusted placement of post-valdiate event hook - * Fixed bug with option callbacks not being called for prompted options - * Updated info command to use latest platform-specific detect() signature - * Fixed minor bug with multiple options with same name not being converted to an array - * Fixed all places where broken symlinks would cause errors - * Fixed bug with detecting if CLI is being invoked by node.exe instead of titanium.cmd on Windows [TIMOB-14933] - * Removed node-appc version check - * Improved invalid and missing option handling - * Refactored info command to call platform specific environment detection and info rendering - * When a Titanium SDK 3.2 or newer is selected, info command displays much more info such as cert validity, installation issues, etc - * Fixed bug in logger where errors were not honoring the --no-colors flag - * Fixed escaping of default global ignoreDirs/ignoreFiles - * Added ability to hide flag default values - * Added --no-progress-bars flag to control display of progress bars and busy indicators which are enabled by default - * Fixed a few places where --no-prompt was not being honored - * Fixed sdk install command would fail when --no-prompt is set [TIMOB-15431] - * Fixed bug when installing a Titanium SDK and the temp directory does not exist - * Fixed bug when selected SDK is pre-3.0 or does not exist [TIMOB-15507] - * Added CLI tools information to info and setup check commands on Mac OS X - * Fixed command line argument parser to accept empty option values [TIMOB-15608] - * Added ampersand checks to the setup command's Android SDK path prompt on Windows - * Fixed bug where --quiet, --no-prompt, and --no-progress-bars when setting a value on via ti config or running ti setup would save those flags to the cli config - * Added alias --no-color for --no-colors because I can never remember which one it is - * Updated third party Node.js module dependency version where safe to do so - * Updated the 'setup check' command to work offline and display connection diagnostics - * Fixed bug when --username and --password are supplied at the command line - * Fixed bug with the paths.sdks config setting continuously appending the default Titanium SDK install location [TIMOB-15813] - * Fixed bug with 'npm outdated' returning a version of 'missing' [TIMOB-15842] - * Removed Java 1.7 warning from the 'setup check' command - * Fixed bug where options that have valid values but don't have a validate function never have their callbacks fired [TIMOB-15935] - -3.1.4 (12/18/2013) -------------------- - * Fixed bug with detecting if CLI is being invoked by node.exe instead of titanium.cmd on Windows [TIMOB-14933] - * Added support for config option cli.rejectUnauthorized to skip SSL cert validation [TIMOB-15783] - -3.1.2 (8/15/2013) -------------------- - * Updated "request" module to 2.25.0 [TIMOB-11267] - -3.1.1 (6/17/2013) -------------------- - * Added support for code processor plugin paths [TIMOB-13118] - -3.1.0 (4/16/2013) -------------------- - * Fixed scoping in hooks system. Added better error handling for bad hooks [TIMOB-13040] - * Fixed bug with "titanium config cli.logLevel" outputting as a log message [TIMOB-13194] - * Changed default log level to "trace" [TIMOB-13194] - * Fixed bug where hooks could reverse --no-colors flag [TIMOB-13374] - -3.0.24 (2/19/2013) -------------------- - * Enabled padding for non-log() messages such as info, debug, etc [TIMOB-12436] - * Fixed config saving to automatically create the ~/.titanium directory if it doesn't exist. [TIMOB-12437] - -3.0.23 (1/21/2013) -------------------- - * Fixed bug with setup command where an error occurs when a previously saved active SDK version is invalid [TIMOB-12268] - * Updated info command to output iOS certs by keychain based on a fix in node-appc [TIMOB-12033] - * Added terminal character encoding detection [TIMOB-12347] - * Fixed bug with detecting if setup needs to be run - -3.0.22 (12/21/2012) -------------------- - * Added the sdk select 'command' to the sdk's help screen [TIMOB-12113] - * Tiny i18n string update - -3.0.21 (12/10/2012) -------------------- - * In the advanced setup wizard, fixed iOS developer cert name validation to allow names with and without an ID [TIMOB-12003] - -3.0.20 (12/6/2012) -------------------- - * Updated i18n strings [TIMOB-11825] - -3.0.19 (11/30/2012) -------------------- - * Fixed bug with --sdk not properly overriding the SDK from the config file [TIMOB-11883] - -3.0.18 (11/21/2012) -------------------- - * Added support for searching for modules using both the module search path from the config file as well as the root of the selected Titanium SDK [TIMOB-11776] - -3.0.17 (11/20/2012) -------------------- - * Added better support for config values that are lists. Now you can set entire lists, append items to lists, and remove items from lists [TIMOB-11753] - * Updated I18N strings - -3.0.16 (11/8/2012) -------------------- - * Reduced "sdk" command's download progress bar width to reduce possibility of rendering artifacts [TIMOB-11470] - -3.0.15 (11/7/2012) -------------------- - * Fixed bug with duplicate abbreviation aliases were conflicting - -3.0.14 (11/7/2012) -------------------- - * Changed behavior to only list commands for the active Titanium SDK - * Added "select" subcommand to "sdk" command for selecting the active Titanium SDK - * Added better error message for invalid "sdk" command subcommands - * Fixed bugs with the "sdk" command download progress bar on Windows - * Added active Titanium SDK version to the banner - * Updated CLI to use new environ detect() method and pass in SDK paths from the config file - -3.0.13 (10/30/2012) -------------------- - * Fixed bug when CLI config doesn't have any Titanium SDK home directories defined - -3.0.12 (10/29/2012) -------------------- - * Added support for specifying additional Titanium SDK home directory paths in the CLI config - -3.0.11 (10/24/2012) -------------------- - * Added "ti" alias +## 8.1.5 (2/2/2026) + +- fix: Scan custom hooks path after loading the Titanium SDK + +## 8.1.4 (1/18/2026) + +- fix: Allow `colors` shim methods to be redefined by the real `colors` + +## 8.1.3 (1/17/2026) + +- fix: Define `colors` shim for `info` command +- fix: Default value ignored when getting an empty config value + +## 8.1.2 (10/28/2025) + +- fix: Add support for SDK `main` branch builds + +## 8.1.1 (10/21/2025) + +- fix: Emit `post-execute` hook for commands with async `run()` +- fix: Display SDK name, then the actual version +- fix: Skip run if `--sdk` version mismatches `` in `tiapp.xml` + and allow correct command to be forked + +## 8.1.0 (9/15/2025) + +- fix: On Windows call `powershell` instead of deprecated `wmic` command to + get the OS name and version. +- chore: Updated dependencies + +## 8.0.0 (8/1/2025) + +- BREAKING CHANGE: Require Node.js 20.18.1 or newer +- chore: Updated dependencies + +## 7.1.7 (4/3/2025) + +- fix: Fixed issue installing Hyperloop module where same name and version, + but different platform. + +## 7.1.6 (2/6/2025) + +- fix: Fixed command path for Node 22.13.1. + +## 7.1.5 (10/4/2024) + +- fix: Improve tiapp sdk-version handling +- chore: Update dependencies + +## 7.1.4 (8/4/2024) + +- fix: Load iOS hooks from `iphone` directory in the SDK + +## 7.1.3 (8/1/2024) + +- fix: Invalid `--platform` would cause crash because the platform + validation was being bypassed + +## 7.1.2 (7/31/2024) + +- fix: Force `--platform` to `ios`; the SDK converts `ios` to `iphone`, but + the CLI needs to reverse this to be able to look up the platform config + +## 7.1.1 (7/30/2024) + +- fix: Rename platform config `iphone` to `ios` to align with + `ti.targetPlatforms` + +## 7.1.0 (5/25/2024) + +- feat: Support async hook `init()` functions +- fix: Surface sdk install errors +- fix: `ti sdk rm ` treats confirm prompt as false +- fix: Assert required Node.js version +- fix: Clear out undefined command args which fixes `ti project` +- fix: `ti sdk install` no longer silently fails when installing new modules +- fix: When reinstalling an SDK, choosing "Overwrite" will force modules to + also be reinstalled +- fix: Properly handle result from `ti sdk install` overwrite prompt, + `ti sdk uninstall` version prompt, and `ti setup user` name prompt + +## 7.0.0 (5/10/2024) + +- Require Node.js 18 or newer +- Refactored entire codebase using ESM +- Removed `login`, `logout`, `plugin`, and `status` commands +- Removed all analytics/telemetry code +- Removed i18n logic; all output rendered in English +- Removed incomplete tab completion code +- `--sdk ` is now a global option + - It assumes the latest installed SDK version... + - If executing the `create` command without any options, it will prompt for + the SDK version to use +- Removed "default" SDK; `` in tiapp.xml is source of truth, + override with `--sdk ` arg +- Replaced custom CLI arg parser (based on `optimist`) with Commander.js + - Order of arguments matter; command options must come after command +- `-d`, `--project-dir` is now a global option + - Used to be `build`, `clean`, and `project` commands only, but needed so + the CLI can read the `tiapp.xml` and determine the ``; + defaults to the current working directory + - Since this conflicts with the `create` command's `-d`, `--workspace-dir` + option, special handling treats these two options the same +- Added a new `--debug` global flag to output CLI debug logging +- `ti config` changes: + - Added `--json` flag + - Replaced `--output json` with `--output json-object` output +- `ti info` changes: + - Added `--json` flag + - Removed `haxm` info + - Removed `genymotion` and VirtualBox info + - Removed macOS info including Xcode CLI Tools + - Removed `jarsigner` from JDK detection; no longer used thanks to Gradle + - Removed `nodeAppcVer` from Titanium CLI and Titanium SDKs in info +- `ti module` changes: + - Removed global `iphone` modules as it was just a copy of `ios` modules + - Modules with `platform` other than `android`, `commonjs`, `ios`, and + `iphone` will be ignored + - Modules with invalid semantic version will be ignored +- `ti sdk` changes: + - Added `--json` flag + - Removed `activeSDK` from JSON result + - `select` subcommand is a no-op + - `install` subcommand `--default` flag is a no-op +- `ti setup` changes: + - Removed Windows Store publishing info + - Removed `haxm` info + - Removed `sdk` setup + - Removed user locale setup + - Removed Xcode CLI tools detection + - Removed Titanium CLI Dependencies section from check + - Removed Java-based connection test from check +- Logger changes: + - Replaced "winston" based logger with lighter weight logger + - Trace and debug log messages written to `stderr` instead of `stdout` + - Added total run time trace message at end + - `--timestamp` option enabled only when command has `--log-level` option +- Performance improvements: + - Lazy load modules as much as possible, within reason + - Removed tons of old dead code + - Use smaller alternative dependencies +- Updated dependencies, replaced deprecated dependencies + - Replaced `colors` with `chalk` + - Replaced `fields` with `prompts` + - Replaced `humanize` with `pretty-bytes` + - Replaced `request` with `undici` + +## 6.1.1 (7/13/2022) + +- Allow SDK directories to contain any version suffix + +## 6.1.0 (6/28/2022) + +- Re-enabled installing CI builds +- Updated minor/patch dependencies + +## 6.0.2 (5/24/2022) + +- Disabled retrieving CI build branches and builds since it didn't work anyways + +## 6.0.1 (5/23/2022) + +- Updated banner copyright and links + +## 6.0.0 (5/23/2022) + +- BREAKING CHANGE: Dropped support for Node 12 and older +- feat(sdk): handle an sdk zip downloaded as an artifact from github +- feat(sdk): add timestamp flag +- fix: optimize error for unknown deviceid + +## 5.2.1 (2/15/2019) + +- Fix selection of SDK when it is already installed [TIMOB-25179] +- Update dependencies + +## 5.2.0 (8/7/2018) + +- Fix failure due to bad URL on `ti setup check` [TIMOB-26206] +- Fix incorrect dependencies being reported when running `appc ti setup check` [TIMOB-24892] +- Update dependencies + +## 5.1.1 (6/5/2018) + +- Added flag to disable analytics [TIMOB-26083] +- Removed email from analytics payloads [TIMOB-26083] + +## 5.1.0 (3/12/2018) + +- Fix typo in Android NDK path prompt +- Support detection of JDK 9 [TIMOB-25429] + +## 5.0.14 (12/5/2017) + +- Fix forking of correct SDK during `ti build` [TIMOB-24690] + +## 5.0.13 (4/26/2017) + +- Error thrown in CLI command plugin errors on load [TIMOB-24546] +- Removed hook that fixed some Titanium SDK 3.x versions (which are no longer supported) +- Avoid re-install of SDK from zipfile if already installed +- Fix NDK validation, fix google.com network test #185 + +## 5.0.10 (9/22/2016) + +- Fixed bug when running `ti setup sdk` and entering "latest" when the selected SDK no longer exists [TIMOB-23941] + +## 5.0.4 (9/17/2015) + +- Added actual SDK version to `ti sdk list` output as well as SDK details to `ti sdk list -o json` [TIMOB-19541] +- Updated NPM dependencies + +## 5.0.3 (9/9/2015) + +- No longer display latest Node.js and NPM version as it confusing may imply the Titanium CLI supports them [TIMOB-19470] +- Updated NPM dependencies + +## 5.0.2 (9/9/2015) + +- Fixed bug where 'ti setup check' was reporting the latest NPM version, not the latest stable version [TIMOB-19470] + +## 5.0.1 (9/3/2015) + +- Updated to node-appc 0.2.31 which fixes a bug with the JDK detection + +## 5.0.0 (9/3/2015) + +- Updated to node-appc 0.2.30 + +## 4.1.5 (8/18/2015) + +- Fixed console colors when running in PowerShell [TIMOB-19126] + +## 4.1.4 (8/4/2015) + +- Fixed bug where the command line args weren't being parsed again after handling a branching option [TIMOB-19281] + +## 4.0.1 (5/29/2015) + +- Fixed bug with building an app that had a different Titanium SDK version than the selected SDK and abbreviated option names were being used [TIMOB-18826] + +## 4.0.0 (5/20/2015) + +- Added support for generic Titanium SDK-level info [TIMOB-17836] +- Allow the colors to be controlled explicitly regardless if attached or detached from a TTY +- Fixed bug where abbreviated options without values passed in before the last argument being treated as a boolean [TIMOB-18067] +- Ripped out all authentication requirements. Analytics are now anonymous. Login and logout are no ops. [TIMOB-18711] +- Capped SDK version monkey patch for which --password is converted to --store-password for Android [TIMOB-18783] + +## 3.4.2 (3/6/2015) + +- Fixed compatibility issues with Node 0.12 [TIMOB-18538] +- Added Node.js version check when using a Titanium SDK [TIMOB-18629] +- Drop support for Node.js 0.8 [TIMOB-18414] + +## 3.4.1 (11/14/14) + +- Fixed bug in Titanium SDK 3 bug fix hook where Xcode should only be removed when running on OS X [TIMOB-17808] + +## 3.4.0 (9/29/14) + +- Added support for selecting latest stable SDK [TIMOB-17378] +- Fixed issues with config.json being overwritten at the same time [TIMOB-17346] +- Improved handling of corrupt a config.json file [TIMOB-17346] +- Fixed backwards compatibility with Titanium SDK 3.3.0 and older when building an iOS app and Xcode 6 or newer is installed +- Added support for a "helpNoPrompt()" callback when a missing option is encountered +- Fixed bug with abbreviated options that don't have a value being set to true +- Fixed bug where Xcode 6 was not being removed from Titanium SDK 3.3.0 and older from the "ti info" results [TIMOB-17649] +- Fixed bug where "ti info" failed when an invalid Titanium SDK is selected [TIMOB-17666] +- Added support for paths.xcode in "ti config" [TIMOB-17696] + +## 3.3.0 (7/17/14) + +- Fixed bug with 'ti setup' when the selected Titanium SDK does not exist [TIMOB-12268] +- Added Genymotion environment information to 'ti info' command [TIMOB-16349] +- Fixed bug where OS and Node.js info was always being returned in 'ti info' +- Added wp8 publisher guid to 'ti setup' [TIMOB-16748] +- Added conflicting hook detection and improved hook error reporting [TIMOB-13847] +- Added support for an array of hook events to emit; needed for [TIMOB-10752] +- Updated Appcelerator API URLs to api.appcelerator.com [TIMOB-16282] +- Added support for Titanium SDKs that can be named anything [TIMOB-16052] +- Improved error handling when sdk command requests list of releases [TIMOB-16917] +- Fixed bug with prompting for missing or invalid options that use generic prompting +- Fixed sorting of Titanium SDKs in the 'sdk select' command to only list valid SDKs and order by their actual version number [TIMOB-16974] +- Fixed bug where integer config values were being saved as strings instead of numbers [TIMOB-17000] +- Fixed 'setup check' command when fetching available Node.js and NPM releases [TIMOB-16996] +- Fixed bug with boolean config setting values being saved as integers [TIMOB-17087] +- Moved the sending of analytics from the 'exit' event to the command finished callback [TIMOB-17046] +- Fixed bug where the CLI would wait for analytics to send [TIMOB-17206] +- Fixed formatting of multiline issues in 'ti info' [TIMOB-17221] +- Fixed display of Android tools that are newer than the maximum supported version [TIMOB-17221] + +## 3.2.3 (5/1/2014) + +- When installing a Titanium SDK using the CI version name, but omitting the branch, it now automatically scans all branches [TIMOB-15899] +- Fixed 'sdk' command to display custom Titanium SDK paths in SDK Install Locations [TIMOB-16141] +- Fixed bug where the password was not being discarded after logging in and ends up confusing the Android build [TIMOB-16422] +- Fixed list of options being displayed in help screen when colors are enabled on Windows [TIMOB-12759] +- Added temp directory checking to the 'setup check' command [TIMOB-16671] +- Fixed disabling of colors for 'setup' command. Also fixed --no-color flag [TIMOB-16853] + +## 3.2.2 + +- Version skipped, no changes + +## 3.2.1 (2/10/2014) + +- Fixed bug where npm version was not being displayed due to race condition [TIMOB-15962] +- Fixed bug where if the node executable is not called "node", the CLI would error because argv[0] != process.execPath [TIMOB-15804] +- Fixed bug when running "ti help" for a command that doesn't have 'platforms' [TIMOB-16233] +- Fixed bug where CLI commands that require a minimum version of the Titanium CLI were not being checked properly [TIMOB-16361] +- Fixed command and hook loading when comparing the minimum required CLI version in which the version has a -beta suffix [TIMOB-16365] +- Fixed bug when a SDK >=3.2.0 build is run with --password instead of --store-password [TIMOB-16354] + +## 3.2.0 (12/20/2013) + +- Completely overhauled entire CLI architecture +- Brand new setup command with 'check environment' and 'quick setup' sections +- Added better error handling with logging in and out of the Appc network; saving cli config [TIMOB-13908] +- Added ID for each installed Android SDK and add-on to info command output [TIMOB-13797] +- Fixed bug with CLI argument parser not properly removing the node process from the arguments [TIMOB-14074] +- Added CLI hook to fix deploy type bug in Titanium SDK 3.0.0-3.1.X where deploy type not correctly set for iOS dist builds [TIMOB-14961] +- Updated all afs.exists() calls to fs.existsSync() calls since we no longer support Node.js 0.6 +- Fixed bug when running quick setup when no SDKs installed [TIMOB-14965] +- Adjusted placement of post-valdiate event hook +- Fixed bug with option callbacks not being called for prompted options +- Updated info command to use latest platform-specific detect() signature +- Fixed minor bug with multiple options with same name not being converted to an array +- Fixed all places where broken symlinks would cause errors +- Fixed bug with detecting if CLI is being invoked by node.exe instead of titanium.cmd on Windows [TIMOB-14933] +- Removed node-appc version check +- Improved invalid and missing option handling +- Refactored info command to call platform specific environment detection and info rendering +- When a Titanium SDK 3.2 or newer is selected, info command displays much more info such as cert validity, installation issues, etc +- Fixed bug in logger where errors were not honoring the --no-colors flag +- Fixed escaping of default global ignoreDirs/ignoreFiles +- Added ability to hide flag default values +- Added --no-progress-bars flag to control display of progress bars and busy indicators which are enabled by default +- Fixed a few places where --no-prompt was not being honored +- Fixed sdk install command would fail when --no-prompt is set [TIMOB-15431] +- Fixed bug when installing a Titanium SDK and the temp directory does not exist +- Fixed bug when selected SDK is pre-3.0 or does not exist [TIMOB-15507] +- Added CLI tools information to info and setup check commands on Mac OS X +- Fixed command line argument parser to accept empty option values [TIMOB-15608] +- Added ampersand checks to the setup command's Android SDK path prompt on Windows +- Fixed bug where --quiet, --no-prompt, and --no-progress-bars when setting a value on via ti config or running ti setup would save those flags to the cli config +- Added alias --no-color for --no-colors because I can never remember which one it is +- Updated third party Node.js module dependency version where safe to do so +- Updated the 'setup check' command to work offline and display connection diagnostics +- Fixed bug when --username and --password are supplied at the command line +- Fixed bug with the paths.sdks config setting continuously appending the default Titanium SDK install location [TIMOB-15813] +- Fixed bug with 'npm outdated' returning a version of 'missing' [TIMOB-15842] +- Removed Java 1.7 warning from the 'setup check' command +- Fixed bug where options that have valid values but don't have a validate function never have their callbacks fired [TIMOB-15935] + +## 3.1.4 (12/18/2013) + +- Fixed bug with detecting if CLI is being invoked by node.exe instead of titanium.cmd on Windows [TIMOB-14933] +- Added support for config option cli.rejectUnauthorized to skip SSL cert validation [TIMOB-15783] + +## 3.1.2 (8/15/2013) + +- Updated "request" module to 2.25.0 [TIMOB-11267] + +## 3.1.1 (6/17/2013) + +- Added support for code processor plugin paths [TIMOB-13118] + +## 3.1.0 (4/16/2013) + +- Fixed scoping in hooks system. Added better error handling for bad hooks [TIMOB-13040] +- Fixed bug with "titanium config cli.logLevel" outputting as a log message [TIMOB-13194] +- Changed default log level to "trace" [TIMOB-13194] +- Fixed bug where hooks could reverse --no-colors flag [TIMOB-13374] + +## 3.0.24 (2/19/2013) + +- Enabled padding for non-log() messages such as info, debug, etc [TIMOB-12436] +- Fixed config saving to automatically create the ~/.titanium directory if it doesn't exist. [TIMOB-12437] + +## 3.0.23 (1/21/2013) + +- Fixed bug with setup command where an error occurs when a previously saved active SDK version is invalid [TIMOB-12268] +- Updated info command to output iOS certs by keychain based on a fix in node-appc [TIMOB-12033] +- Added terminal character encoding detection [TIMOB-12347] +- Fixed bug with detecting if setup needs to be run + +## 3.0.22 (12/21/2012) + +- Added the sdk select 'command' to the sdk's help screen [TIMOB-12113] +- Tiny i18n string update + +## 3.0.21 (12/10/2012) + +- In the advanced setup wizard, fixed iOS developer cert name validation to allow names with and without an ID [TIMOB-12003] + +## 3.0.20 (12/6/2012) + +- Updated i18n strings [TIMOB-11825] + +## 3.0.19 (11/30/2012) + +- Fixed bug with --sdk not properly overriding the SDK from the config file [TIMOB-11883] + +## 3.0.18 (11/21/2012) + +- Added support for searching for modules using both the module search path from the config file as well as the root of the selected Titanium SDK [TIMOB-11776] + +## 3.0.17 (11/20/2012) + +- Added better support for config values that are lists. Now you can set entire lists, append items to lists, and remove items from lists [TIMOB-11753] +- Updated I18N strings + +## 3.0.16 (11/8/2012) + +- Reduced "sdk" command's download progress bar width to reduce possibility of rendering artifacts [TIMOB-11470] + +## 3.0.15 (11/7/2012) + +- Fixed bug with duplicate abbreviation aliases were conflicting + +## 3.0.14 (11/7/2012) + +- Changed behavior to only list commands for the active Titanium SDK +- Added "select" subcommand to "sdk" command for selecting the active Titanium SDK +- Added better error message for invalid "sdk" command subcommands +- Fixed bugs with the "sdk" command download progress bar on Windows +- Added active Titanium SDK version to the banner +- Updated CLI to use new environ detect() method and pass in SDK paths from the config file + +## 3.0.13 (10/30/2012) + +- Fixed bug when CLI config doesn't have any Titanium SDK home directories defined + +## 3.0.12 (10/29/2012) + +- Added support for specifying additional Titanium SDK home directory paths in the CLI config + +## 3.0.11 (10/24/2012) + +- Added "ti" alias diff --git a/README.md b/README.md index a7f1d45e4..82953da65 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,6 @@ To show help or help for a specific command. Visit https://titaniumsdk.com for more info. - ## Commands - [`ti build`](#build) - build a project diff --git a/package.json b/package.json index feb44fa6b..1b91ee6ce 100644 --- a/package.json +++ b/package.json @@ -1,65 +1,68 @@ { - "name": "titanium", - "version": "8.1.5", - "author": "TiDev, Inc. ", - "description": "Command line interface for building Titanium SDK apps", - "type": "module", - "keywords": [ - "titanium", - "titanium-sdk", - "tidev", - "mobile", - "ios", - "iphone", - "android" - ], - "homepage": "https://titaniumsdk.com", - "bugs": "https://github.com/tidev/titanium-cli/issues", - "repository": { - "type": "git", - "url": "git+https://github.com/tidev/titanium-cli.git" - }, - "preferGlobal": true, - "dependencies": { - "@xmldom/xmldom": "0.9.8", - "chalk": "5.6.2", - "commander": "14.0.3", - "execa": "9.6.1", - "fs-extra": "11.3.4", - "pretty-bytes": "7.1.0", - "prompts": "2.4.2", - "semver": "7.7.4", - "undici": "7.22.0", - "which": "6.0.1", - "wrap-ansi": "10.0.0", - "xpath": "0.0.34", - "yauzl": "3.2.0" - }, - "devDependencies": { - "@reporters/github": "1.12.0", - "@vitest/coverage-istanbul": "4.0.18", - "c8": "11.0.0", - "glob": "13.0.6", - "globals": "17.4.0", - "memory-streams": "0.1.3", - "oxlint": "1.51.0", - "proxy": "2.2.0" - }, - "license": "Apache-2.0", - "bin": { - "ti": "bin/ti.js", - "titanium": "bin/ti.js" - }, - "files": [ - "./src" - ], - "scripts": { - "coverage": "node scripts/test.js --coverage", - "lint": "oxlint", - "test": "node scripts/test.js", - "test-only": "node scripts/test.js --only" - }, - "engines": { - "node": ">=20.18.1" - } + "name": "titanium", + "version": "8.1.5", + "description": "Command line interface for building Titanium SDK apps", + "keywords": [ + "android", + "ios", + "iphone", + "mobile", + "tidev", + "titanium", + "titanium-sdk" + ], + "homepage": "https://titaniumsdk.com", + "bugs": "https://github.com/tidev/titanium-cli/issues", + "license": "Apache-2.0", + "author": "TiDev, Inc. ", + "repository": { + "type": "git", + "url": "git+https://github.com/tidev/titanium-cli.git" + }, + "bin": { + "ti": "bin/ti.js", + "titanium": "bin/ti.js" + }, + "files": [ + "./src" + ], + "type": "module", + "scripts": { + "coverage": "node scripts/test.js --coverage", + "fmt": "oxfmt", + "fmt:check": "oxfmt --check", + "lint": "oxlint", + "test": "node scripts/test.js", + "test-only": "node scripts/test.js --only" + }, + "dependencies": { + "@xmldom/xmldom": "0.9.8", + "chalk": "5.6.2", + "commander": "14.0.3", + "execa": "9.6.1", + "fs-extra": "11.3.4", + "pretty-bytes": "7.1.0", + "prompts": "2.4.2", + "semver": "7.7.4", + "undici": "7.22.0", + "which": "6.0.1", + "wrap-ansi": "10.0.0", + "xpath": "0.0.34", + "yauzl": "3.2.0" + }, + "devDependencies": { + "@reporters/github": "1.12.0", + "@vitest/coverage-istanbul": "4.0.18", + "c8": "11.0.0", + "glob": "13.0.6", + "globals": "17.4.0", + "memory-streams": "0.1.3", + "oxfmt": "0.36.0", + "oxlint": "1.51.0", + "proxy": "2.2.0" + }, + "preferGlobal": true, + "engines": { + "node": ">=20.18.1" + } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0d41cf58b..d848408b9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -53,7 +53,7 @@ importers: version: 1.12.0 '@vitest/coverage-istanbul': specifier: 4.0.18 - version: 4.0.18(vitest@1.4.0) + version: 4.0.18(vitest@4.0.18) c8: specifier: 11.0.0 version: 11.0.0 @@ -66,6 +66,9 @@ importers: memory-streams: specifier: 0.1.3 version: 0.1.3 + oxfmt: + specifier: 0.36.0 + version: 0.36.0 oxlint: specifier: 1.51.0 version: 1.51.0 @@ -158,141 +161,159 @@ packages: resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} engines: {node: '>=18'} - '@esbuild/aix-ppc64@0.21.5': - resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} - engines: {node: '>=12'} + '@esbuild/aix-ppc64@0.27.3': + resolution: {integrity: sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==} + engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.21.5': - resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} - engines: {node: '>=12'} + '@esbuild/android-arm64@0.27.3': + resolution: {integrity: sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==} + engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.21.5': - resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} - engines: {node: '>=12'} + '@esbuild/android-arm@0.27.3': + resolution: {integrity: sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==} + engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.21.5': - resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} - engines: {node: '>=12'} + '@esbuild/android-x64@0.27.3': + resolution: {integrity: sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==} + engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.21.5': - resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} - engines: {node: '>=12'} + '@esbuild/darwin-arm64@0.27.3': + resolution: {integrity: sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==} + engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.21.5': - resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} - engines: {node: '>=12'} + '@esbuild/darwin-x64@0.27.3': + resolution: {integrity: sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==} + engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.21.5': - resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} - engines: {node: '>=12'} + '@esbuild/freebsd-arm64@0.27.3': + resolution: {integrity: sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==} + engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.21.5': - resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} - engines: {node: '>=12'} + '@esbuild/freebsd-x64@0.27.3': + resolution: {integrity: sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==} + engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.21.5': - resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} - engines: {node: '>=12'} + '@esbuild/linux-arm64@0.27.3': + resolution: {integrity: sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==} + engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.21.5': - resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} - engines: {node: '>=12'} + '@esbuild/linux-arm@0.27.3': + resolution: {integrity: sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==} + engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.21.5': - resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} - engines: {node: '>=12'} + '@esbuild/linux-ia32@0.27.3': + resolution: {integrity: sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==} + engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.21.5': - resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} - engines: {node: '>=12'} + '@esbuild/linux-loong64@0.27.3': + resolution: {integrity: sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==} + engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.21.5': - resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} - engines: {node: '>=12'} + '@esbuild/linux-mips64el@0.27.3': + resolution: {integrity: sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==} + engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.21.5': - resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} - engines: {node: '>=12'} + '@esbuild/linux-ppc64@0.27.3': + resolution: {integrity: sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==} + engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.21.5': - resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} - engines: {node: '>=12'} + '@esbuild/linux-riscv64@0.27.3': + resolution: {integrity: sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==} + engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.21.5': - resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} - engines: {node: '>=12'} + '@esbuild/linux-s390x@0.27.3': + resolution: {integrity: sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==} + engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.21.5': - resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} - engines: {node: '>=12'} + '@esbuild/linux-x64@0.27.3': + resolution: {integrity: sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==} + engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-x64@0.21.5': - resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} - engines: {node: '>=12'} + '@esbuild/netbsd-arm64@0.27.3': + resolution: {integrity: sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.27.3': + resolution: {integrity: sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==} + engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-x64@0.21.5': - resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} - engines: {node: '>=12'} + '@esbuild/openbsd-arm64@0.27.3': + resolution: {integrity: sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.27.3': + resolution: {integrity: sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==} + engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/sunos-x64@0.21.5': - resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} - engines: {node: '>=12'} + '@esbuild/openharmony-arm64@0.27.3': + resolution: {integrity: sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.27.3': + resolution: {integrity: sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==} + engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.21.5': - resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} - engines: {node: '>=12'} + '@esbuild/win32-arm64@0.27.3': + resolution: {integrity: sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==} + engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.21.5': - resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} - engines: {node: '>=12'} + '@esbuild/win32-ia32@0.27.3': + resolution: {integrity: sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==} + engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.21.5': - resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} - engines: {node: '>=12'} + '@esbuild/win32-x64@0.27.3': + resolution: {integrity: sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==} + engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -300,10 +321,6 @@ packages: resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} engines: {node: '>=8'} - '@jest/schemas@29.6.3': - resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} @@ -320,6 +337,128 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + '@oxfmt/binding-android-arm-eabi@0.36.0': + resolution: {integrity: sha512-Z4yVHJWx/swHHjtr0dXrBZb6LxS+qNz1qdza222mWwPTUK4L790+5i3LTgjx3KYGBzcYpjaiZBw4vOx94dH7MQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@oxfmt/binding-android-arm64@0.36.0': + resolution: {integrity: sha512-3ElCJRFNPQl7jexf2CAa9XmAm8eC5JPrIDSjc9jSchkVSFTEqyL0NtZinBB2h1a4i4JgP1oGl/5G5n8YR4FN8Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@oxfmt/binding-darwin-arm64@0.36.0': + resolution: {integrity: sha512-nak4znWCqIExKhYSY/mz/lWsqWIpdsS7o0+SRzXR1Q0m7GrMcG1UrF1pS7TLGZhhkf7nTfEF7q6oZzJiodRDuw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@oxfmt/binding-darwin-x64@0.36.0': + resolution: {integrity: sha512-V4GP96thDnpKx6ADnMDnhIXNdtV+Ql9D4HUU+a37VTeVbs5qQSF/s6hhUP1b3xUqU7iRcwh72jUU2Y12rtGHAw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@oxfmt/binding-freebsd-x64@0.36.0': + resolution: {integrity: sha512-/xapWCADfI5wrhxpEUjhI9fnw7MV5BUZizVa8e24n3VSK6A3Y1TB/ClOP1tfxNspykFKXp4NBWl6NtDJP3osqQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@oxfmt/binding-linux-arm-gnueabihf@0.36.0': + resolution: {integrity: sha512-1lOmv61XMFIH5uNm27620kRRzWt/RK6tdn250BRDoG9W7OXGOQ5UyI1HVT+SFkoOoKztBiinWgi68+NA1MjBVQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxfmt/binding-linux-arm-musleabihf@0.36.0': + resolution: {integrity: sha512-vMH23AskdR1ujUS9sPck2Df9rBVoZUnCVY86jisILzIQ/QQ/yKUTi7tgnIvydPx7TyB/48wsQ5QMr5Knq5p/aw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxfmt/binding-linux-arm64-gnu@0.36.0': + resolution: {integrity: sha512-Hy1V+zOBHpBiENRx77qrUTt5aPDHeCASRc8K5KwwAHkX2AKP0nV89eL17hsZrE9GmnXFjsNmd80lyf7aRTXsbw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@oxfmt/binding-linux-arm64-musl@0.36.0': + resolution: {integrity: sha512-SPGLJkOIHSIC6ABUQ5V8NqJpvYhMJueJv26NYqfCnwi/Mn6A61amkpJJ9Suy0Nmvs+OWESJpcebrBUbXPGZyQQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@oxfmt/binding-linux-ppc64-gnu@0.36.0': + resolution: {integrity: sha512-3EuoyB8x9x8ysYJjbEO/M9fkSk72zQKnXCvpZMDHXlnY36/1qMp55Nm0PrCwjGO/1pen5hdOVkz9WmP3nAp2IQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@oxfmt/binding-linux-riscv64-gnu@0.36.0': + resolution: {integrity: sha512-MpY3itLwpGh8dnywtrZtaZ604T1m715SydCKy0+qTxetv+IHzuA+aO/AGzrlzUNYZZmtWtmDBrChZGibvZxbRQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@oxfmt/binding-linux-riscv64-musl@0.36.0': + resolution: {integrity: sha512-mmDhe4Vtx+XwQPRPn/V25+APnkApYgZ23q+6GVsNYY98pf3aU0aI3Me96pbRs/AfJ1jIiGC+/6q71FEu8dHcHw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@oxfmt/binding-linux-s390x-gnu@0.36.0': + resolution: {integrity: sha512-AYXhU+DmNWLSnvVwkHM92fuYhogtVHab7UQrPNaDf1sxadugg9gWVmcgJDlIwxJdpk5CVW/TFvwUKwI432zhhA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@oxfmt/binding-linux-x64-gnu@0.36.0': + resolution: {integrity: sha512-H16QhhQ3usoakMleiAAQ2mg0NsBDAdyE9agUgfC8IHHh3jZEbr0rIKwjEqwbOHK5M0EmfhJmr+aGO/MgZPsneA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@oxfmt/binding-linux-x64-musl@0.36.0': + resolution: {integrity: sha512-EFFGkixA39BcmHiCe2ECdrq02D6FCve5ka6ObbvrheXl4V+R0U/E+/uLyVx1X65LW8TA8QQHdnbdDallRekohw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@oxfmt/binding-openharmony-arm64@0.36.0': + resolution: {integrity: sha512-zr/t369wZWFOj1qf06Z5gGNjFymfUNDrxKMmr7FKiDRVI1sNsdKRCuRL4XVjtcptKQ+ao3FfxLN1vrynivmCYg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@oxfmt/binding-win32-arm64-msvc@0.36.0': + resolution: {integrity: sha512-FxO7UksTv8h4olzACgrqAXNF6BP329+H322323iDrMB5V/+a1kcAw07fsOsUmqNrb9iJBsCQgH/zqcqp5903ag==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@oxfmt/binding-win32-ia32-msvc@0.36.0': + resolution: {integrity: sha512-OjoMQ89H01M0oLMfr/CPNH1zi48ZIwxAKObUl57oh7ssUBNDp/2Vjf7E1TQ8M4oj4VFQ/byxl2SmcPNaI2YNDg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ia32] + os: [win32] + + '@oxfmt/binding-win32-x64-msvc@0.36.0': + resolution: {integrity: sha512-MoyeQ9S36ZTz/4bDhOKJgOBIDROd4dQ5AkT9iezhEaUBxAPdNX9Oq0jD8OSnCj3G4wam/XNxVWKMA52kmzmPtQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + '@oxlint/binding-android-arm-eabi@1.51.0': resolution: {integrity: sha512-jJYIqbx4sX+suIxWstc4P7SzhEwb4ArWA2KVrmEuu9vH2i0qM6QIHz/ehmbGE4/2fZbpuMuBzTl7UkfNoqiSgw==} engines: {node: ^20.19.0 || >=22.12.0} @@ -367,48 +506,56 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] + libc: [glibc] '@oxlint/binding-linux-arm64-musl@1.51.0': resolution: {integrity: sha512-YSJua5irtG4DoMAjUapDTPhkQLHhBIY0G9JqlZS6/SZPzqDkPku/1GdWs0D6h/wyx0Iz31lNCfIaWKBQhzP0wQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] + libc: [musl] '@oxlint/binding-linux-ppc64-gnu@1.51.0': resolution: {integrity: sha512-7L4Wj2IEUNDETKssB9IDYt16T6WlF+X2jgC/hBq3diGHda9vJLpAgb09+D3quFq7TdkFtI7hwz/jmuQmQFPc1Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] + libc: [glibc] '@oxlint/binding-linux-riscv64-gnu@1.51.0': resolution: {integrity: sha512-cBUHqtOXy76G41lOB401qpFoKx1xq17qYkhWrLSM7eEjiHM9sOtYqpr6ZdqCnN9s6ZpzudX4EkeHOFH2E9q0vA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] + libc: [glibc] '@oxlint/binding-linux-riscv64-musl@1.51.0': resolution: {integrity: sha512-WKbg8CysgZcHfZX0ixQFBRSBvFZUHa3SBnEjHY2FVYt2nbNJEjzTxA3ZR5wMU0NOCNKIAFUFvAh5/XJKPRJuJg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] + libc: [musl] '@oxlint/binding-linux-s390x-gnu@1.51.0': resolution: {integrity: sha512-N1QRUvJTxqXNSu35YOufdjsAVmKVx5bkrggOWAhTWBc3J4qjcBwr1IfyLh/6YCg8sYRSR1GraldS9jUgJL/U4A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] + libc: [glibc] '@oxlint/binding-linux-x64-gnu@1.51.0': resolution: {integrity: sha512-e0Mz0DizsCoqNIjeOg6OUKe8JKJWZ5zZlwsd05Bmr51Jo3AOL4UJnPvwKumr4BBtBrDZkCmOLhCvDGm95nJM2g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] + libc: [glibc] '@oxlint/binding-linux-x64-musl@1.51.0': resolution: {integrity: sha512-wD8HGTWhYBKXvRDvoBVB1y+fEYV01samhWQSy1Zkxq2vpezvMnjaFKRuiP6tBNITLGuffbNDEXOwcAhJ3gI5Ug==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] + libc: [musl] '@oxlint/binding-openharmony-arm64@1.51.0': resolution: {integrity: sha512-5NSwQ2hDEJ0GPXqikjWtwzgAQCsS7P9aLMNenjjKa+gknN3lTCwwwERsT6lKXSirfU3jLjexA2XQvQALh5h27w==} @@ -471,66 +618,79 @@ packages: resolution: {integrity: sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==} cpu: [arm] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.59.0': resolution: {integrity: sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==} cpu: [arm] os: [linux] + libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.59.0': resolution: {integrity: sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==} cpu: [arm64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.59.0': resolution: {integrity: sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==} cpu: [arm64] os: [linux] + libc: [musl] '@rollup/rollup-linux-loong64-gnu@4.59.0': resolution: {integrity: sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==} cpu: [loong64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-loong64-musl@4.59.0': resolution: {integrity: sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==} cpu: [loong64] os: [linux] + libc: [musl] '@rollup/rollup-linux-ppc64-gnu@4.59.0': resolution: {integrity: sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==} cpu: [ppc64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-ppc64-musl@4.59.0': resolution: {integrity: sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==} cpu: [ppc64] os: [linux] + libc: [musl] '@rollup/rollup-linux-riscv64-gnu@4.59.0': resolution: {integrity: sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==} cpu: [riscv64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-musl@4.59.0': resolution: {integrity: sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==} cpu: [riscv64] os: [linux] + libc: [musl] '@rollup/rollup-linux-s390x-gnu@4.59.0': resolution: {integrity: sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==} cpu: [s390x] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.59.0': resolution: {integrity: sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==} cpu: [x64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-musl@4.59.0': resolution: {integrity: sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==} cpu: [x64] os: [linux] + libc: [musl] '@rollup/rollup-openbsd-x64@4.59.0': resolution: {integrity: sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==} @@ -565,13 +725,19 @@ packages: '@sec-ant/readable-stream@0.4.1': resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} - '@sinclair/typebox@0.27.10': - resolution: {integrity: sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==} - '@sindresorhus/merge-streams@4.0.0': resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} engines: {node: '>=18'} + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} @@ -583,34 +749,39 @@ packages: peerDependencies: vitest: 4.0.18 - '@vitest/expect@1.4.0': - resolution: {integrity: sha512-Jths0sWCJZ8BxjKe+p+eKsoqev1/T8lYcrjavEaz8auEJ4jAVY0GwW3JKmdVU4mmNPLPHixh4GNXP7GFtAiDHA==} + '@vitest/expect@4.0.18': + resolution: {integrity: sha512-8sCWUyckXXYvx4opfzVY03EOiYVxyNrHS5QxX3DAIi5dpJAAkyJezHCP77VMX4HKA2LDT/Jpfo8i2r5BE3GnQQ==} + + '@vitest/mocker@4.0.18': + resolution: {integrity: sha512-HhVd0MDnzzsgevnOWCBj5Otnzobjy5wLBe4EdeeFGv8luMsGcYqDuFRMcttKWZA5vVO8RFjexVovXvAM4JoJDQ==} + peerDependencies: + msw: ^2.4.9 + vite: ^6.0.0 || ^7.0.0-0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@4.0.18': + resolution: {integrity: sha512-P24GK3GulZWC5tz87ux0m8OADrQIUVDPIjjj65vBXYG17ZeU3qD7r+MNZ1RNv4l8CGU2vtTRqixrOi9fYk/yKw==} - '@vitest/runner@1.4.0': - resolution: {integrity: sha512-EDYVSmesqlQ4RD2VvWo3hQgTJ7ZrFQ2VSJdfiJiArkCerDAGeyF1i6dHkmySqk573jLp6d/cfqCN+7wUB5tLgg==} + '@vitest/runner@4.0.18': + resolution: {integrity: sha512-rpk9y12PGa22Jg6g5M3UVVnTS7+zycIGk9ZNGN+m6tZHKQb7jrP7/77WfZy13Y/EUDd52NDsLRQhYKtv7XfPQw==} - '@vitest/snapshot@1.4.0': - resolution: {integrity: sha512-saAFnt5pPIA5qDGxOHxJ/XxhMFKkUSBJmVt5VgDsAqPTX6JP326r5C/c9UuCMPoXNzuudTPsYDZCoJ5ilpqG2A==} + '@vitest/snapshot@4.0.18': + resolution: {integrity: sha512-PCiV0rcl7jKQjbgYqjtakly6T1uwv/5BQ9SwBLekVg/EaYeQFPiXcgrC2Y7vDMA8dM1SUEAEV82kgSQIlXNMvA==} - '@vitest/spy@1.4.0': - resolution: {integrity: sha512-Ywau/Qs1DzM/8Uc+yA77CwSegizMlcgTJuYGAi0jujOteJOUf1ujunHThYo243KG9nAyWT3L9ifPYZ5+As/+6Q==} + '@vitest/spy@4.0.18': + resolution: {integrity: sha512-cbQt3PTSD7P2OARdVW3qWER5EGq7PHlvE+QfzSC0lbwO+xnt7+XH06ZzFjFRgzUX//JmpxrCu92VdwvEPlWSNw==} - '@vitest/utils@1.4.0': - resolution: {integrity: sha512-mx3Yd1/6e2Vt/PUC98DcqTirtfxUyAZ32uK82r8rZzbtBeBo+nqgnjx/LvqQdWsrvNtm14VmurNgcf4nqY5gJg==} + '@vitest/utils@4.0.18': + resolution: {integrity: sha512-msMRKLMVLWygpK3u2Hybgi4MNjcYJvwTb0Ru09+fOyCXIgT5raYP041DRRdiJiI3k/2U6SEbAETB3YtBrUkCFA==} '@xmldom/xmldom@0.9.8': resolution: {integrity: sha512-p96FSY54r+WJ50FIOsCOjyj/wavs8921hG5+kVMmZgKcvIKxMXHTrjNJvRgWa/zuX3B6t2lijLNFaOyuxUH+2A==} engines: {node: '>=14.6'} - acorn-walk@8.3.5: - resolution: {integrity: sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==} - engines: {node: '>=0.4.0'} - - acorn@8.16.0: - resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} - engines: {node: '>=0.4.0'} - hasBin: true - ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -627,10 +798,6 @@ packages: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} - ansi-styles@5.2.0: - resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} - engines: {node: '>=10'} - ansi-styles@6.2.3: resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} engines: {node: '>=12'} @@ -639,8 +806,9 @@ packages: resolution: {integrity: sha512-h6k/zfFgusnv3i5TU08KQkVKuCPBtL/PWQbWkHUxvJrZ2nAyeaUupneemcrgn1xmqxPQsPIzwkUhOpoqPDRZuA==} engines: {node: '>= 6.0.0'} - assertion-error@1.1.0: - resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} balanced-match@4.0.4: resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} @@ -676,10 +844,6 @@ packages: monocart-coverage-reports: optional: true - cac@6.7.14: - resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} - engines: {node: '>=8'} - camelcase@5.0.0: resolution: {integrity: sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==} engines: {node: '>=6'} @@ -687,9 +851,9 @@ packages: caniuse-lite@1.0.30001776: resolution: {integrity: sha512-sg01JDPzZ9jGshqKSckOQthXnYwOEP50jeVFhaSFbZcOy05TiuuaffDOfcwtCisJ9kNQuLBFibYywv2Bgm9osw==} - chai@4.5.0: - resolution: {integrity: sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==} - engines: {node: '>=4'} + chai@6.2.2: + resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} + engines: {node: '>=18'} chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} @@ -699,9 +863,6 @@ packages: resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} - check-error@1.0.3: - resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} - cliui@8.0.1: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} @@ -723,9 +884,6 @@ packages: resolution: {integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==} engines: {node: '>=20'} - confbox@0.1.8: - resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} - convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} @@ -745,23 +903,18 @@ packages: supports-color: optional: true - deep-eql@4.1.4: - resolution: {integrity: sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==} - engines: {node: '>=6'} - - diff-sequences@29.6.3: - resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - electron-to-chromium@1.5.307: resolution: {integrity: sha512-5z3uFKBWjiNR44nFcYdkcXjKMbg5KXNdciu7mhTPo9tB7NbqSNP2sSnGR+fqknZSCwKkBN+oxiiajWs4dT6ORg==} emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - esbuild@0.21.5: - resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} - engines: {node: '>=12'} + es-module-lexer@1.7.0: + resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + + esbuild@0.27.3: + resolution: {integrity: sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==} + engines: {node: '>=18'} hasBin: true escalade@3.2.0: @@ -779,14 +932,23 @@ packages: estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} - execa@8.0.1: - resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} - engines: {node: '>=16.17'} - execa@9.6.1: resolution: {integrity: sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA==} engines: {node: ^18.19.0 || >=20.5.0} + expect-type@1.3.0: + resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} + engines: {node: '>=12.0.0'} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + figures@6.1.0: resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==} engines: {node: '>=18'} @@ -820,13 +982,6 @@ packages: resolution: {integrity: sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==} engines: {node: '>=18'} - get-func-name@2.0.2: - resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} - - get-stream@8.0.1: - resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} - engines: {node: '>=16'} - get-stream@9.0.1: resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==} engines: {node: '>=18'} @@ -853,10 +1008,6 @@ packages: html-escaper@2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} - human-signals@5.0.0: - resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} - engines: {node: '>=16.17.0'} - human-signals@8.0.1: resolution: {integrity: sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==} engines: {node: '>=18.18.0'} @@ -872,10 +1023,6 @@ packages: resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} engines: {node: '>=12'} - is-stream@3.0.0: - resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - is-stream@4.0.1: resolution: {integrity: sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==} engines: {node: '>=18'} @@ -913,9 +1060,6 @@ packages: js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - js-tokens@9.0.1: - resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} - jsesc@3.1.0: resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} engines: {node: '>=6'} @@ -937,17 +1081,10 @@ packages: resolution: {integrity: sha512-nvVPLpIHUxCUoRLrFqTgSxXJ614d8AgQoWl7zPe/2VadE8+1dpU3LBhowRuBAcuwruWtOdD8oYC9jDNJjXDPyA==} engines: {node: '>=0.10.0'} - local-pkg@0.5.1: - resolution: {integrity: sha512-9rrA30MRRP3gBD3HTGnC6cDFpaE1kVDWxWgqWJUN0RvDNAo+Nz/9GxB+nHOH0ifbVFy0hSA1V6vFDvnx54lTEQ==} - engines: {node: '>=14'} - locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} - loupe@2.3.7: - resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} - lru-cache@11.2.6: resolution: {integrity: sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==} engines: {node: 20 || >=22} @@ -968,13 +1105,6 @@ packages: memory-streams@0.1.3: resolution: {integrity: sha512-qVQ/CjkMyMInPaaRMrwWNDvf6boRZXaT/DbQeMYcCWuXPEBf1v8qChOc9OlEVQp2uOvRXa1Qu30fLmKhY6NipA==} - merge-stream@2.0.0: - resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} - - mimic-fn@4.0.0: - resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} - engines: {node: '>=12'} - minimatch@10.2.4: resolution: {integrity: sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==} engines: {node: 18 || 20 || >=22} @@ -983,9 +1113,6 @@ packages: resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} engines: {node: '>=16 || 14 >=14.17'} - mlly@1.8.0: - resolution: {integrity: sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==} - mri@1.1.4: resolution: {integrity: sha512-6y7IjGPm8AzlvoUrwAaw1tLnUBudaS3752vcd8JtrpGGQn+rXIe63LFVHm/YMwtqAuh+LJPCFdlLYPWM1nYn6w==} engines: {node: '>=4'} @@ -1001,10 +1128,6 @@ packages: node-releases@2.0.27: resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} - npm-run-path@5.3.0: - resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - npm-run-path@6.0.0: resolution: {integrity: sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==} engines: {node: '>=18'} @@ -1012,9 +1135,10 @@ packages: obug@2.1.1: resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} - onetime@6.0.0: - resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} - engines: {node: '>=12'} + oxfmt@0.36.0: + resolution: {integrity: sha512-/ejJ+KoSW6J9bcNT9a9UtJSJNWhJ3yOLSBLbkoFHJs/8CZjmaZVZAJe4YgO1KMJlKpNQasrn/G9JQUEZI3p0EQ==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true oxlint@1.51.0: resolution: {integrity: sha512-g6DNPaV9/WI9MoX2XllafxQuxwY1TV++j7hP8fTJByVBuCoVtm3dy9f/2vtH/HU40JztcgWF4G7ua+gkainklQ==} @@ -1030,10 +1154,6 @@ packages: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} - p-limit@5.0.0: - resolution: {integrity: sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==} - engines: {node: '>=18'} - p-locate@5.0.0: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} @@ -1058,23 +1178,18 @@ packages: resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==} engines: {node: 18 || 20 || >=22} - pathe@1.1.2: - resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} - pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} - pathval@1.1.1: - resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} - pend@1.2.0: resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - pkg-types@1.3.1: - resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} postcss@8.5.8: resolution: {integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==} @@ -1084,10 +1199,6 @@ packages: resolution: {integrity: sha512-nODzvTiYVRGRqAOvE84Vk5JDPyyxsVk0/fbA/bq7RqlnhksGpset09XTxbpvLTIjoaF7K8Z8DG8yHtKGTPSYRw==} engines: {node: '>=20'} - pretty-format@29.7.0: - resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - pretty-ms@9.3.0: resolution: {integrity: sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==} engines: {node: '>=18'} @@ -1101,9 +1212,6 @@ packages: engines: {node: '>= 14'} hasBin: true - react-is@18.3.1: - resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} - readable-stream@1.0.34: resolution: {integrity: sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==} @@ -1176,17 +1284,10 @@ packages: resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==} engines: {node: '>=12'} - strip-final-newline@3.0.0: - resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} - engines: {node: '>=12'} - strip-final-newline@4.0.0: resolution: {integrity: sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==} engines: {node: '>=18'} - strip-literal@2.1.1: - resolution: {integrity: sha512-631UJ6O00eNGfMiWG78ck80dfBab8X6IVFB51jZK5Icd7XAs60Z5y7QdSd/wGIklnWvRbUNloVzhOKKmutxQ6Q==} - supports-color@5.5.0: resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} engines: {node: '>=4'} @@ -1202,29 +1303,26 @@ packages: tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} - tinypool@0.8.4: - resolution: {integrity: sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==} - engines: {node: '>=14.0.0'} + tinyexec@1.0.2: + resolution: {integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==} + engines: {node: '>=18'} + + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} + + tinypool@2.1.0: + resolution: {integrity: sha512-Pugqs6M0m7Lv1I7FtxN4aoyToKg1C4tu+/381vH35y8oENM/Ai7f7C4StcoK4/+BSw9ebcS8jRiVrORFKCALLw==} + engines: {node: ^20.0.0 || >=22.0.0} tinyrainbow@3.0.3: resolution: {integrity: sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==} engines: {node: '>=14.0.0'} - tinyspy@2.2.1: - resolution: {integrity: sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==} - engines: {node: '>=14.0.0'} - tunnel@0.0.6: resolution: {integrity: sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==} engines: {node: '>=0.6.11 <=0.7.0 || >=0.7.3'} - type-detect@4.1.0: - resolution: {integrity: sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==} - engines: {node: '>=4'} - - ufo@1.6.3: - resolution: {integrity: sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==} - undici@6.23.0: resolution: {integrity: sha512-VfQPToRA5FZs/qJxLIinmU59u0r7LXqoJkCzinq3ckNJp3vKEh7jTWN589YQ5+aoAC/TGRLyJLCPKcLQbM8r9g==} engines: {node: '>=18.17'} @@ -1251,27 +1349,27 @@ packages: resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==} engines: {node: '>=10.12.0'} - vite-node@1.4.0: - resolution: {integrity: sha512-VZDAseqjrHgNd4Kh8icYHWzTKSCZMhia7GyHfhtzLW33fZlG9SwsB6CEhgyVOWkJfJ2pFLrp/Gj1FSfAiqH9Lw==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true - - vite@5.4.21: - resolution: {integrity: sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==} - engines: {node: ^18.0.0 || >=20.0.0} + vite@7.3.1: + resolution: {integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==} + engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: - '@types/node': ^18.0.0 || >=20.0.0 - less: '*' + '@types/node': ^20.19.0 || >=22.12.0 + jiti: '>=1.21.0' + less: ^4.0.0 lightningcss: ^1.21.0 - sass: '*' - sass-embedded: '*' - stylus: '*' - sugarss: '*' - terser: ^5.4.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 peerDependenciesMeta: '@types/node': optional: true + jiti: + optional: true less: optional: true lightningcss: @@ -1286,24 +1384,37 @@ packages: optional: true terser: optional: true + tsx: + optional: true + yaml: + optional: true - vitest@1.4.0: - resolution: {integrity: sha512-gujzn0g7fmwf83/WzrDTnncZt2UiXP41mHuFYFrdwaLRVQ6JYQEiME2IfEjU3vcFL3VKa75XhI3lFgn+hfVsQw==} - engines: {node: ^18.0.0 || >=20.0.0} + vitest@4.0.18: + resolution: {integrity: sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ==} + engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' - '@types/node': ^18.0.0 || >=20.0.0 - '@vitest/browser': 1.4.0 - '@vitest/ui': 1.4.0 + '@opentelemetry/api': ^1.9.0 + '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 4.0.18 + '@vitest/browser-preview': 4.0.18 + '@vitest/browser-webdriverio': 4.0.18 + '@vitest/ui': 4.0.18 happy-dom: '*' jsdom: '*' peerDependenciesMeta: '@edge-runtime/vm': optional: true + '@opentelemetry/api': + optional: true '@types/node': optional: true - '@vitest/browser': + '@vitest/browser-playwright': + optional: true + '@vitest/browser-preview': + optional: true + '@vitest/browser-webdriverio': optional: true '@vitest/ui': optional: true @@ -1362,10 +1473,6 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} - yocto-queue@1.2.2: - resolution: {integrity: sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==} - engines: {node: '>=12.20'} - yoctocolors@2.1.2: resolution: {integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==} engines: {node: '>=18'} @@ -1490,80 +1597,85 @@ snapshots: '@bcoe/v8-coverage@1.0.2': {} - '@esbuild/aix-ppc64@0.21.5': + '@esbuild/aix-ppc64@0.27.3': optional: true - '@esbuild/android-arm64@0.21.5': + '@esbuild/android-arm64@0.27.3': optional: true - '@esbuild/android-arm@0.21.5': + '@esbuild/android-arm@0.27.3': optional: true - '@esbuild/android-x64@0.21.5': + '@esbuild/android-x64@0.27.3': optional: true - '@esbuild/darwin-arm64@0.21.5': + '@esbuild/darwin-arm64@0.27.3': optional: true - '@esbuild/darwin-x64@0.21.5': + '@esbuild/darwin-x64@0.27.3': optional: true - '@esbuild/freebsd-arm64@0.21.5': + '@esbuild/freebsd-arm64@0.27.3': optional: true - '@esbuild/freebsd-x64@0.21.5': + '@esbuild/freebsd-x64@0.27.3': optional: true - '@esbuild/linux-arm64@0.21.5': + '@esbuild/linux-arm64@0.27.3': optional: true - '@esbuild/linux-arm@0.21.5': + '@esbuild/linux-arm@0.27.3': optional: true - '@esbuild/linux-ia32@0.21.5': + '@esbuild/linux-ia32@0.27.3': optional: true - '@esbuild/linux-loong64@0.21.5': + '@esbuild/linux-loong64@0.27.3': optional: true - '@esbuild/linux-mips64el@0.21.5': + '@esbuild/linux-mips64el@0.27.3': optional: true - '@esbuild/linux-ppc64@0.21.5': + '@esbuild/linux-ppc64@0.27.3': optional: true - '@esbuild/linux-riscv64@0.21.5': + '@esbuild/linux-riscv64@0.27.3': optional: true - '@esbuild/linux-s390x@0.21.5': + '@esbuild/linux-s390x@0.27.3': optional: true - '@esbuild/linux-x64@0.21.5': + '@esbuild/linux-x64@0.27.3': optional: true - '@esbuild/netbsd-x64@0.21.5': + '@esbuild/netbsd-arm64@0.27.3': optional: true - '@esbuild/openbsd-x64@0.21.5': + '@esbuild/netbsd-x64@0.27.3': optional: true - '@esbuild/sunos-x64@0.21.5': + '@esbuild/openbsd-arm64@0.27.3': optional: true - '@esbuild/win32-arm64@0.21.5': + '@esbuild/openbsd-x64@0.27.3': optional: true - '@esbuild/win32-ia32@0.21.5': + '@esbuild/openharmony-arm64@0.27.3': optional: true - '@esbuild/win32-x64@0.21.5': + '@esbuild/sunos-x64@0.27.3': optional: true - '@istanbuljs/schema@0.1.3': {} + '@esbuild/win32-arm64@0.27.3': + optional: true - '@jest/schemas@29.6.3': - dependencies: - '@sinclair/typebox': 0.27.10 + '@esbuild/win32-ia32@0.27.3': + optional: true + + '@esbuild/win32-x64@0.27.3': + optional: true + + '@istanbuljs/schema@0.1.3': {} '@jridgewell/gen-mapping@0.3.13': dependencies: @@ -1584,6 +1696,63 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 + '@oxfmt/binding-android-arm-eabi@0.36.0': + optional: true + + '@oxfmt/binding-android-arm64@0.36.0': + optional: true + + '@oxfmt/binding-darwin-arm64@0.36.0': + optional: true + + '@oxfmt/binding-darwin-x64@0.36.0': + optional: true + + '@oxfmt/binding-freebsd-x64@0.36.0': + optional: true + + '@oxfmt/binding-linux-arm-gnueabihf@0.36.0': + optional: true + + '@oxfmt/binding-linux-arm-musleabihf@0.36.0': + optional: true + + '@oxfmt/binding-linux-arm64-gnu@0.36.0': + optional: true + + '@oxfmt/binding-linux-arm64-musl@0.36.0': + optional: true + + '@oxfmt/binding-linux-ppc64-gnu@0.36.0': + optional: true + + '@oxfmt/binding-linux-riscv64-gnu@0.36.0': + optional: true + + '@oxfmt/binding-linux-riscv64-musl@0.36.0': + optional: true + + '@oxfmt/binding-linux-s390x-gnu@0.36.0': + optional: true + + '@oxfmt/binding-linux-x64-gnu@0.36.0': + optional: true + + '@oxfmt/binding-linux-x64-musl@0.36.0': + optional: true + + '@oxfmt/binding-openharmony-arm64@0.36.0': + optional: true + + '@oxfmt/binding-win32-arm64-msvc@0.36.0': + optional: true + + '@oxfmt/binding-win32-ia32-msvc@0.36.0': + optional: true + + '@oxfmt/binding-win32-x64-msvc@0.36.0': + optional: true + '@oxlint/binding-android-arm-eabi@1.51.0': optional: true @@ -1723,15 +1892,22 @@ snapshots: '@sec-ant/readable-stream@0.4.1': {} - '@sinclair/typebox@0.27.10': {} - '@sindresorhus/merge-streams@4.0.0': {} + '@standard-schema/spec@1.1.0': {} + + '@types/chai@5.2.3': + dependencies: + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 + + '@types/deep-eql@4.0.2': {} + '@types/estree@1.0.8': {} '@types/istanbul-lib-coverage@2.0.6': {} - '@vitest/coverage-istanbul@4.0.18(vitest@1.4.0)': + '@vitest/coverage-istanbul@4.0.18(vitest@4.0.18)': dependencies: '@istanbuljs/schema': 0.1.3 '@jridgewell/gen-mapping': 0.3.13 @@ -1743,46 +1919,50 @@ snapshots: magicast: 0.5.2 obug: 2.1.1 tinyrainbow: 3.0.3 - vitest: 1.4.0 + vitest: 4.0.18 transitivePeerDependencies: - supports-color - '@vitest/expect@1.4.0': + '@vitest/expect@4.0.18': dependencies: - '@vitest/spy': 1.4.0 - '@vitest/utils': 1.4.0 - chai: 4.5.0 + '@standard-schema/spec': 1.1.0 + '@types/chai': 5.2.3 + '@vitest/spy': 4.0.18 + '@vitest/utils': 4.0.18 + chai: 6.2.2 + tinyrainbow: 3.0.3 - '@vitest/runner@1.4.0': + '@vitest/mocker@4.0.18(vite@7.3.1)': dependencies: - '@vitest/utils': 1.4.0 - p-limit: 5.0.0 - pathe: 1.1.2 + '@vitest/spy': 4.0.18 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 7.3.1 - '@vitest/snapshot@1.4.0': + '@vitest/pretty-format@4.0.18': dependencies: - magic-string: 0.30.21 - pathe: 1.1.2 - pretty-format: 29.7.0 + tinyrainbow: 3.0.3 - '@vitest/spy@1.4.0': + '@vitest/runner@4.0.18': dependencies: - tinyspy: 2.2.1 + '@vitest/utils': 4.0.18 + pathe: 2.0.3 - '@vitest/utils@1.4.0': + '@vitest/snapshot@4.0.18': dependencies: - diff-sequences: 29.6.3 - estree-walker: 3.0.3 - loupe: 2.3.7 - pretty-format: 29.7.0 + '@vitest/pretty-format': 4.0.18 + magic-string: 0.30.21 + pathe: 2.0.3 - '@xmldom/xmldom@0.9.8': {} + '@vitest/spy@4.0.18': {} - acorn-walk@8.3.5: + '@vitest/utils@4.0.18': dependencies: - acorn: 8.16.0 + '@vitest/pretty-format': 4.0.18 + tinyrainbow: 3.0.3 - acorn@8.16.0: {} + '@xmldom/xmldom@0.9.8': {} ansi-regex@5.0.1: {} @@ -1796,8 +1976,6 @@ snapshots: dependencies: color-convert: 2.0.1 - ansi-styles@5.2.0: {} - ansi-styles@6.2.3: {} args@5.0.3: @@ -1807,7 +1985,7 @@ snapshots: leven: 2.1.0 mri: 1.1.4 - assertion-error@1.1.0: {} + assertion-error@2.0.1: {} balanced-match@4.0.4: {} @@ -1843,21 +2021,11 @@ snapshots: yargs: 17.7.2 yargs-parser: 21.1.1 - cac@6.7.14: {} - camelcase@5.0.0: {} caniuse-lite@1.0.30001776: {} - chai@4.5.0: - dependencies: - assertion-error: 1.1.0 - check-error: 1.0.3 - deep-eql: 4.1.4 - get-func-name: 2.0.2 - loupe: 2.3.7 - pathval: 1.1.1 - type-detect: 4.1.0 + chai@6.2.2: {} chalk@2.4.2: dependencies: @@ -1867,10 +2035,6 @@ snapshots: chalk@5.6.2: {} - check-error@1.0.3: - dependencies: - get-func-name: 2.0.2 - cliui@8.0.1: dependencies: string-width: 4.2.3 @@ -1891,8 +2055,6 @@ snapshots: commander@14.0.3: {} - confbox@0.1.8: {} - convert-source-map@2.0.0: {} core-util-is@1.0.3: {} @@ -1907,41 +2069,40 @@ snapshots: dependencies: ms: 2.1.3 - deep-eql@4.1.4: - dependencies: - type-detect: 4.1.0 - - diff-sequences@29.6.3: {} - electron-to-chromium@1.5.307: {} emoji-regex@8.0.0: {} - esbuild@0.21.5: + es-module-lexer@1.7.0: {} + + esbuild@0.27.3: optionalDependencies: - '@esbuild/aix-ppc64': 0.21.5 - '@esbuild/android-arm': 0.21.5 - '@esbuild/android-arm64': 0.21.5 - '@esbuild/android-x64': 0.21.5 - '@esbuild/darwin-arm64': 0.21.5 - '@esbuild/darwin-x64': 0.21.5 - '@esbuild/freebsd-arm64': 0.21.5 - '@esbuild/freebsd-x64': 0.21.5 - '@esbuild/linux-arm': 0.21.5 - '@esbuild/linux-arm64': 0.21.5 - '@esbuild/linux-ia32': 0.21.5 - '@esbuild/linux-loong64': 0.21.5 - '@esbuild/linux-mips64el': 0.21.5 - '@esbuild/linux-ppc64': 0.21.5 - '@esbuild/linux-riscv64': 0.21.5 - '@esbuild/linux-s390x': 0.21.5 - '@esbuild/linux-x64': 0.21.5 - '@esbuild/netbsd-x64': 0.21.5 - '@esbuild/openbsd-x64': 0.21.5 - '@esbuild/sunos-x64': 0.21.5 - '@esbuild/win32-arm64': 0.21.5 - '@esbuild/win32-ia32': 0.21.5 - '@esbuild/win32-x64': 0.21.5 + '@esbuild/aix-ppc64': 0.27.3 + '@esbuild/android-arm': 0.27.3 + '@esbuild/android-arm64': 0.27.3 + '@esbuild/android-x64': 0.27.3 + '@esbuild/darwin-arm64': 0.27.3 + '@esbuild/darwin-x64': 0.27.3 + '@esbuild/freebsd-arm64': 0.27.3 + '@esbuild/freebsd-x64': 0.27.3 + '@esbuild/linux-arm': 0.27.3 + '@esbuild/linux-arm64': 0.27.3 + '@esbuild/linux-ia32': 0.27.3 + '@esbuild/linux-loong64': 0.27.3 + '@esbuild/linux-mips64el': 0.27.3 + '@esbuild/linux-ppc64': 0.27.3 + '@esbuild/linux-riscv64': 0.27.3 + '@esbuild/linux-s390x': 0.27.3 + '@esbuild/linux-x64': 0.27.3 + '@esbuild/netbsd-arm64': 0.27.3 + '@esbuild/netbsd-x64': 0.27.3 + '@esbuild/openbsd-arm64': 0.27.3 + '@esbuild/openbsd-x64': 0.27.3 + '@esbuild/openharmony-arm64': 0.27.3 + '@esbuild/sunos-x64': 0.27.3 + '@esbuild/win32-arm64': 0.27.3 + '@esbuild/win32-ia32': 0.27.3 + '@esbuild/win32-x64': 0.27.3 escalade@3.2.0: {} @@ -1953,18 +2114,6 @@ snapshots: dependencies: '@types/estree': 1.0.8 - execa@8.0.1: - dependencies: - cross-spawn: 7.0.6 - get-stream: 8.0.1 - human-signals: 5.0.0 - is-stream: 3.0.0 - merge-stream: 2.0.0 - npm-run-path: 5.3.0 - onetime: 6.0.0 - signal-exit: 4.1.0 - strip-final-newline: 3.0.0 - execa@9.6.1: dependencies: '@sindresorhus/merge-streams': 4.0.0 @@ -1980,6 +2129,12 @@ snapshots: strip-final-newline: 4.0.0 yoctocolors: 2.1.2 + expect-type@1.3.0: {} + + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + figures@6.1.0: dependencies: is-unicode-supported: 2.1.0 @@ -2009,10 +2164,6 @@ snapshots: get-east-asian-width@1.5.0: {} - get-func-name@2.0.2: {} - - get-stream@8.0.1: {} - get-stream@9.0.1: dependencies: '@sec-ant/readable-stream': 0.4.1 @@ -2034,8 +2185,6 @@ snapshots: html-escaper@2.0.2: {} - human-signals@5.0.0: {} - human-signals@8.0.1: {} inherits@2.0.4: {} @@ -2044,8 +2193,6 @@ snapshots: is-plain-obj@4.1.0: {} - is-stream@3.0.0: {} - is-stream@4.0.1: {} is-unicode-supported@2.1.0: {} @@ -2081,8 +2228,6 @@ snapshots: js-tokens@4.0.0: {} - js-tokens@9.0.1: {} - jsesc@3.1.0: {} json5@2.2.3: {} @@ -2097,19 +2242,10 @@ snapshots: leven@2.1.0: {} - local-pkg@0.5.1: - dependencies: - mlly: 1.8.0 - pkg-types: 1.3.1 - locate-path@6.0.0: dependencies: p-locate: 5.0.0 - loupe@2.3.7: - dependencies: - get-func-name: 2.0.2 - lru-cache@11.2.6: {} lru-cache@5.1.1: @@ -2134,23 +2270,12 @@ snapshots: dependencies: readable-stream: 1.0.34 - merge-stream@2.0.0: {} - - mimic-fn@4.0.0: {} - minimatch@10.2.4: dependencies: brace-expansion: 5.0.4 minipass@7.1.3: {} - mlly@1.8.0: - dependencies: - acorn: 8.16.0 - pathe: 2.0.3 - pkg-types: 1.3.1 - ufo: 1.6.3 - mri@1.1.4: {} ms@2.1.3: {} @@ -2159,10 +2284,6 @@ snapshots: node-releases@2.0.27: {} - npm-run-path@5.3.0: - dependencies: - path-key: 4.0.0 - npm-run-path@6.0.0: dependencies: path-key: 4.0.0 @@ -2170,9 +2291,29 @@ snapshots: obug@2.1.1: {} - onetime@6.0.0: + oxfmt@0.36.0: dependencies: - mimic-fn: 4.0.0 + tinypool: 2.1.0 + optionalDependencies: + '@oxfmt/binding-android-arm-eabi': 0.36.0 + '@oxfmt/binding-android-arm64': 0.36.0 + '@oxfmt/binding-darwin-arm64': 0.36.0 + '@oxfmt/binding-darwin-x64': 0.36.0 + '@oxfmt/binding-freebsd-x64': 0.36.0 + '@oxfmt/binding-linux-arm-gnueabihf': 0.36.0 + '@oxfmt/binding-linux-arm-musleabihf': 0.36.0 + '@oxfmt/binding-linux-arm64-gnu': 0.36.0 + '@oxfmt/binding-linux-arm64-musl': 0.36.0 + '@oxfmt/binding-linux-ppc64-gnu': 0.36.0 + '@oxfmt/binding-linux-riscv64-gnu': 0.36.0 + '@oxfmt/binding-linux-riscv64-musl': 0.36.0 + '@oxfmt/binding-linux-s390x-gnu': 0.36.0 + '@oxfmt/binding-linux-x64-gnu': 0.36.0 + '@oxfmt/binding-linux-x64-musl': 0.36.0 + '@oxfmt/binding-openharmony-arm64': 0.36.0 + '@oxfmt/binding-win32-arm64-msvc': 0.36.0 + '@oxfmt/binding-win32-ia32-msvc': 0.36.0 + '@oxfmt/binding-win32-x64-msvc': 0.36.0 oxlint@1.51.0: optionalDependencies: @@ -2200,10 +2341,6 @@ snapshots: dependencies: yocto-queue: 0.1.0 - p-limit@5.0.0: - dependencies: - yocto-queue: 1.2.2 - p-locate@5.0.0: dependencies: p-limit: 3.1.0 @@ -2221,21 +2358,13 @@ snapshots: lru-cache: 11.2.6 minipass: 7.1.3 - pathe@1.1.2: {} - pathe@2.0.3: {} - pathval@1.1.1: {} - pend@1.2.0: {} picocolors@1.1.1: {} - pkg-types@1.3.1: - dependencies: - confbox: 0.1.8 - mlly: 1.8.0 - pathe: 2.0.3 + picomatch@4.0.3: {} postcss@8.5.8: dependencies: @@ -2245,12 +2374,6 @@ snapshots: pretty-bytes@7.1.0: {} - pretty-format@29.7.0: - dependencies: - '@jest/schemas': 29.6.3 - ansi-styles: 5.2.0 - react-is: 18.3.1 - pretty-ms@9.3.0: dependencies: parse-ms: 4.0.0 @@ -2268,8 +2391,6 @@ snapshots: transitivePeerDependencies: - supports-color - react-is@18.3.1: {} - readable-stream@1.0.34: dependencies: core-util-is: 1.0.3 @@ -2357,14 +2478,8 @@ snapshots: dependencies: ansi-regex: 6.2.2 - strip-final-newline@3.0.0: {} - strip-final-newline@4.0.0: {} - strip-literal@2.1.1: - dependencies: - js-tokens: 9.0.1 - supports-color@5.5.0: dependencies: has-flag: 3.0.0 @@ -2381,17 +2496,18 @@ snapshots: tinybench@2.9.0: {} - tinypool@0.8.4: {} + tinyexec@1.0.2: {} - tinyrainbow@3.0.3: {} + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 - tinyspy@2.2.1: {} + tinypool@2.1.0: {} - tunnel@0.0.6: {} - - type-detect@4.1.0: {} + tinyrainbow@3.0.3: {} - ufo@1.6.3: {} + tunnel@0.0.6: {} undici@6.23.0: {} @@ -2413,63 +2529,51 @@ snapshots: '@types/istanbul-lib-coverage': 2.0.6 convert-source-map: 2.0.0 - vite-node@1.4.0: - dependencies: - cac: 6.7.14 - debug: 4.4.3 - pathe: 1.1.2 - picocolors: 1.1.1 - vite: 5.4.21 - transitivePeerDependencies: - - '@types/node' - - less - - lightningcss - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - vite@5.4.21: + vite@7.3.1: dependencies: - esbuild: 0.21.5 + esbuild: 0.27.3 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 postcss: 8.5.8 rollup: 4.59.0 + tinyglobby: 0.2.15 optionalDependencies: fsevents: 2.3.3 - vitest@1.4.0: - dependencies: - '@vitest/expect': 1.4.0 - '@vitest/runner': 1.4.0 - '@vitest/snapshot': 1.4.0 - '@vitest/spy': 1.4.0 - '@vitest/utils': 1.4.0 - acorn-walk: 8.3.5 - chai: 4.5.0 - debug: 4.4.3 - execa: 8.0.1 - local-pkg: 0.5.1 + vitest@4.0.18: + dependencies: + '@vitest/expect': 4.0.18 + '@vitest/mocker': 4.0.18(vite@7.3.1) + '@vitest/pretty-format': 4.0.18 + '@vitest/runner': 4.0.18 + '@vitest/snapshot': 4.0.18 + '@vitest/spy': 4.0.18 + '@vitest/utils': 4.0.18 + es-module-lexer: 1.7.0 + expect-type: 1.3.0 magic-string: 0.30.21 - pathe: 1.1.2 - picocolors: 1.1.1 + obug: 2.1.1 + pathe: 2.0.3 + picomatch: 4.0.3 std-env: 3.10.0 - strip-literal: 2.1.1 tinybench: 2.9.0 - tinypool: 0.8.4 - vite: 5.4.21 - vite-node: 1.4.0 + tinyexec: 1.0.2 + tinyglobby: 0.2.15 + tinyrainbow: 3.0.3 + vite: 7.3.1 why-is-node-running: 2.3.0 transitivePeerDependencies: + - jiti - less - lightningcss + - msw - sass - sass-embedded - stylus - sugarss - - supports-color - terser + - tsx + - yaml which@2.0.2: dependencies: @@ -2521,6 +2625,4 @@ snapshots: yocto-queue@0.1.0: {} - yocto-queue@1.2.2: {} - yoctocolors@2.1.2: {} diff --git a/scripts/test.js b/scripts/test.js index 2541fde45..162985843 100644 --- a/scripts/test.js +++ b/scripts/test.js @@ -7,7 +7,7 @@ let only = false; const argv = process.argv .slice(2) - .map(arg => { + .map((arg) => { if (arg === '--coverage') { cover = true; } else if (arg === '--only') { @@ -21,20 +21,15 @@ const argv = process.argv const args = []; if (cover) { - args.push( - join('node_modules', 'c8', 'bin', 'c8.js'), - process.execPath - ); + args.push(join('node_modules', 'c8', 'bin', 'c8.js'), process.execPath); } -let tests = await glob([ - './test/**/*.test.js' -]); +let tests = await glob(['./test/**/*.test.js']); tests.sort(); if (argv.length) { - tests = tests.filter(file => { + tests = tests.filter((file) => { const filename = basename(file); - return argv.some(filter => filename.includes(filter)); + return argv.some((filter) => filename.includes(filter)); }); } @@ -54,13 +49,9 @@ args.push( console.log(`\n> ${process.execPath} ${args.join(' ')}\n\n`); -await execa( - process.execPath, - args, - { - env: { - TI_CLI_SKIP_ENV_PATHS: 1 - }, - stdio: 'inherit' - } -); +await execa(process.execPath, args, { + env: { + TI_CLI_SKIP_ENV_PATHS: 1, + }, + stdio: 'inherit', +}); diff --git a/src/cli.js b/src/cli.js index cb793a7d4..942884716 100644 --- a/src/cli.js +++ b/src/cli.js @@ -1,22 +1,21 @@ -import chalk from 'chalk'; -import fs from 'fs-extra'; -import { program, Command, Option } from 'commander'; -import { basename, dirname, join } from 'node:path'; -import { fileURLToPath, pathToFileURL } from 'node:url'; -import { unique } from './util/unique.js'; -import { ticonfig } from './util/ticonfig.js'; -import { initSDK, typeLabels } from './util/tisdk.js'; -import { expand } from './util/expand.js'; +import { applyCommandConfig } from './util/apply-command-config.js'; import { arrayify } from './util/arrayify.js'; -import * as version from './util/version.js'; import { Logger } from './util/logger.js'; -import { capitalize } from './util/capitalize.js'; -import wrapAnsi from 'wrap-ansi'; -import { TiError } from './util/tierror.js'; import { prompt } from './util/prompt.js'; -import { applyCommandConfig } from './util/apply-command-config.js'; +import { ticonfig } from './util/ticonfig.js'; +import { TiError } from './util/tierror.js'; import { TiHelp } from './util/tihelp.js'; +import { initSDK, typeLabels } from './util/tisdk.js'; +import { unique } from './util/unique.js'; +import chalk from 'chalk'; +import { program, Command, Option } from 'commander'; +import { capitalize, expand, isDir, version } from 'node-titanium-sdk/util'; +import { readdirSync, readFileSync } from 'node:fs'; +import { readdir } from 'node:fs/promises'; +import { basename, dirname, join } from 'node:path'; +import { fileURLToPath, pathToFileURL } from 'node:url'; import semver from 'semver'; +import wrapAnsi from 'wrap-ansi'; const { blue, bold, cyan, gray, green, magenta, red, yellow } = chalk; @@ -25,18 +24,18 @@ const { blue, bold, cyan, gray, green, magenta, red, yellow } = chalk; * Titanium SDK. These are hard coded for speed. */ const commands = { - config: 'get and set config options', - info: 'display development environment information', - module: 'displays installed Titanium modules', - sdk: 'manages installed Titanium SDKs', - setup: 'sets up the Titanium CLI' + config: 'get and set config options', + info: 'display development environment information', + module: 'displays installed Titanium modules', + sdk: 'manages installed Titanium SDKs', + setup: 'sets up the Titanium CLI', }; const sdkCommands = { - build: 'builds a project', - clean: 'removes previous build directories', - create: 'creates a new project', - project: 'get and set tiapp.xml settings' + build: 'builds a project', + clean: 'removes previous build directories', + create: 'creates a new project', + project: 'get and set tiapp.xml settings', }; /** @@ -75,7 +74,7 @@ export class CLI { _: [], // parsed arguments (reset each time the context's parse() is called) $: 'titanium', // resolved node script path $_: process.argv.slice(), // original arguments - $0: process.argv.slice(0, 2).join(' ') // node process and original node script path + $0: process.argv.slice(0, 2).join(' '), // node process and original node script path }; /** @@ -105,7 +104,7 @@ export class CLI { os: { name: process.platform === 'darwin' ? 'osx' : process.platform, sdkPaths: [], - sdks: {} + sdks: {}, }, getOSInfo: async (callback) => { const { detect } = await import('./util/detect.js'); @@ -120,10 +119,10 @@ export class CLI { oscpu: os.numcpus, memory: os.memory, node: node.version, - npm: npm.version + npm: npm.version, }); } - } + }, }; /** @@ -138,7 +137,7 @@ export class CLI { loadedFilenames: [], post: {}, pre: {}, - scannedPaths: {} + scannedPaths: {}, }; /** @@ -168,7 +167,7 @@ export class CLI { constructor() { const pkgJsonFile = join(dirname(fileURLToPath(import.meta.url)), '../package.json'); - const { engines, version } = fs.readJsonSync(pkgJsonFile); + const { engines, version } = JSON.parse(readFileSync(pkgJsonFile, 'utf8')); this.name = 'Titanium Command-Line Interface'; this.copyright = 'Copyright TiDev, Inc. 4/7/2022-Present. All Rights Reserved.'; @@ -177,7 +176,7 @@ export class CLI { this.logger.setBanner({ name: this.name, copyright: this.copyright, - version: this.version + version: this.version, }); this.debugLogger.timestampEnabled(true); @@ -228,7 +227,9 @@ export class CLI { if (opt.negate) { name = name.replace(/^no-/, ''); } - this.debugLogger.trace(` Setting ${name} = ${cargv[opt.attributeName()]} (prev: ${argv[name]})`); + this.debugLogger.trace( + ` Setting ${name} = ${cargv[opt.attributeName()]} (prev: ${argv[name]})` + ); argv[name] = cargv[opt.attributeName()]; } } @@ -251,7 +252,9 @@ export class CLI { applyCommandConfig(this, cmdName, cmd, conf); if (conf.platforms) { - this.debugLogger.trace(`Detected conf.platforms applying config for "${cmd.name()}", overriding createHelp()`); + this.debugLogger.trace( + `Detected conf.platforms applying config for "${cmd.name()}", overriding createHelp()` + ); this.command.createHelp = () => { return Object.assign(new TiHelp(this, conf.platforms), this.command.configureHelp()); }; @@ -287,70 +290,74 @@ export class CLI { type: name, args, fn, - ctx + ctx, }); const callback = data.args.pop(); const pres = this.hooks.pre[name] || []; const posts = this.hooks.post[name] || []; - this.debugLogger.trace(`Firing ${hookType} hook "${name}" pres=${pres.length}, posts=${posts.length}`); + this.debugLogger.trace( + `Firing ${hookType} hook "${name}" pres=${pres.length}, posts=${posts.length}` + ); (async () => { // call all pre filters - await pres - .reduce((promise, pre) => promise.then(async () => { - if (pre.length >= 2) { - await new Promise((resolve, reject) => { - pre.call(ctx, data, (err, newData) => { - if (err) { - return reject(err); - } - if (newData && typeof newData === 'object' && newData.type) { - data = newData; - } - resolve(); + await pres.reduce( + (promise, pre) => + promise.then(async () => { + if (pre.length >= 2) { + await new Promise((resolve, reject) => { + pre.call(ctx, data, (err, newData) => { + if (err) { + return reject(err); + } + if (newData && typeof newData === 'object' && newData.type) { + data = newData; + } + resolve(); + }); }); - }); - } else { - await pre.call(ctx, data); - } - }), Promise.resolve()); + } else { + await pre.call(ctx, data); + } + }), + Promise.resolve() + ); if (data.fn) { - data.result = await new Promise(resolve => { + data.result = await new Promise((resolve) => { // call the hooked function - data.fn.call( - data.ctx, - ...data.args, - (...args) => resolve(args) - ); + data.fn.call(data.ctx, ...data.args, (...args) => resolve(args)); }); } // call all post filters - await posts - .reduce((promise, post) => promise.then(async () => { - if (post.length >= 2) { - await new Promise((resolve, reject) => { - post.call(ctx, data, (err, newData) => { - if (err) { - return reject(err); - } - if (newData && typeof newData === 'object' && newData.type) { - data = newData; - } - resolve(); + await posts.reduce( + (promise, post) => + promise.then(async () => { + if (post.length >= 2) { + await new Promise((resolve, reject) => { + post.call(ctx, data, (err, newData) => { + if (err) { + return reject(err); + } + if (newData && typeof newData === 'object' && newData.type) { + data = newData; + } + resolve(); + }); }); - }); - } else { - await post.call(ctx, data); - } - }), Promise.resolve()); + } else { + await post.call(ctx, data); + } + }), + Promise.resolve() + ); if (typeof callback === 'function') { callback.apply(data, data.result); } - })().catch(err => { + })().catch((err) => { // this is the primary error handler if (typeof callback === 'function') { callback(err); @@ -383,21 +390,29 @@ export class CLI { // create each hook and immediately fire them const events = unique(arrayify(name, true)); - this.debugLogger.trace(`Emitting "${name}" (${events.length} listener${events.length !== 1 ? 's' : ''})`); - - const promise = events - .reduce((promise, name) => promise.then(() => new Promise((resolve, reject) => { - const hook = this.createHook(name, data); - hook((err, result) => { - err ? reject(err) : resolve(result); - }); - })), Promise.resolve(this)); + this.debugLogger.trace( + `Emitting "${name}" (${events.length} listener${events.length !== 1 ? 's' : ''})` + ); + + const promise = events.reduce( + (promise, name) => + promise.then( + () => + new Promise((resolve, reject) => { + const hook = this.createHook(name, data); + hook((err, result) => { + err ? reject(err) : resolve(result); + }); + }) + ), + Promise.resolve(this) + ); if (typeof callback !== 'function') { return promise; } - promise.then(result => callback(null, result), callback); + promise.then((result) => callback(null, result), callback); return this; } @@ -436,17 +451,63 @@ export class CLI { if (sdkCommands[commandName] || commandName === 'info') { // the SDK still uses the `colors` package, so we need to add the // colors to the string prototype - const assignColors = proto => Object.defineProperties(proto, { - blue: { get() { return blue(`${this}`); }, configurable: true }, - bold: { get() { return bold(`${this}`); }, configurable: true }, - cyan: { get() { return cyan(`${this}`); }, configurable: true }, - gray: { get() { return gray(`${this}`); }, configurable: true }, - green: { get() { return green(`${this}`); }, configurable: true }, - grey: { get() { return gray(`${this}`); }, configurable: true }, - magenta: { get() { return magenta(`${this}`); }, configurable: true }, - red: { get() { return red(`${this}`); }, configurable: true }, - yellow: { get() { return yellow(`${this}`); }, configurable: true } - }); + const assignColors = (proto) => + Object.defineProperties(proto, { + blue: { + get() { + return blue(`${this}`); + }, + configurable: true, + }, + bold: { + get() { + return bold(`${this}`); + }, + configurable: true, + }, + cyan: { + get() { + return cyan(`${this}`); + }, + configurable: true, + }, + gray: { + get() { + return gray(`${this}`); + }, + configurable: true, + }, + green: { + get() { + return green(`${this}`); + }, + configurable: true, + }, + grey: { + get() { + return gray(`${this}`); + }, + configurable: true, + }, + magenta: { + get() { + return magenta(`${this}`); + }, + configurable: true, + }, + red: { + get() { + return red(`${this}`); + }, + configurable: true, + }, + yellow: { + get() { + return yellow(`${this}`); + }, + configurable: true, + }, + }); assignColors(String.prototype); assignColors(Number.prototype); @@ -522,7 +583,9 @@ export class CLI { */ async go() { if (this.nodeVersion && !semver.satisfies(process.version, this.nodeVersion)) { - throw new TiError(`Node.js version ${this.nodeVersion} required, current version is ${process.version}`); + throw new TiError( + `Node.js version ${this.nodeVersion} required, current version is ${process.version}` + ); } Command.prototype.createHelp = () => { @@ -550,8 +613,8 @@ export class CLI { // wire up the event listeners program .on('option:no-banner', () => this.logger.bannerEnabled(false)) - .on('option:no-color', () => chalk.level = 0) - .on('option:no-colors', () => chalk.level = 0) + .on('option:no-color', () => (chalk.level = 0)) + .on('option:no-colors', () => (chalk.level = 0)) .on('option:quiet', () => this.debugLogger.silence()) .on('option:version', () => { this.logger.log(this.version); @@ -583,7 +646,8 @@ export class CLI { if (!this.promptingEnabled || !option.prompt || !option.values) { throw new TiError(`Missing required option "--${name}"`, { - after: option.values && `Allowed values:\n${option.values.map(v => ` ${cyan(v)}`)}` + after: + option.values && `Allowed values:\n${option.values.map((v) => ` ${cyan(v)}`)}`, }); } @@ -596,7 +660,7 @@ export class CLI { Object.assign(conf.options, src?.options); applyCommandConfig(this, cmdName, this.command, { flags: src?.flags, - options: src?.options + options: src?.options, }); } } @@ -605,7 +669,7 @@ export class CLI { if (conf.options) { for (const name of Object.keys(conf.options)) { if (!Object.hasOwn(this.argv, name) && conf.options[name].default !== undefined) { - cmd.setOptionValue(name, this.argv[name] = conf.options[name].default); + cmd.setOptionValue(name, (this.argv[name] = conf.options[name].default)); } } } @@ -650,20 +714,23 @@ export class CLI { this.argv.platform = 'ios'; } - this.debugLogger.trace(`Processing --platform option: ${this.argv.platform || 'not specified'}`); + this.debugLogger.trace( + `Processing --platform option: ${this.argv.platform || 'not specified'}` + ); try { if (!this.argv.platform) { throw new TiError('Missing required option: --platform ', { - after: `Available Platforms:\n${ - platformOption.values - .map(v => ` ${cyan(v)}`) - .join('\n') - }` + after: `Available Platforms:\n${platformOption.values + .map((v) => ` ${cyan(v)}`) + .join('\n')}`, }); } else if (!platformOption.values.includes(this.argv.platform)) { - throw new TiError(`Invalid platform "${this.argv.$originalPlatform || this.argv.platform}"`, { - code: 'INVALID_PLATFORM' - }); + throw new TiError( + `Invalid platform "${this.argv.$originalPlatform || this.argv.platform}"`, + { + code: 'INVALID_PLATFORM', + } + ); } } catch (e) { if (!this.promptingEnabled) { @@ -680,7 +747,7 @@ export class CLI { this.argv.platform = await prompt({ type: 'select', message: 'Please select a valid platform:', - choices: platformOption.values.map(v => ({ label: v, value: v })) + choices: platformOption.values.map((v) => ({ label: v, value: v })), }); this.debugLogger.trace(`Selecting platform "${this.argv.platform}"`); } else { @@ -697,7 +764,7 @@ export class CLI { // set platform context this.command.platform = { - conf: platformConf + conf: platformConf, }; this.debugLogger.trace('Applying platform config...'); @@ -727,42 +794,32 @@ export class CLI { .configureHelp({ helpWidth: ticonfig.get('cli.width', 80), showGlobalOptions: true, - sortSubcommands: true + sortSubcommands: true, }) .configureOutput({ outputError(msg) { throw new TiError(msg.replace(/^error:\s*/, '')); - } + }, }) .option('--no-banner', 'disable Titanium version banner') .addOption( // completely ignored, we just need the parser to not choke - new Option('--color') - .hideHelp() - ) - .addOption( - new Option('--colors') - .hideHelp() + new Option('--color').hideHelp() ) + .addOption(new Option('--colors').hideHelp()) .option('--no-color', 'disable colors') - .addOption( - new Option('--no-colors') - .hideHelp() - ) + .addOption(new Option('--no-colors').hideHelp()) .option('--no-progress-bars', 'disable progress bars') .option('--no-prompt', 'disable interactive prompting') .option('--config [json]', 'serialized JSON string to mix into the CLI config') .option('--config-file [file]', 'path to CLI config file') .option('--debug', 'display CLI debug log messages') - .addOption( - new Option('--debug-no-run') - .hideHelp() - ) + .addOption(new Option('--debug-no-run').hideHelp()) .option('-d, --project-dir ', 'the directory containing the project') .option('-q, --quiet', 'suppress all output') .option('-v, --version', 'displays the current version') .option('-s, --sdk [version]', `Titanium SDK version to use ${gray('(default: "latest")')}`) - .on('option:config', cfg => { + .on('option:config', (cfg) => { try { const json = (0, eval)(`(${cfg})`); this.debugLogger.trace('Applying --config:', json); @@ -775,16 +832,13 @@ export class CLI { throw new Error(`Failed to parse --config: ${e.message}`); } }) - .on('option:config-file', file => { + .on('option:config-file', (file) => { this.config.load(file); this.loadCustomCommands(); }) - .on('option:project-dir', dir => program.setOptionValue('projectDir', expand(dir))); + .on('option:project-dir', (dir) => program.setOptionValue('projectDir', expand(dir))); - const allCommands = [ - ...Object.entries(commands), - ...Object.entries(sdkCommands) - ]; + const allCommands = [...Object.entries(commands), ...Object.entries(sdkCommands)]; for (const [name, summary] of allCommands) { program .command(name) @@ -807,7 +861,8 @@ export class CLI { this.command = cmd; this.applyArgv(program); - this.promptingEnabled = this.argv.prompt && !this.argv.$_.includes('-h') && !this.argv.$_.includes('--help'); + this.promptingEnabled = + this.argv.prompt && !this.argv.$_.includes('-h') && !this.argv.$_.includes('--help'); // if `--project-dir` was not set, default to the current working directory const cwd = expand(this.argv['project-dir'] || '.'); @@ -829,7 +884,7 @@ export class CLI { const hooks = ticonfig.paths?.hooks; if (hooks) { const paths = arrayify(hooks, true); - await Promise.all(paths.map(p => this.scanHooks(p))); + await Promise.all(paths.map((p) => this.scanHooks(p))); } await this.loadCommand(cmd); @@ -864,7 +919,7 @@ export class CLI { // that changes the available flags/options const skipRegExp = /^(flags|options|args|subcommands)$/; const optionBranches = Object.keys(conf) - .filter(name => conf.options?.[name] && !skipRegExp.test(name)) + .filter((name) => conf.options?.[name] && !skipRegExp.test(name)) .sort((a, b) => { // if we have multiple option groups, then try to process them in order if (!conf.options[a]?.order) { @@ -892,7 +947,7 @@ export class CLI { Object.assign(conf.options, src?.options); applyCommandConfig(this, cmdName, this.command, { flags: src?.flags, - options: src?.options + options: src?.options, }); optionBranches.splice(i--, 1); } @@ -917,7 +972,9 @@ export class CLI { if (commands[cmdName]) { desc = commands[cmdName]; - commandFile = pathToFileURL(join(fileURLToPath(import.meta.url), `../commands/${cmdName}.js`)); + commandFile = pathToFileURL( + join(fileURLToPath(import.meta.url), `../commands/${cmdName}.js`) + ); } else if (sdkCommands[cmdName]) { desc = sdkCommands[cmdName]; commandFile = pathToFileURL(join(this.sdk.path, `cli/commands/${cmdName}.js`)); @@ -935,7 +992,7 @@ export class CLI { // check if this command is compatible with this version of the CLI if (cmd.module.cliVersion && !version.satisfies(this.version, cmd.module.cliVersion)) { throw new TiError(`Command "${cmdName}" incompatible with this version of the CLI`, { - after: `Requires version ${cmd.module.cliVersion}, currently ${this.version}` + after: `Requires version ${cmd.module.cliVersion}, currently ${this.version}`, }); } @@ -950,9 +1007,8 @@ export class CLI { // load the command's config if (typeof cmd.module.config === 'function') { const fn = await cmd.module.config(this.logger, this.config, this); - const conf = (typeof fn === 'function' - ? await new Promise(resolve => fn(resolve)) - : fn) || {}; + const conf = + (typeof fn === 'function' ? await new Promise((resolve) => fn(resolve)) : fn) || {}; cmd.conf = conf; @@ -965,7 +1021,7 @@ export class CLI { // if `--alloy` is not defined, define it if (!conf.flags.alloy) { conf.flags.alloy = { - desc: 'initialize new project as an Alloy project' + desc: 'initialize new project as an Alloy project', }; } } @@ -979,7 +1035,9 @@ export class CLI { delete conf.platforms.iphone; } - this.debugLogger.trace(`Detected conf.platforms loading "${cmdName}", overriding createHelp()`); + this.debugLogger.trace( + `Detected conf.platforms loading "${cmdName}", overriding createHelp()` + ); this.command.createHelp = () => { return Object.assign(new TiHelp(this, conf.platforms), this.command.configureHelp()); }; @@ -1009,11 +1067,11 @@ export class CLI { try { p = expand(p); - const isDir = fs.statSync(p).isDirectory(); - const files = isDir ? fs.readdirSync(p) : [p]; + const isDir = isDir(p); + const files = isDir ? readdirSync(p) : [p]; for (const filename of files) { const file = isDir ? join(p, filename) : filename; - if (!ignoreRE.test(filename) && fs.existsSync(file) && (fs.statSync(file)).isFile() && jsRE.test(file)) { + if (!ignoreRE.test(filename) && isFile(file) && jsRE.test(file)) { const name = basename(file).replace(jsRE, ''); this.debugLogger.trace(`Found custom command "${name}"`); this.customCommands[name] = file; @@ -1048,37 +1106,35 @@ export class CLI { // including the Titanium SDK version let showSDKPrompt = false; const createOpts = ['-p', '--platforms', '-n', '--name', '-t', '--type']; - if (cmdName === 'create' && !this.argv.sdk && !createOpts.some(f => this.argv.$_.includes(f))) { + if ( + cmdName === 'create' && + !this.argv.sdk && + !createOpts.some((f) => this.argv.$_.includes(f)) + ) { showSDKPrompt = true; } // load the SDK and its hooks - const { - installPath, - sdk, - sdkPaths, - sdks, - tiappSdkVersion - } = await initSDK({ + const { installPath, sdk, sdkPaths, sdks, tiappSdkVersion } = await initSDK({ config: this.config, cwd, debugLogger: this.debugLogger, logger: this.logger, promptingEnabled: this.promptingEnabled, selectedSdk: this.argv.sdk, - showSDKPrompt + showSDKPrompt, }); this.env.installPath = installPath; this.env.os.sdkPaths = sdkPaths; this.env.sdks = sdks; - this.env.getSDK = version => { + this.env.getSDK = (version) => { if (!version || version === 'latest') { return sdk; } const values = Object.values(sdks); - return values.find(s => s.name === version) || - values.find(s => s.version === version) || - null; + return ( + values.find((s) => s.name === version) || values.find((s) => s.version === version) || null + ); }; this.sdk = sdk; this.argv.sdk = sdk?.name; @@ -1087,19 +1143,24 @@ export class CLI { const hasSDKs = Object.keys(sdks).length > 0; if (!hasSDKs || !sdk) { if (hasSDKs && tiappSdkVersion) { - throw new TiError(`The in the tiapp.xml is set to "${tiappSdkVersion}", but this version is not installed`, { - after: `Available SDKs:\n${Object.values(sdks).map(sdk => ` ${cyan(sdk.name.padEnd(24))} ${gray(typeLabels[sdk.type])}`).join('\n')}` - }); + throw new TiError( + `The in the tiapp.xml is set to "${tiappSdkVersion}", but this version is not installed`, + { + after: `Available SDKs:\n${Object.values(sdks) + .map((sdk) => ` ${cyan(sdk.name.padEnd(24))} ${gray(typeLabels[sdk.type])}`) + .join('\n')}`, + } + ); } throw new TiError('No Titanium SDKs found', { - after: `You can download the latest Titanium SDK by running: ${cyan('titanium sdk install')}` + after: `You can download the latest Titanium SDK by running: ${cyan('titanium sdk install')}`, }); } try { // check if the SDK is compatible with our version of node - sdk.packageJson = await fs.readJson(join(sdk.path, 'package.json')); + sdk.packageJson = JSON.parse(await readFile(join(sdk.path, 'package.json'), 'utf8')); const current = process.versions.node; const required = sdk.packageJson.vendorDependencies.node; @@ -1107,7 +1168,7 @@ export class CLI { if (supported === false) { throw new TiError(`Titanium SDK v${sdk.name} is incompatible with Node.js v${current}`, { - after: `Please install Node.js ${version.parseMax(required)} in order to use this version of the Titanium SDK.` + after: `Please install Node.js ${version.parseMax(required)} in order to use this version of the Titanium SDK.`, }); } } catch { @@ -1120,7 +1181,7 @@ export class CLI { name: this.name, copyright: this.copyright, version: this.version, - sdkVersion: this.sdk?.name + sdkVersion: this.sdk?.name, }); // if we're running a `sdk` command, then scan the SDK for hooks @@ -1180,7 +1241,7 @@ export class CLI { if (typeof opt.prompt === 'function') { // option has it's own prompt handler which probably uses `fields` - const field = await new Promise(resolve => opt.prompt(resolve)); + const field = await new Promise((resolve) => opt.prompt(resolve)); if (!field) { return; } @@ -1189,7 +1250,7 @@ export class CLI { field.autoSelectOne = false; } - value = await new Promise(resolve => { + value = await new Promise((resolve) => { field.prompt((err, value) => { if (err) { process.exit(1); @@ -1209,28 +1270,30 @@ export class CLI { def = def.join(','); } - const validate = pr.validate || (value => { - if (pr.validator) { - try { - pr.validator(value); - } catch (ex) { - return ex.toString(); + const validate = + pr.validate || + ((value) => { + if (pr.validator) { + try { + pr.validator(value); + } catch (ex) { + return ex.toString(); + } + } else if (!value.length || (pr.pattern && !pr.pattern.test(value))) { + return pr.error; } - } else if (!value.length || (pr.pattern && !pr.pattern.test(value))) { - return pr.error; - } - return true; - }); + return true; + }); if (Array.isArray(opt.values)) { if (opt.values.length > 1) { - const choices = opt.values.map(v => ({ label: v, value: v })); + const choices = opt.values.map((v) => ({ label: v, value: v })); value = await prompt({ type: 'select', message: p, - initial: def && choices.find(c => c.value === def) || undefined, + initial: (def && choices.find((c) => c.value === def)) || undefined, validate, - choices + choices, }); } else { value = opt.values[0]; @@ -1240,7 +1303,7 @@ export class CLI { type: opt.password ? 'password' : 'text', message: p, initial: def || undefined, - validate + validate, }); } @@ -1252,7 +1315,7 @@ export class CLI { this.debugLogger.trace(`Selected value = ${value}`); this.command.setOptionValue(opt.name, value); - return this.argv[opt.name] = value; + return (this.argv[opt.name] = value); } /** @@ -1268,7 +1331,7 @@ export class CLI { ctx._lifeCycleHooks = {}; ctx._savedState = null; - const optionEvents = ctx.eventNames().filter(name => name.startsWith('option:')); + const optionEvents = ctx.eventNames().filter((name) => name.startsWith('option:')); for (const name of optionEvents) { ctx.removeAllListeners(name); } @@ -1295,12 +1358,12 @@ export class CLI { try { const jsfile = /\.js$/; const ignore = /^[._]/; - const files = fs.statSync(dir).isDirectory() ? fs.readdirSync(dir).map(n => join(dir, n)) : [dir]; + const files = isDir(dir) ? (await readdir(dir)).map((n) => join(dir, n)) : [dir]; let appc; for (const file of files) { try { - if (fs.statSync(file).isFile() && jsfile.test(file) && !ignore.test(basename(dirname(file)))) { + if (isFile(file) && jsfile.test(file) && !ignore.test(basename(dirname(file)))) { const startTime = Date.now(); const mod = await import(pathToFileURL(file)); if (mod.id) { @@ -1309,7 +1372,7 @@ export class CLI { } this.hooks.ids[mod.id].push({ file: file, - version: mod.version || null + version: mod.version || null, }); // don't load duplicate ids @@ -1318,10 +1381,17 @@ export class CLI { } } - if (this.sdk && (!this.version || !mod.cliVersion || version.satisfies(this.version, mod.cliVersion))) { + if ( + this.sdk && + (!this.version || !mod.cliVersion || version.satisfies(this.version, mod.cliVersion)) + ) { if (!appc) { - const nodeAppc = pathToFileURL(join(this.sdk.path, 'node_modules', 'node-appc', 'index.js')); - this.debugLogger.trace(`Importing: ${join(this.sdk.path, 'node_modules', 'node-appc', 'index.js')}`); + const nodeAppc = pathToFileURL( + join(this.sdk.path, 'node_modules', 'node-appc', 'index.js') + ); + this.debugLogger.trace( + `Importing: ${join(this.sdk.path, 'node_modules', 'node-appc', 'index.js')}` + ); appc = (await import(nodeAppc)).default; } if (typeof mod.init === 'function') { @@ -1416,8 +1486,8 @@ export class CLI { this.debugLogger.trace(`--${name} required, but undefined`); if (typeof opt.verifyIfRequired === 'function') { - await new Promise(resolve => { - opt.verifyIfRequired(stillRequired => { + await new Promise((resolve) => { + opt.verifyIfRequired((stillRequired) => { if (stillRequired) { missing[name] = obj; missingCount++; @@ -1430,12 +1500,16 @@ export class CLI { missing[name] = obj; missingCount++; } - } else if (Array.isArray(opt.values) && !opt.skipValueCheck && opt.values.indexOf(this.argv[name]) === -1) { + } else if ( + Array.isArray(opt.values) && + !opt.skipValueCheck && + opt.values.indexOf(this.argv[name]) === -1 + ) { invalid[name] = obj; invalidCount++; } else if (!opt.validated && typeof opt.validate === 'function') { try { - await new Promise(resolve => { + await new Promise((resolve) => { opt.validate(this.argv[name], (err, value) => { if (err) { obj._err = err; @@ -1531,7 +1605,7 @@ export class CLI { if (!opt.prompt) { // option doesn't have a prompt, so let's make a generic one - opt.prompt = async callback => { + opt.prompt = async (callback) => { // if the option has values, then display a pretty list if (Array.isArray(opt.values)) { return callback({ @@ -1539,10 +1613,10 @@ export class CLI { const value = await prompt({ type: 'select', message: `Please select a valid ${cyan(name)} value:`, - choices: opt.values.map(v => ({ label: v, value: v })) + choices: opt.values.map((v) => ({ label: v, value: v })), }); callback(null, value); - } + }, }); } @@ -1552,25 +1626,27 @@ export class CLI { const value = await prompt({ type: opt.password ? 'password' : 'text', message: `Please enter a valid ${cyan(name)}`, - validate: opt.validate || (value => { - if (pr.validator) { - try { - pr.validator(value); - } catch (ex) { - return ex.toString(); + validate: + opt.validate || + ((value) => { + if (pr.validator) { + try { + pr.validator(value); + } catch (ex) { + return ex.toString(); + } + } else if (!value.length || (pr.pattern && !pr.pattern.test(value))) { + return pr.error; } - } else if (!value.length || (pr.pattern && !pr.pattern.test(value))) { - return pr.error; - } - return true; - }) + return true; + }), }); if (value === undefined) { // sigint process.exit(0); } callback(null, value); - } + }, }); }; } @@ -1613,7 +1689,7 @@ export class CLI { // fn should always be a function for `build` and `clean` commands if (typeof result === 'function') { - await new Promise(resolve => result(resolve)); + await new Promise((resolve) => result(resolve)); } else if (result === false) { this.command.skipRun = true; } @@ -1653,36 +1729,42 @@ export class CLI { */ validateHooks() { if (this.hooks.incompatibleFilenames.length) { - this.logger.warn(`Incompatible plugin hooks:\n${ - this.hooks.incompatibleFilenames.map(file => ` ${file}`).join('\n') - }\n`); + this.logger.warn( + `Incompatible plugin hooks:\n${this.hooks.incompatibleFilenames + .map((file) => ` ${file}`) + .join('\n')}\n` + ); } if (Object.keys(this.hooks.errors).length) { - this.logger.warn(`Bad plugin hooks that failed to load:\n${ - Object.values(this.hooks.errors) - .map(e => (e.stack || e.toString()) - .trim() - .split('\n') - .map(line => ` ${line}`) - .join('\n') + this.logger.warn( + `Bad plugin hooks that failed to load:\n${Object.values(this.hooks.errors) + .map((e) => + (e.stack || e.toString()) + .trim() + .split('\n') + .map((line) => ` ${line}`) + .join('\n') ) - .join('\n') - }\n`); + .join('\n')}\n` + ); } - if (Object.keys(this.hooks.ids).some(id => this.hooks.ids[id].length > 1)) { - this.logger.warn(`Conflicting plugins that were not loaded:\n${ - Object.entries(this.hooks.ids) - .map(([id, conflicting]) => ` Hook ID: ${cyan(id)}\n${ - conflicting.map((c, i) => { - return i === 0 - ? ` Loaded: ${c.file} ${c.version ? `(version ${version})` : ''}` - : ` Didn't load: ${c.file} ${c.version ? `(version ${version})` : ''}`; - }).join('\n') - }`) - .join('\n') - }\n`); + if (Object.keys(this.hooks.ids).some((id) => this.hooks.ids[id].length > 1)) { + this.logger.warn( + `Conflicting plugins that were not loaded:\n${Object.entries(this.hooks.ids) + .map( + ([id, conflicting]) => + ` Hook ID: ${cyan(id)}\n${conflicting + .map((c, i) => { + return i === 0 + ? ` Loaded: ${c.file} ${c.version ? `(version ${version})` : ''}` + : ` Didn't load: ${c.file} ${c.version ? `(version ${version})` : ''}`; + }) + .join('\n')}` + ) + .join('\n')}\n` + ); } } } diff --git a/src/commands/config.js b/src/commands/config.js index 54f98e9a2..3eeec6f65 100644 --- a/src/commands/config.js +++ b/src/commands/config.js @@ -1,9 +1,9 @@ /* eslint-disable max-len */ -import chalk from 'chalk'; +import { expand } from '../util/expand.js'; import { ticonfig } from '../util/ticonfig.js'; import { TiError } from '../util/tierror.js'; -import { expand } from '../util/expand.js'; +import chalk from 'chalk'; const { cyan } = chalk; @@ -38,34 +38,34 @@ export function config(_logger, _config, _cli) { flags: { append: { abbr: 'a', - desc: 'appends a value to a key containing a list of values' + desc: 'appends a value to a key containing a list of values', }, json: { - desc: 'output config as JSON' + desc: 'output config as JSON', }, remove: { abbr: 'r', - desc: 'removes all values and all its descendants or a specific value from a list of values' - } + desc: 'removes all values and all its descendants or a specific value from a list of values', + }, }, options: { output: { abbr: 'o', default: 'report', hidden: true, - values: ['report', 'json'] - } + values: ['report', 'json'], + }, }, args: [ { name: 'key', - desc: 'the key to get or set' + desc: 'the key to get or set', }, { name: 'value', - desc: 'the value to set the specified key' - } - ] + desc: 'the value to set the specified key', + }, + ], }; } @@ -78,14 +78,17 @@ export function config(_logger, _config, _cli) { export function validate(_logger, _config, cli) { const [key, value] = cli.argv._; - if (key !== undefined && !/^([A-Za-z_]{1}[A-Za-z0-9-_]*(\.[A-Za-z-_]{1}[A-Za-z0-9-_]*)*)$/.test(key)) { + if ( + key !== undefined && + !/^([A-Za-z_]{1}[A-Za-z0-9-_]*(\.[A-Za-z-_]{1}[A-Za-z0-9-_]*)*)$/.test(key) + ) { throw new TiError(`Invalid key "${key}"`); } if (cli.argv.remove) { if (key === undefined) { throw new TiError('Missing key of the config setting to remove', { - after: `Run ${cyan('titanium config --remove ')} to remove the config setting.` + after: `Run ${cyan('titanium config --remove ')} to remove the config setting.`, }); } @@ -94,7 +97,7 @@ export function validate(_logger, _config, cli) { throw new TiError('Too many arguments for "--remove" flag', { after: `Run ${cyan( `titanium config --remove ${key.includes(' ') ? `"${key}"` : key}` - )} to remove the config setting.` + )} to remove the config setting.`, }); } } @@ -148,13 +151,7 @@ export async function run(logger, config, cli) { if (listMatch) { const subPath = listMatch[1]; - const validKeys = [ - 'hooks', - 'modules', - 'sdks', - 'templates', - 'xcode' - ]; + const validKeys = ['hooks', 'modules', 'sdks', 'templates', 'xcode']; if (!validKeys.includes(subPath)) { throw new TiError(`Unsupported key "${key}"\n`); } diff --git a/src/commands/info.js b/src/commands/info.js index a07d3c564..76047f004 100644 --- a/src/commands/info.js +++ b/src/commands/info.js @@ -1,8 +1,8 @@ import { BusyIndicator } from '../util/busyindicator.js'; -import chalk from 'chalk'; import { detect } from '../util/detect.js'; -import wrapAnsi from 'wrap-ansi'; +import chalk from 'chalk'; import { basename } from 'node:path'; +import wrapAnsi from 'wrap-ansi'; const { bold, cyan, gray, magenta, red, yellow } = chalk; const typesList = ['all', 'os', 'nodejs', 'titanium', 'jdk', 'android', 'ios']; @@ -20,23 +20,23 @@ export function config(_logger, _config, _cli) { skipBanner: true, flags: { json: { - desc: 'display info as JSON' - } + desc: 'display info as JSON', + }, }, options: { output: { abbr: 'o', default: 'report', hidden: true, - values: ['report', 'json'] + values: ['report', 'json'], }, types: { abbr: 't', default: 'all', desc: 'information types to display; you may select one or more', - values: typesList.filter(t => t !== 'ios' || process.platform === 'darwin') - } - } + values: typesList.filter((t) => t !== 'ios' || process.platform === 'darwin'), + }, + }, }; } @@ -77,10 +77,7 @@ export async function run(logger, config, cli) { let data; let platformInfo; try { - ({ - data, - platformInfo - } = await detect(cli.debugLogger, config, cli, types)); + ({ data, platformInfo } = await detect(cli.debugLogger, config, cli, types)); } finally { busy?.stop(); } @@ -94,86 +91,104 @@ export async function run(logger, config, cli) { const sections = []; if (types.all || types.os) { - sections.push(new Section({ - name: 'os', - title: 'Operating System', - render() { - logger.log(bold(this.title)); - logger.log(` ${'Name'.padEnd(indent)} = ${magenta(data.os.name)}`); - logger.log(` ${'Version'.padEnd(indent)} = ${magenta(data.os.version)}`); - logger.log(` ${'Architecture'.padEnd(indent)} = ${magenta(data.os.architecture)}`); - logger.log(` ${'# CPUs'.padEnd(indent)} = ${magenta(data.os.numcpus)}`); - logger.log(` ${'Memory'.padEnd(indent)} = ${magenta((data.os.memory / 1024 / 1024 / 1024).toFixed(1) + 'GB')}\n`); - } - })); + sections.push( + new Section({ + name: 'os', + title: 'Operating System', + render() { + logger.log(bold(this.title)); + logger.log(` ${'Name'.padEnd(indent)} = ${magenta(data.os.name)}`); + logger.log(` ${'Version'.padEnd(indent)} = ${magenta(data.os.version)}`); + logger.log(` ${'Architecture'.padEnd(indent)} = ${magenta(data.os.architecture)}`); + logger.log(` ${'# CPUs'.padEnd(indent)} = ${magenta(data.os.numcpus)}`); + logger.log( + ` ${'Memory'.padEnd(indent)} = ${magenta((data.os.memory / 1024 / 1024 / 1024).toFixed(1) + 'GB')}\n` + ); + }, + }) + ); } if (types.all || types.nodejs || types.npm) { - sections.push(new Section({ - name: 'nodejs', - title: 'Node.js', - render() { - logger.log(bold(this.title)); - logger.log(` ${'Node.js Version'.padEnd(indent)} = ${magenta(data.node.version)}`); - logger.log(` ${'npm Version'.padEnd(indent)} = ${magenta(data.npm.version)}\n`); - } - })); + sections.push( + new Section({ + name: 'nodejs', + title: 'Node.js', + render() { + logger.log(bold(this.title)); + logger.log(` ${'Node.js Version'.padEnd(indent)} = ${magenta(data.node.version)}`); + logger.log(` ${'npm Version'.padEnd(indent)} = ${magenta(data.npm.version)}\n`); + }, + }) + ); } if (types.all || types.titanium) { - sections.push(new Section({ - name: 'titanium', - title: 'Titanium SDK', - render() { - logger.log(bold('Titanium CLI')); - logger.log(` ${'CLI Version'.padEnd(indent)} = ${magenta(data.titaniumCLI.version)}\n`); - - logger.log(bold('Titanium SDKs')); - const names = Object.keys(data.titanium); - if (names.length) { - for (const name of names.sort().reverse()) { - const sdk = data.titanium[name]; - logger.log(` ${cyan(name)}`); - logger.log(` ${' Version'.padEnd(indent)} = ${magenta(sdk.version)}`); - logger.log(` ${' Install Location'.padEnd(indent)} = ${magenta(sdk.path)}`); - logger.log(` ${' Platforms'.padEnd(indent)} = ${magenta(sdk.platforms.join(', '))}`); - logger.log(` ${' git Hash'.padEnd(indent)} = ${magenta(sdk.githash || 'unknown')}`); - logger.log(` ${' git Timestamp'.padEnd(indent)} = ${magenta(sdk.timestamp || 'unknown')}\n`); + sections.push( + new Section({ + name: 'titanium', + title: 'Titanium SDK', + render() { + logger.log(bold('Titanium CLI')); + logger.log(` ${'CLI Version'.padEnd(indent)} = ${magenta(data.titaniumCLI.version)}\n`); + + logger.log(bold('Titanium SDKs')); + const names = Object.keys(data.titanium); + if (names.length) { + for (const name of names.sort().reverse()) { + const sdk = data.titanium[name]; + logger.log(` ${cyan(name)}`); + logger.log(` ${' Version'.padEnd(indent)} = ${magenta(sdk.version)}`); + logger.log(` ${' Install Location'.padEnd(indent)} = ${magenta(sdk.path)}`); + logger.log( + ` ${' Platforms'.padEnd(indent)} = ${magenta(sdk.platforms.join(', '))}` + ); + logger.log(` ${' git Hash'.padEnd(indent)} = ${magenta(sdk.githash || 'unknown')}`); + logger.log( + ` ${' git Timestamp'.padEnd(indent)} = ${magenta(sdk.timestamp || 'unknown')}\n` + ); + } + } else { + logger.log(` ${gray('None')}\n`); } - } else { - logger.log(` ${gray('None')}\n`); - } - } - })); + }, + }) + ); } if (types.all || types.jdk) { - sections.push(new Section({ - name: 'jdk', - title: 'Java Development Kit', - render() { - logger.log(bold(this.title)); - if (data.jdk.version) { - logger.log(` ${'Version'.padEnd(indent)} = ${magenta(`${data.jdk.version}_${data.jdk.build}`)}`); - logger.log(` ${'Java Home'.padEnd(indent)} = ${magenta(data.jdk.home)}\n`); - } else { - logger.log(` ${gray('Not found')}\n`); - } - } - })); + sections.push( + new Section({ + name: 'jdk', + title: 'Java Development Kit', + render() { + logger.log(bold(this.title)); + if (data.jdk.version) { + logger.log( + ` ${'Version'.padEnd(indent)} = ${magenta(`${data.jdk.version}_${data.jdk.build}`)}` + ); + logger.log(` ${'Java Home'.padEnd(indent)} = ${magenta(data.jdk.home)}\n`); + } else { + logger.log(` ${gray('Not found')}\n`); + } + }, + }) + ); } for (const info of platformInfo) { - sections.push(new Section({ - name: info.name, - title: info.title, - render() { - const container = { - data: data[info.name] - }; - info.render.call(container, logger, config, s => s.padEnd(indent), bold, magenta, red); - } - })); + sections.push( + new Section({ + name: info.name, + title: info.title, + render() { + const container = { + data: data[info.name], + }; + info.render.call(container, logger, config, (s) => s.padEnd(indent), bold, magenta, red); + }, + }) + ); } if (process.platform === 'darwin' && (types.all || types.ios) && data.iosKeychains) { @@ -201,30 +216,36 @@ export async function run(logger, config, cli) { { issues: [ { - message: 'No Titanium SDKs found. You can download the latest Titanium SDK by running: titanium sdk install', - type: 'error' - } - ] - } + message: + 'No Titanium SDKs found. You can download the latest Titanium SDK by running: titanium sdk install', + type: 'error', + }, + ], + }, ]); } if (withIssues.length) { for (const [type, info] of withIssues) { - const section = sections.find(s => s.name === type); + const section = sections.find((s) => s.name === type); logger.log(bold(`${section.title} Issues`)); for (const issue of info.issues) { - const msg = issue.message.split('\n\n').map(chunk => { - return wrapAnsi( - chunk - .split('\n') - .map(line => line.replace(/(__(.+?)__)/g, bold('$2'))) - .join('\n'), - config.get('cli.width', 80), - { hard: true, trim: false } - ).replace(/\n/g, '\n ') + '\n'; - }).join('\n '); + const msg = issue.message + .split('\n\n') + .map((chunk) => { + return ( + wrapAnsi( + chunk + .split('\n') + .map((line) => line.replace(/(__(.+?)__)/g, bold('$2'))) + .join('\n'), + config.get('cli.width', 80), + { hard: true, trim: false } + ).replace(/\n/g, '\n ') + '\n' + ); + }) + .join('\n '); if (issue.type === 'error') { logger.log(red(` ${process.platform === 'win32' ? '\u00D7' : '\u2715'} ${msg}`)); diff --git a/src/commands/module.js b/src/commands/module.js index defe85f23..a1d531879 100644 --- a/src/commands/module.js +++ b/src/commands/module.js @@ -1,11 +1,11 @@ import { arrayify } from '../util/arrayify.js'; +import { capitalize } from '../util/capitalize.js'; import { expand } from '../util/expand.js'; import { TiError } from '../util/tierror.js'; +import chalk from 'chalk'; +import { detectTiModules } from 'node-titanium-sdk/titanium'; import { existsSync } from 'node:fs'; import { dirname, join, parse } from 'node:path'; -import chalk from 'chalk'; -import { detect } from '../util/timodule.js'; -import { capitalize } from '../util/capitalize.js'; const { bold, cyan, gray } = chalk; @@ -14,7 +14,7 @@ const platformNames = { android: 'Android', commonjs: 'CommonJS', iphone: 'iPhone', - ios: 'iOS' + ios: 'iOS', }; const ModuleSubcommands = {}; @@ -38,7 +38,7 @@ export function config(logger, config, cli) { title: 'Module', defaultSubcommand: 'list', skipBanner: true, - subcommands + subcommands, }; } @@ -82,20 +82,20 @@ ModuleSubcommands.list = { desc: 'print a list of installed modules', flags: { json: { - desc: 'display installed modules as JSON' - } + desc: 'display installed modules as JSON', + }, }, options: { output: { abbr: 'o', default: 'report', hidden: true, - values: ['report', 'json'] + values: ['report', 'json'], }, 'project-dir': { - desc: 'the directory of the project to search' - } - } + desc: 'the directory of the project to search', + }, + }, }; }, async fn(logger, config, cli) { @@ -105,16 +105,16 @@ ModuleSubcommands.list = { const searchPaths = { project: [], config: [], - global: [] + global: [], }; const scopeLabels = { project: 'Project Modules', config: 'Configured Path Modules', - global: 'Global Modules' + global: 'Global Modules', }; const confPaths = arrayify(config.get('paths.modules'), true); const defaultInstallLocation = cli.env.installPath; - const sdkLocations = cli.env.os.sdkPaths.map(p => expand(p)); + const sdkLocations = cli.env.os.sdkPaths.map((p) => expand(p)); // attempt to detect if we're in a project folder by scanning for a tiapp.xml // until we hit the root @@ -134,7 +134,11 @@ ModuleSubcommands.list = { // set our paths from the config file for (let path of confPaths) { path = expand(path); - if (existsSync(path) && !searchPaths.project.includes(path) && !searchPaths.config.includes(path)) { + if ( + existsSync(path) && + !searchPaths.project.includes(path) && + !searchPaths.config.includes(path) + ) { searchPaths.config.push(path); } } @@ -148,7 +152,12 @@ ModuleSubcommands.list = { } for (let path of sdkLocations) { path = expand(path, 'modules'); - if (existsSync(path) && !searchPaths.project.includes(path) && !searchPaths.config.includes(path) && !searchPaths.global.includes(path)) { + if ( + existsSync(path) && + !searchPaths.project.includes(path) && + !searchPaths.config.includes(path) && + !searchPaths.global.includes(path) + ) { searchPaths.global.push(path); } } @@ -193,5 +202,5 @@ ModuleSubcommands.list = { } logger.log(); } - } + }, }; diff --git a/src/commands/sdk.js b/src/commands/sdk.js index f2cf591ed..9369f51bd 100644 --- a/src/commands/sdk.js +++ b/src/commands/sdk.js @@ -1,21 +1,29 @@ -import chalk from 'chalk'; -import { TiError } from '../util/tierror.js'; -import { expand } from '../util/expand.js'; -import * as version from '../util/version.js'; import { BusyIndicator } from '../util/busyindicator.js'; -import fs from 'fs-extra'; -import { mkdir } from 'node:fs/promises'; -import { suggest } from '../util/suggest.js'; import { columns } from '../util/columns.js'; -import { basename, dirname, join } from 'node:path'; -import os from 'node:os'; import { ProgressBar } from '../util/progress.js'; +import { prompt } from '../util/prompt.js'; +import { TiError } from '../util/tierror.js'; +import chalk from 'chalk'; +import { + getTitaniumBranchBuilds, + getTitaniumBranches, + getTitaniumReleases, +} from 'node-titanium-sdk/titanium'; +import { + exists, + expand, + extractZip, + isDir, + request, + suggest, + version, +} from 'node-titanium-sdk/util'; +import { createWriteStream } from 'node:fs'; +import { mkdir, readFile, rename, writeFile } from 'node:fs/promises'; +import os from 'node:os'; +import { basename, dirname, join } from 'node:path'; import { Transform } from 'node:stream'; import { pipeline } from 'node:stream/promises'; -import { extractZip } from '../util/extract-zip.js'; -import { prompt } from '../util/prompt.js'; -import { request } from 'node-titanium-sdk/util'; -import { getTitaniumBranchBuilds, getTitaniumBranches, getTitaniumReleases } from 'node-titanium-sdk/titanium'; import prettyBytes from 'pretty-bytes'; import wrapAnsi from 'wrap-ansi'; @@ -42,7 +50,7 @@ export function config(logger, config, cli) { title: 'SDK', defaultSubcommand: 'list', skipBanner: true, - subcommands + subcommands, }; } @@ -73,7 +81,7 @@ export async function run(logger, config, cli) { SdkSubcommands.select = { conf() { return { - hidden: true + hidden: true, }; }, fn(logger, config, _cli) { @@ -92,7 +100,7 @@ default to the latest installed SDK.`, ) ) ); - } + }, }; /** @@ -110,41 +118,43 @@ SdkSubcommands.list = { flags: { branches: { abbr: 'b', - desc: 'retrieve and print all branches' + desc: 'retrieve and print all branches', }, json: { - desc: 'display installed modules as JSON' + desc: 'display installed modules as JSON', }, releases: { abbr: 'r', - desc: 'retrieve and print all releases' + desc: 'retrieve and print all releases', }, unstable: { abbr: 'u', - desc: 'retrieve and print all unstable release candidate (rc) and beta releases' - } + desc: 'retrieve and print all unstable release candidate (rc) and beta releases', + }, }, options: { branch: { - desc: 'branch to fetch CI builds' + desc: 'branch to fetch CI builds', }, output: { abbr: 'o', default: 'report', hidden: true, - values: ['report', 'json'] - } - } + values: ['report', 'json'], + }, + }, }; }, async fn(logger, config, cli) { const os = cli.env.os.name; - const [releases, branches, branchBuilds] = (await Promise.allSettled([ - (cli.argv.releases || cli.argv.unstable) && getTitaniumReleases(cli.argv.unstable), - cli.argv.branches && getTitaniumBranches(), - cli.argv.branch && getTitaniumBranchBuilds(cli.argv.branch, os) - ])).map(r => { + const [releases, branches, branchBuilds] = ( + await Promise.allSettled([ + (cli.argv.releases || cli.argv.unstable) && getTitaniumReleases(cli.argv.unstable), + cli.argv.branches && getTitaniumBranches(), + cli.argv.branch && getTitaniumBranchBuilds(cli.argv.branch, os), + ]) + ).map((r) => { return r.status === 'fulfilled' ? r.value : new TiError(r.reason); }); @@ -153,11 +163,12 @@ SdkSubcommands.list = { const defaultInstallLocation = cli.env.installPath; const locations = Array.from( - new Set([ - cli.env.os.sdkPaths, - defaultInstallLocation, - config.get('paths.sdks') - ].flat().filter(Boolean).map(p => p && expand(p))) + new Set( + [cli.env.os.sdkPaths, defaultInstallLocation, config.get('paths.sdks')] + .flat() + .filter(Boolean) + .map((p) => p && expand(p)) + ) ).sort(); if (cli.argv.json || cli.argv.output === 'json') { @@ -168,12 +179,14 @@ SdkSubcommands.list = { } const obj = { - branch: branchBuilds?.length ? { - [cli.argv.branch]: branchBuilds - } : {}, + branch: branchBuilds?.length + ? { + [cli.argv.branch]: branchBuilds, + } + : {}, branches: { defaultBranch: 'main', - branches: branches || [] + branches: branches || [], }, defaultInstallLocation, installLocations: locations, @@ -181,11 +194,14 @@ SdkSubcommands.list = { obj[v] = sdks[v].path; return obj; }, {}), - releases: releases && releases.reduce((obj, { name, assets }) => { - obj[name] = assets.find(a => a.os === os).url; - return obj; - }, {}) || {}, - sdks + releases: + (releases && + releases.reduce((obj, { name, assets }) => { + obj[name] = assets.find((a) => a.os === os).url; + return obj; + }, {})) || + {}, + sdks, }; logger.log(JSON.stringify(obj, null, '\t')); @@ -204,7 +220,10 @@ SdkSubcommands.list = { if (vers.length) { const maxVersionLen = vers.reduce((len, b) => Math.max(len, sdks[b].version.length), 0); const maxNameLen = vers.reduce((len, b) => { - return Math.max(len, sdks[b].manifest && sdks[b].manifest.name ? sdks[b].manifest.name.length : 0); + return Math.max( + len, + sdks[b].manifest && sdks[b].manifest.name ? sdks[b].manifest.name.length : 0 + ); }, 0); logger.log('Installed SDKs:'); @@ -220,15 +239,17 @@ SdkSubcommands.list = { } } - logger.log(` ${ - cyan(name.padEnd(maxNameLen)) - } ${ - magenta(ver.padEnd(maxVersionLen) - )} ${sdks[v].path}`); + logger.log( + ` ${cyan(name.padEnd(maxNameLen))} ${magenta( + ver.padEnd(maxVersionLen) + )} ${sdks[v].path}` + ); } } else { logger.log(red('No Titanium SDKs found\n')); - logger.log(`You can download the latest Titanium SDK by running: ${cyan('titanium sdk install')}\n`); + logger.log( + `You can download the latest Titanium SDK by running: ${cyan('titanium sdk install')}\n` + ); } if (releases) { @@ -243,7 +264,9 @@ SdkSubcommands.list = { for (const r of releases) { logger.log(` ${cyan(r.name.padEnd(12))}\ ${Intl.DateTimeFormat('en-US', { dateStyle: 'short' }).format(new Date(r.date)).padStart(8)}\ -${prettyBytes(r.assets.find(a => a.os === os).size).toUpperCase().padStart(11)}\ +${prettyBytes(r.assets.find((a) => a.os === os).size) + .toUpperCase() + .padStart(11)}\ ${Object.prototype.hasOwnProperty.call(sdks, r) ? ' [installed]' : ''}\ ${r.type !== 'ga' ? gray(' [unstable]') : i++ === 0 ? green(' [latest stable]') : ''}`); } @@ -271,10 +294,14 @@ ${r.type !== 'ga' ? gray(' [unstable]') : i++ === 0 ? green(' [latest stable]' logger.log(`'${cli.argv.branch}' Branch Builds:`); if (branchBuilds?.length) { for (const b of branchBuilds) { - const dt = Intl.DateTimeFormat('en-US', { dateStyle: 'short' }).format(new Date(b.date)); + const dt = Intl.DateTimeFormat('en-US', { dateStyle: 'short' }).format( + new Date(b.date) + ); logger.log(` ${cyan(b.name)}\ ${dt.padStart(11)}\ -${prettyBytes(b.assets.find(a => a.os === os).size).toUpperCase().padStart(11)} ${gray('[unstable]')}`); +${prettyBytes(b.assets.find((a) => a.os === os).size) + .toUpperCase() + .padStart(11)} ${gray('[unstable]')}`); } logger.log(gray('** NOTE: these builds not recommended for production use **')); } else { @@ -282,7 +309,7 @@ ${prettyBytes(b.assets.find(a => a.os === os).size).toUpperCase().padStart(11)} } } } - } + }, }; /** @@ -312,38 +339,38 @@ SdkSubcommands.install = { args: [ { desc: 'the version to install, "latest", URL, zip file, or :', - name: 'version' - } + name: 'version', + }, ], flags: { default: { abbr: 'd', - hidden: true + hidden: true, }, force: { abbr: 'f', - desc: 'force re-install' + desc: 'force re-install', }, 'keep-files': { abbr: 'k', - desc: 'keep downloaded files after install' - } + desc: 'keep downloaded files after install', + }, }, options: { branch: { abbr: 'b', desc: 'the branch to install from or "latest" (stable)', - hint: 'branch name' - } - } + hint: 'branch name', + }, + }, }; }, async fn(logger, config, cli) { - const titaniumDir = expand(cli.env.installPath); + const titaniumDir = expand(cli.env.installPath); const showProgress = !cli.argv.quiet && !!cli.argv['progress-bars']; - const osName = cli.env.os.name; - const subject = cli.argv._.shift() || 'latest'; - const { trace } = cli.debugLogger; + const osName = cli.env.os.name; + const subject = cli.argv._.shift() || 'latest'; + const { trace } = cli.debugLogger; logger.skipBanner(false); logger.banner(); @@ -360,7 +387,7 @@ SdkSubcommands.install = { logger, osName, showProgress, - subject + subject, }); // step 2: extract the SDK zip file @@ -374,7 +401,7 @@ SdkSubcommands.install = { osName, showProgress, subject, - titaniumDir + titaniumDir, }); // step 3: validate the manifest.json @@ -383,10 +410,10 @@ SdkSubcommands.install = { if (name) { try { const manifestFile = join(src, 'manifest.json'); - const manifest = await fs.readJson(manifestFile); + const manifest = JSON.parse(await readFile(manifestFile, 'utf8')); if (renameTo) { manifest.name = renameTo; - await fs.writeJson(manifestFile, manifest); + await writeFile(manifestFile, JSON.stringify(manifest, null, '\t')); } } catch { name = null; @@ -403,36 +430,37 @@ SdkSubcommands.install = { logger.log(); } logger.log(`\nInstalling SDK files to ${cyan(dest)}`); - await fs.mkdirs(dest); - await fs.move(src, dest, { overwrite: true }); + await mkdir(dest, { recursive: true }); + await rm(dest, { force: true, recursive: true }); + await rename(src, dest); // step 5: install the modules const modules = {}; src = join(tempDir, 'modules'); - if (fs.statSync(src).isDirectory()) { + if (isDir(src)) { const modulesDest = join(titaniumDir, 'modules'); - for (const platform of fs.readdirSync(src)) { + for (const platform of await readdir(src)) { const srcPlatformDir = join(src, platform); - if (!fs.statSync(srcPlatformDir).isDirectory()) { + if (!isDir(srcPlatformDir)) { continue; } - for (const moduleName of fs.readdirSync(srcPlatformDir)) { + for (const moduleName of await readdir(srcPlatformDir)) { const srcModuleDir = join(srcPlatformDir, moduleName); - if (!fs.statSync(srcModuleDir).isDirectory()) { + if (!isDir(srcModuleDir)) { continue; } - for (const ver of fs.readdirSync(srcModuleDir)) { + for (const ver of await readdir(srcModuleDir)) { const srcVersionDir = join(srcModuleDir, ver); - if (!fs.statSync(srcVersionDir).isDirectory()) { + if (!isDir(srcVersionDir)) { continue; } const destDir = join(modulesDest, platform, moduleName, ver); - if (!forceModules && fs.existsSync(destDir)) { + if (!forceModules && exists(destDir)) { trace(`Module ${cyan(`${moduleName}@${ver}`)} already installed`); continue; } @@ -447,7 +475,8 @@ SdkSubcommands.install = { trace(`Installing ${cyan(Object.keys(modules).length)} modules:`); for (const [name, { src, dest }] of Object.entries(modules)) { trace(` ${cyan(name)}`); - await fs.move(src, dest, { overwrite: true }); + await rm(dest, { force: true, recursive: true }); + await rename(src, dest); } } else { trace('SDK has new modules to install'); @@ -456,12 +485,12 @@ SdkSubcommands.install = { // step 6: cleanup if (downloadedFile && !cli.argv['keep-files']) { - await fs.remove(downloadedFile); + await rm(downloadedFile); } - await fs.remove(tempDir); + await rm(tempDir, { force: true, recursive: true }); logger.log(`\nTitanium SDK ${cyan(name)} successfully installed!`); - } + }, }; async function getInstallFile({ branch, config, logger, osName, showProgress, subject }) { @@ -470,14 +499,14 @@ async function getInstallFile({ branch, config, logger, osName, showProgress, su if (uriMatch && uriMatch[2]) { file = uriMatch[2]; - } else if (subject && fs.existsSync(subject)) { + } else if (subject && exists(subject)) { file = subject; } if (file) { file = expand(file); - if (!fs.existsSync(file)) { + if (!exists(file)) { throw new TiError('Specified file does not exist'); } @@ -499,21 +528,21 @@ async function getInstallFile({ branch, config, logger, osName, showProgress, su const branches = await getBranches(); if (!branches.includes(branch)) { throw new TiError(`Branch "${branch}" does not exist`, { - after: `${ - suggest(branch, branches, 2) - }Available Branches:\n${ - columns(branches, ' ', config.get('cli.width', 80)) - }` + after: `${suggest(branch, branches, 2)}Available Branches:\n${columns( + branches, + ' ', + config.get('cli.width', 80) + )}`, }); } const builds = await getBranchBuilds(branch, osName); - const build = uri === 'latest' ? builds[0] : builds.find(b => b.name.toLowerCase() === uri); + const build = uri === 'latest' ? builds[0] : builds.find((b) => b.name.toLowerCase() === uri); if (!build) { throw new TiError(`CI build ${subject} does not exist`); } - const asset = build.assets.find(a => a.os === osName); + const asset = build.assets.find((a) => a.os === osName); if (!asset) { throw new TiError(`CI build ${subject} does not support ${osName}`); } @@ -525,21 +554,21 @@ async function getInstallFile({ branch, config, logger, osName, showProgress, su const releases = await getTitaniumReleases(true); if (uri === 'latest') { - release = releases.find(r => r.type === 'ga'); + release = releases.find((r) => r.type === 'ga'); } else if (uri === 'latest-rc') { - release = releases.find(r => r.type === 'rc'); + release = releases.find((r) => r.type === 'rc'); } else if (uri === 'latest-beta') { - release = releases.find(r => r.type === 'beta'); + release = releases.find((r) => r.type === 'beta'); } else { - release = releases.find(r => r.name.toLowerCase() === uri); + release = releases.find((r) => r.name.toLowerCase() === uri); if (!release) { const name = `${uri}.ga`; - release = releases.find(r => r.name.toLowerCase() === name); + release = releases.find((r) => r.name.toLowerCase() === name); } } if (release) { - const asset = release.assets.find(a => a.os === osName); + const asset = release.assets.find((a) => a.os === osName); if (!asset) { throw new TiError(`SDK release ${subject} does not support ${osName}`); } @@ -548,12 +577,19 @@ async function getInstallFile({ branch, config, logger, osName, showProgress, su } if (!url) { - throw new TiError(`Unable to find any Titanium SDK releases or CI builds that match "${subject}"`); + throw new TiError( + `Unable to find any Titanium SDK releases or CI builds that match "${subject}"` + ); } // step 1.5: download the file - let downloadedFile = expand('~', '.titanium', 'downloads', `titanium-sdk-${Math.floor(Math.random(1e6))}.zip`); + let downloadedFile = expand( + '~', + '.titanium', + 'downloads', + `titanium-sdk-${Math.floor(Math.random(1e6))}.zip` + ); const downloadDir = dirname(downloadedFile); await mkdir(downloadDir, { recursive: true }); @@ -563,7 +599,7 @@ async function getInstallFile({ branch, config, logger, osName, showProgress, su let busy; let filename; let total; - const out = fs.createWriteStream(downloadedFile); + const out = createWriteStream(downloadedFile); let response = await request(url); if ([301, 302].includes(response.statusCode)) { @@ -587,7 +623,7 @@ async function getInstallFile({ branch, config, logger, osName, showProgress, su complete: cyan('='), incomplete: gray('.'), width: 40, - total + total, }); } else { busy = new BusyIndicator(); @@ -600,7 +636,7 @@ async function getInstallFile({ branch, config, logger, osName, showProgress, su bar?.tick(chunk.length); this.push(chunk); callback(); - } + }, }); await pipeline(response.body, progressStream, out); @@ -616,7 +652,8 @@ async function getInstallFile({ branch, config, logger, osName, showProgress, su if (filename) { file = join(downloadDir, filename); - await fs.move(downloadedFile, file, { overwrite: true }); + await rm(file, { force: true }); + await rename(downloadedFile, file); downloadedFile = file; } else { file = downloadedFile; @@ -625,7 +662,17 @@ async function getInstallFile({ branch, config, logger, osName, showProgress, su return { downloadedFile, file }; } -async function extractSDK({ debugLogger, file, force, logger, noPrompt, osName, showProgress, subject, titaniumDir }) { +async function extractSDK({ + debugLogger, + file, + force, + logger, + noPrompt, + osName, + showProgress, + subject, + titaniumDir, +}) { const sdkDestRegExp = new RegExp(`^mobilesdk[/\\\\]${osName}[/\\\\]([^/\\\\]+)`); const tempDir = join(os.tmpdir(), `titanium-cli-${Math.floor(Math.random() * 1e6)}`); let artifact; @@ -647,7 +694,7 @@ async function extractSDK({ debugLogger, file, force, logger, noPrompt, osName, noPrompt, osName, sdkDir: join(titaniumDir, 'mobilesdk', osName, name), - subject + subject, }); forceModules = result?.forceModules ?? force; @@ -659,7 +706,7 @@ async function extractSDK({ debugLogger, file, force, logger, noPrompt, osName, complete: cyan('='), incomplete: gray('.'), width: 40, - total + total, }); } } @@ -674,7 +721,7 @@ async function extractSDK({ debugLogger, file, force, logger, noPrompt, osName, await extractZip({ dest: tempDir, file, - onEntry + onEntry, }); if (!artifact) { @@ -689,16 +736,25 @@ async function extractSDK({ debugLogger, file, force, logger, noPrompt, osName, await extractZip({ dest: tempDir2, file, - onEntry + onEntry, }); - await fs.remove(tempDir); + await rm(tempDir, { force: true, recursive: true }); return { forceModules, name, renameTo, tempDir: tempDir2 }; } -async function checkSDKFile({ force, logger, _filename, name, noPrompt, _osName, sdkDir, subject }) { +async function checkSDKFile({ + force, + logger, + _filename, + name, + noPrompt, + _osName, + sdkDir, + subject, +}) { try { - if (force || !fs.statSync(sdkDir).isDirectory()) { + if (force || !isDir(sdkDir)) { return; } } catch { @@ -713,11 +769,11 @@ async function checkSDKFile({ force, logger, _filename, name, noPrompt, _osName, if (noPrompt) { if (subject === 'latest' && name === latest.name) { throw new TiError(`Titanium SDK ${name} is already installed`, { - after: `You're up-to-date. Version ${cyan(latest.name)} is currently the newest version available.\n${tip}` + after: `You're up-to-date. Version ${cyan(latest.name)} is currently the newest version available.\n${tip}`, }); } throw new TiError(`Titanium SDK ${name} is already installed`, { - after: tip + after: tip, }); } @@ -726,7 +782,7 @@ async function checkSDKFile({ force, logger, _filename, name, noPrompt, _osName, for (let i = 2; true; i++) { try { renameTo = `${name}-${i}`; - if (!fs.statSync(`${sdkDir}-${i}`).isDirectory()) { + if (!isDir(`${sdkDir}-${i}`)) { break; } } catch { @@ -743,8 +799,8 @@ async function checkSDKFile({ force, logger, _filename, name, noPrompt, _osName, choices: [ { title: 'Overwrite', value: 'overwrite' }, { title: `Rename as ${basename(renameTo)}`, value: 'rename' }, - { title: 'Abort', value: 'abort' } - ] + { title: 'Abort', value: 'abort' }, + ], }); if (!action || action === 'abort') { @@ -778,15 +834,15 @@ SdkSubcommands.uninstall = { { desc: 'one or more SDK names to uninstall', name: 'versions', - variadic: true - } + variadic: true, + }, ], flags: { force: { abbr: 'f', - desc: 'force uninstall without confirmation' - } - } + desc: 'force uninstall without confirmation', + }, + }, }; }, async fn(logger, _config, cli) { @@ -802,7 +858,9 @@ SdkSubcommands.uninstall = { throw new TiError('Missing argument'); } if (!force) { - throw new TiError('To uninstall a Titanium SDK in non-interactive mode, you must use --force'); + throw new TiError( + 'To uninstall a Titanium SDK in non-interactive mode, you must use --force' + ); } } @@ -813,10 +871,10 @@ SdkSubcommands.uninstall = { message: 'Which SDKs to uninstall?', instructions: false, hint: 'Space to select. Return to submit', - choices: vers.map(v => ({ + choices: vers.map((v) => ({ title: v, - value: v - })) + value: v, + })), }); if (!versions) { return; @@ -824,19 +882,26 @@ SdkSubcommands.uninstall = { logger.log(); } - const found = versions.filter(v => vers.includes(v)); + const found = versions.filter((v) => vers.includes(v)); const maxlen = versions.reduce((a, b) => Math.max(a, b.length), 0); if (!found.length) { for (const v of versions) { - logger.log(` • ${cyan(v.padEnd(maxlen))} ${cli.env.sdks[v]?.path || yellow('not found')}`); + const recommended = suggest(v, vers); + if (recommended) { + logger.log(` • ${cyan(v)} not found, did you mean ${recommended}?`); + } else { + logger.log(` • ${cyan(v)} not found`); + } } return; } if (!force) { // prompt for confirmation - logger.log(`${yellow('WARNING!')} This will permanently remove the following Titanium SDKs:\n`); + logger.log( + `${yellow('WARNING!')} This will permanently remove the following Titanium SDKs:\n` + ); for (const v of versions) { logger.log(` • ${cyan(v.padEnd(maxlen))} ${cli.env.sdks[v]?.path || yellow('not found')}`); } @@ -848,7 +913,7 @@ SdkSubcommands.uninstall = { message: 'Proceed?', initial: false, active: 'yes', - inactive: 'no' + inactive: 'no', }); if (!confirm) { return; @@ -864,17 +929,19 @@ SdkSubcommands.uninstall = { let results; try { - results = await Promise.allSettled(found.map(async (ver) => { - const dir = cli.env.sdks[ver].path; - try { - await fs.remove(dir); - return dir; - } catch (e) { - throw new TiError(`Failed to remove ${dir}`, { - after: e.message - }); - } - })); + results = await Promise.allSettled( + found.map(async (ver) => { + const dir = cli.env.sdks[ver].path; + try { + await rm(dir, { force: true, recursive: true }); + return dir; + } catch (e) { + throw new TiError(`Failed to remove ${dir}`, { + after: e.message, + }); + } + }) + ); } finally { busy?.stop(); } @@ -889,5 +956,5 @@ SdkSubcommands.uninstall = { } } } - } + }, }; diff --git a/src/commands/setup.js b/src/commands/setup.js index 17cfe3005..3f5f26db4 100644 --- a/src/commands/setup.js +++ b/src/commands/setup.js @@ -29,9 +29,9 @@ export async function config(logger, config, cli) { name: 'screen', default: 'mainmenu', desc: 'initial screen', - values: Object.keys(screens.screens).sort() - } - ] + values: Object.keys(screens.screens).sort(), + }, + ], }; } diff --git a/src/main.js b/src/main.js index a5025d22a..3ccf34d9a 100644 --- a/src/main.js +++ b/src/main.js @@ -1,6 +1,6 @@ import { CLI } from './cli.js'; -import chalk from 'chalk'; import { TiError } from './util/tierror.js'; +import chalk from 'chalk'; const cli = new CLI(); @@ -10,13 +10,11 @@ try { cli.logger.bannerEnabled(true); cli.logger.skipBanner(false); cli.logger.banner(); - console.error(`${ - e.before ? `${e.before}\n\n` : '' - }${ - chalk.red((e instanceof TiError ? `Error: ${e.message}` : e.stack).trim()) - }\n${ - e.after ? `\n${e.after}\n` : '' - }`); + console.error( + `${e.before ? `${e.before}\n\n` : ''}${chalk.red( + (e instanceof TiError ? `Error: ${e.message}` : e.stack).trim() + )}\n${e.after ? `\n${e.after}\n` : ''}` + ); if (!(e instanceof TiError) || e.showHelp) { cli.command?.help(); } diff --git a/src/util/apply-command-config.js b/src/util/apply-command-config.js index 5c2a61a5c..bfd3b91db 100644 --- a/src/util/apply-command-config.js +++ b/src/util/apply-command-config.js @@ -1,6 +1,6 @@ -import { Argument, Command, Option } from 'commander'; import { ticonfig } from './ticonfig.js'; import { TiError } from './tierror.js'; +import { Argument, Command, Option } from 'commander'; /** * Takes a Titanium CLI command config with flags, options, args, and @@ -22,7 +22,9 @@ export function applyCommandConfig(cli, cmdName, cmd, conf) { if (conf.flags) { for (const [name, meta] of Object.entries(conf.flags)) { - cli.debugLogger.trace(`Adding "${cmdName}" flag: ${meta.abbr ? `-${meta.abbr}, ` : ''}--${name}`); + cli.debugLogger.trace( + `Adding "${cmdName}" flag: ${meta.abbr ? `-${meta.abbr}, ` : ''}--${name}` + ); const opt = new Option(`${meta.abbr ? `-${meta.abbr}, ` : ''}--${name}`, meta.desc); if (meta.default) { opt.default(meta.default); @@ -56,11 +58,13 @@ export function applyCommandConfig(cli, cmdName, cmd, conf) { continue; } - cli.debugLogger.trace(`Adding "${cmdName}" option: ${meta.abbr ? `-${meta.abbr}, ` : ''}${long} [value]`); + cli.debugLogger.trace( + `Adding "${cmdName}" option: ${meta.abbr ? `-${meta.abbr}, ` : ''}${long} [value]` + ); cmd.addOption(opt); if (typeof meta.callback === 'function') { - cmd.on(`option:${opt.attributeName()}`, value => { + cmd.on(`option:${opt.attributeName()}`, (value) => { cli.debugLogger.trace(`Firing --${name} option callback: ${value ?? opt.defaultValue}`); const result = meta.callback(value ?? opt.defaultValue); cmd.setOptionValue(opt.name(), result ?? value ?? opt.defaultValue); @@ -78,7 +82,7 @@ export function applyCommandConfig(cli, cmdName, cmd, conf) { } return true; - } + }, }); } @@ -127,15 +131,15 @@ export function applyCommandConfig(cli, cmdName, cmd, conf) { .configureHelp({ helpWidth: ticonfig.get('cli.width', 80), showGlobalOptions: true, - sortSubcommands: true + sortSubcommands: true, }) .configureOutput({ - outputError: msg => { + outputError: (msg) => { // explicitly set the subcommand so the global error // handler can print the correct help screen cli.command = subcmd; throw new TiError(msg.replace(/^error:\s*/, '')); - } + }, }) .action((...args) => cli.executeCommand(args)); @@ -148,14 +152,14 @@ export function applyCommandConfig(cli, cmdName, cmd, conf) { cmd.addCommand(subcmd, { isDefault: conf.defaultSubcommand === name, - hidden: !!subconf.hidden + hidden: !!subconf.hidden, }); } } } function optionExists(ctx, name) { - const exists = ctx.options.find(o => o.name() === name); + const exists = ctx.options.find((o) => o.name() === name); if (exists) { return true; } diff --git a/src/util/arrayify.js b/src/util/arrayify.js index 19df9706b..6f1359b9a 100644 --- a/src/util/arrayify.js +++ b/src/util/arrayify.js @@ -6,6 +6,22 @@ * @returns {Array} */ export function arrayify(it, removeFalsey) { - const arr = typeof it === 'undefined' ? [] : it instanceof Set ? Array.from(it) : Array.isArray(it) ? it : [it]; - return removeFalsey ? arr.filter(v => typeof v !== 'undefined' && v !== null && v !== '' && v !== false && (typeof v !== 'number' || !isNaN(v))) : arr; + const arr = + typeof it === 'undefined' + ? [] + : it instanceof Set + ? Array.from(it) + : Array.isArray(it) + ? it + : [it]; + return removeFalsey + ? arr.filter( + (v) => + typeof v !== 'undefined' && + v !== null && + v !== '' && + v !== false && + (typeof v !== 'number' || !isNaN(v)) + ) + : arr; } diff --git a/src/util/columns.js b/src/util/columns.js index fcd0f367f..de2afa98b 100644 --- a/src/util/columns.js +++ b/src/util/columns.js @@ -6,9 +6,10 @@ * @returns {String} The rendered columns */ export function columns(items, margin = '', maxwidth = 80) { - const longest = items.reduce((a, b) => { - return Math.max(a, b.length); - }, 0) + 6; + const longest = + items.reduce((a, b) => { + return Math.max(a, b.length); + }, 0) + 6; const curwidth = process.stdout.columns || 80; const width = maxwidth ? Math.min(maxwidth, curwidth) : curwidth; const len = items.length; diff --git a/src/util/detect.js b/src/util/detect.js index 500befdea..be03d7aff 100644 --- a/src/util/detect.js +++ b/src/util/detect.js @@ -1,36 +1,30 @@ -import { existsSync } from 'node:fs'; -import os from 'node:os'; import { detect as jdkInfo } from './jdk.js'; +import chalk from 'chalk'; import { detectTitaniumSDKs } from 'node-titanium-sdk/titanium'; +import { existsSync } from 'node:fs'; +import { readFile } from 'node:fs/promises'; +import os from 'node:os'; import { join } from 'node:path'; import { pathToFileURL } from 'node:url'; -import chalk from 'chalk'; -import { readFile } from 'node:fs/promises'; const { cyan } = chalk; export async function detect(logger, config, cli, types = { all: true }) { - const [ - os, - node, - npm, - titanium, - titaniumCLI, - jdk, - ...platformData - ] = await Promise.all([ + const [os, node, npm, titanium, titaniumCLI, jdk, ...platformData] = await Promise.all([ (types.all || types.os) && osInfo(), (types.all || types.nodejs) && nodeInfo(), (types.all || types.nodejs) && npmInfo(), (types.all || types.titanium) && titaniumSDKInfo(config), (types.all || types.titanium) && titaniumCLIInfo(cli), (types.all || types.jdk) && jdkInfo(config), - ...Object.keys(cli.sdk?.platforms || {}).sort().map(async name => { - const type = name === 'iphone' ? 'ios' : name; - if (types.all || types[type]) { - return await loadPlatformInfo(logger, cli.sdk.platforms[name], config); - } - }) + ...Object.keys(cli.sdk?.platforms || {}) + .sort() + .map(async (name) => { + const type = name === 'iphone' ? 'ios' : name; + if (types.all || types[type]) { + return await loadPlatformInfo(logger, cli.sdk.platforms[name], config); + } + }), ]); const data = { @@ -39,7 +33,7 @@ export async function detect(logger, config, cli, types = { all: true }) { npm, titanium, titaniumCLI, - jdk + jdk, }; const platformInfo = []; @@ -53,7 +47,7 @@ export async function detect(logger, config, cli, types = { all: true }) { return { data, - platformInfo + platformInfo, }; } @@ -69,7 +63,7 @@ async function loadPlatformInfo(logger, platform, config) { const mod = await import(fileUrl); const dummy = { data: null, - issues: [] + issues: [], }; return await new Promise((resolve, reject) => { @@ -83,7 +77,7 @@ async function loadPlatformInfo(logger, platform, config) { title: mod.title, render: mod.render, }, - data + data, }); } }); @@ -102,11 +96,11 @@ async function osInfo() { if (name === 'darwin') { const { stdout } = await $`sw_vers`; - m = stdout.match(/ProductName:\s+(.+)/i) + m = stdout.match(/ProductName:\s+(.+)/i); if (m) { name = m[1]; } - m = stdout.match(/ProductVersion:\s+(.+)/i) + m = stdout.match(/ProductVersion:\s+(.+)/i); if (m) { version = m[1]; } @@ -114,11 +108,11 @@ async function osInfo() { name = 'GNU/Linux'; if (existsSync('/etc/lsb-release')) { const s = await readFile('/etc/lsb-release', 'utf-8'); - m = s.match(/DISTRIB_DESCRIPTION=(.+)/i) + m = s.match(/DISTRIB_DESCRIPTION=(.+)/i); if (m) { name = m[1].replaceAll('"', ''); } - m = s.match(/DISTRIB_RELEASE=(.+)/i) + m = s.match(/DISTRIB_RELEASE=(.+)/i); if (m) { name = m[1].replaceAll('"', ''); } @@ -133,7 +127,10 @@ async function osInfo() { } } else { try { - const { stdout } = await $('powershell', ['-Command', 'Get-CimInstance Win32_OperatingSystem | Select-Object Caption, Version | ConvertTo-Json']); + const { stdout } = await $('powershell', [ + '-Command', + 'Get-CimInstance Win32_OperatingSystem | Select-Object Caption, Version | ConvertTo-Json', + ]); ({ Caption: name, Version: version } = JSON.parse(stdout)); } catch {} } @@ -143,13 +140,13 @@ async function osInfo() { version, architecture: `${process.arch.includes('64') ? 64 : 32}-bit`, numcpus: os.cpus().length, - memory: os.totalmem() + memory: os.totalmem(), }; } async function nodeInfo() { return { - version: process.versions.node + version: process.versions.node, }; } @@ -157,7 +154,7 @@ async function npmInfo() { const { $ } = await import('execa'); const { stdout: version } = await $`npm --version`; return { - version + version, }; } @@ -171,7 +168,7 @@ async function titaniumSDKInfo(config) { path: sdk.path, platforms: Object.keys(sdk.platforms), githash: sdk.githash, - timestamp: sdk.timestamp + timestamp: sdk.timestamp, }; } @@ -180,6 +177,6 @@ async function titaniumSDKInfo(config) { async function titaniumCLIInfo(cli) { return { - version: cli.version + version: cli.version, }; } diff --git a/src/util/expand.js b/src/util/expand.js index 1f51136fe..d8416b57e 100644 --- a/src/util/expand.js +++ b/src/util/expand.js @@ -5,11 +5,16 @@ const winRegExp = /^win/; const winEnvVarRegExp = /(%([^%]*)%)/g; export function expand(...segments) { - segments[0] = segments[0].replace(homeDirRegExp, (process.env.HOME || process.env.USERPROFILE) + '$1'); + segments[0] = segments[0].replace( + homeDirRegExp, + (process.env.HOME || process.env.USERPROFILE) + '$1' + ); if (winRegExp.test(process.platform)) { - return resolve(join(...segments).replace(winEnvVarRegExp, (_s, m, n) => { - return process.env[n] || m; - })); + return resolve( + join(...segments).replace(winEnvVarRegExp, (_s, m, n) => { + return process.env[n] || m; + }) + ); } return resolve(...segments); } diff --git a/src/util/extract-zip.js b/src/util/extract-zip.js deleted file mode 100644 index 74fef2db7..000000000 --- a/src/util/extract-zip.js +++ /dev/null @@ -1,119 +0,0 @@ -import yauzl from 'yauzl'; -import fs from 'node:fs'; -import { mkdir } from 'node:fs/promises'; -import path from 'node:path'; - -/** - * Extracts a zip file to the specified destination. - * - * @param {Object} params - Various parameters. - * @param {String} params.dest - The destination to extract the file. - * @param {String} params.file - The path to the zip file to extract. - * @param {Function} [params.onEntry] - A callback to fire per entry. - * @returns {Promise} - */ -export async function extractZip(params) { - if (!params || typeof params !== 'object') { - throw new TypeError('Expected params to be an object'); - } - - let { dest, file } = params; - - if (!dest || typeof dest !== 'string') { - throw new TypeError('Expected destination directory to be a non-empty string'); - } - - if (!file || typeof file !== 'string') { - throw new TypeError('Expected zip file to be a non-empty string'); - } - - if (!fs.existsSync(file)) { - throw new Error('The specified zip file does not exist'); - } - - if (!fs.statSync(file).isFile()) { - throw new Error('The specified zip file is not a file'); - } - - await new Promise((resolve, reject) => { - yauzl.open(file, { lazyEntries: true }, (err, zipfile) => { - if (err) { - return reject(new Error(`Invalid zip file: ${err.message || err}`)); - } - - let idx = 0; - const total = zipfile.entryCount; - const abort = err => { - zipfile.removeListener('end', resolve); - zipfile.close(); - reject(err); - }; - - zipfile - .on('entry', async entry => { - idx++; - if (typeof params.onEntry === 'function') { - try { - await params.onEntry(entry.fileName, idx, total); - } catch (e) { - return reject(e); - } - } - - const fullPath = path.join(dest, entry.fileName); - const mode = (entry.externalFileAttributes >>> 16) || 0o644; - - const symlink = (mode & fs.constants.S_IFMT) === fs.constants.S_IFLNK; - let isDir = (mode & fs.constants.S_IFMT) === fs.constants.S_IFDIR; - - // check for Windows weird way of specifying a directory - // https://github.com/maxogden/extract-zip/issues/13#issuecomment-154494566 - const madeBy = entry.versionMadeBy >> 8; - if (!isDir) { - isDir = (madeBy === 0 && entry.externalFileAttributes === 16); - } - - if (symlink) { - await mkdir(path.dirname(fullPath), { recursive: true }); - zipfile.openReadStream(entry, (err, readStream) => { - if (err) { - return abort(err); - } - - const chunks = []; - readStream.on('data', chunk => chunks.push(chunk)); - readStream.on('error', abort); - readStream.on('end', () => { - let _str = Buffer.concat(chunks).toString('utf8'); - if (fs.existsSync(fullPath)) { - fs.unlinkSync(fullPath); - } - // Note: I have no idea why this is commented out - // fs.symlinkSync(str, fullPath); - zipfile.readEntry(); - }); - }); - } else if (isDir) { - await mkdir(fullPath, { recursive: true }); - zipfile.readEntry(); - } else { - await mkdir(path.dirname(fullPath), { recursive: true }); - zipfile.openReadStream(entry, (err, readStream) => { - if (err) { - return abort(err); - } - - const writeStream = fs.createWriteStream(fullPath, { - mode - }); - writeStream.on('close', () => zipfile.readEntry()); - writeStream.on('error', abort); - readStream.pipe(writeStream); - }); - } - }) - .once('end', resolve) - .readEntry(); - }); - }); -} diff --git a/src/util/jdk.js b/src/util/jdk.js index f1d431590..ae0465057 100644 --- a/src/util/jdk.js +++ b/src/util/jdk.js @@ -1,7 +1,7 @@ +import { expand } from './expand.js'; import { existsSync } from 'node:fs'; import { readdir, realpath } from 'node:fs/promises'; import { dirname, join, resolve } from 'node:path'; -import { expand } from './expand.js'; import which from 'which'; const exe = process.platform === 'win32' ? '.exe' : ''; @@ -22,7 +22,7 @@ export async function detect(config) { version: null, build: null, executables: executables, - issues: [] + issues: [], }; const { $ } = await import('execa'); @@ -60,10 +60,7 @@ export async function detect(config) { } } catch {} - const dirs = [ - '/Library/Java/JavaVirtualMachines', - '/System/Library/Java/JavaVirtualMachines' - ]; + const dirs = ['/Library/Java/JavaVirtualMachines', '/System/Library/Java/JavaVirtualMachines']; for (const jvmPath of dirs) { if (existsSync(jvmPath)) { for (const name of await readdir(jvmPath)) { @@ -89,75 +86,79 @@ export async function detect(config) { } } - await Promise.all(jdkPaths.map(async home => { - const jdk = { - home: home, - version: null, - build: null, - executables: {} - }; - const missingTools = []; - - for (const cmd of requiredTools) { - const p = join(home, `bin/${cmd}${exe}`); - if (existsSync(p)) { - jdk.executables[cmd] = await realpath(p); - } else { - missingTools.push(cmd); + await Promise.all( + jdkPaths.map(async (home) => { + const jdk = { + home: home, + version: null, + build: null, + executables: {}, + }; + const missingTools = []; + + for (const cmd of requiredTools) { + const p = join(home, `bin/${cmd}${exe}`); + if (existsSync(p)) { + jdk.executables[cmd] = await realpath(p); + } else { + missingTools.push(cmd); + } } - } - if (missingTools.length) { - results.issues.push({ - id: 'JDK_MISSING_PROGRAMS', - type: 'warning', - message: `JDK (Java Development Kit) at ${home} missing required programs: __${missingTools.join(', ')}__ -${process.env.JAVA_HOME + if (missingTools.length) { + results.issues.push({ + id: 'JDK_MISSING_PROGRAMS', + type: 'warning', + message: `JDK (Java Development Kit) at ${home} missing required programs: __${missingTools.join(', ')}__ +${ + process.env.JAVA_HOME ? `Please verify your __JAVA_HOME__ environment variable is correctly set to the JDK install location\n__JAVA_HOME__ is currently set to "${process.env.JAVA_HOME}".` - : 'Please set the __JAVA_HOME__ environment variable to the JDK install location and not the JRE (Java Runtime Environment).'} + : 'Please set the __JAVA_HOME__ environment variable to the JDK install location and not the JRE (Java Runtime Environment).' +} The __JAVA_HOME__ environment variable must point to the JDK and not the JRE (Java Runtime Environment). You may want to reinstall the JDK by downloading it from __https://www.oracle.com/java/technologies/downloads/__ -or __https://jdk.java.net/archive/__.` - }); - return; - } +or __https://jdk.java.net/archive/__.`, + }); + return; + } - let arch = '32bit'; - let result; - try { - result = await $`${jdk.executables.javac} -version -d64`; - arch = '64bit'; - } catch { - result = await $`${jdk.executables.javac} -version`; - } + let arch = '32bit'; + let result; + try { + result = await $`${jdk.executables.javac} -version -d64`; + arch = '64bit'; + } catch { + result = await $`${jdk.executables.javac} -version`; + } - const re = /^javac (.+?)(?:_(.+))?$/; - let m = result?.stderr?.trim().match(re) || result?.stdout?.trim().match(re); - if (m) { - let name = m[1]; - - jdk.architecture = arch; - jdk.version = m[1]; - jdk.build = m[2]; - - if (jdk.build) { - name += `_${jdk.build}`; - } else { - const { stderr } = await $`${jdk.executables.java} -version`; - m = stderr.trim().match(/\(build .+?\+(\d+(-[-a-zA-Z0-9.]+)?)\)/); - if (m) { - jdk.build = m[1]; - name += `_${m[1]}`; + const re = /^javac (.+?)(?:_(.+))?$/; + let m = result?.stderr?.trim().match(re) || result?.stdout?.trim().match(re); + if (m) { + let name = m[1]; + + jdk.architecture = arch; + jdk.version = m[1]; + jdk.build = m[2]; + + if (jdk.build) { + name += `_${jdk.build}`; + } else { + const { stderr } = await $`${jdk.executables.java} -version`; + m = stderr.trim().match(/\(build .+?\+(\d+(-[-a-zA-Z0-9.]+)?)\)/); + if (m) { + jdk.build = m[1]; + name += `_${m[1]}`; + } } - } - results.jdks[name] = jdk; + results.jdks[name] = jdk; - if (results.version === null) { - Object.assign(results, jdk); + if (results.version === null) { + Object.assign(results, jdk); + } } - } - })); + }) + ); if (results.version === null) { results.issues.push({ @@ -166,7 +167,7 @@ or __https://jdk.java.net/archive/__.` message: `JDK (Java Development Kit) not installed. If you already have installed the JDK, verify your __JAVA_HOME__ environment variable is correctly set. The JDK is required for Titanium and must be manually downloaded and installed from __https://www.oracle.com/java/technologies/downloads/__ -or __https://jdk.java.net/archive/__` +or __https://jdk.java.net/archive/__`, }); } @@ -188,29 +189,25 @@ function isJDK(dir) { 'lib/amd64/server/libjvm.so', 'jre/lib/amd64/client/libjvm.so', 'jre/lib/amd64/server/libjvm.so', - 'lib/server/libjvm.so' + 'lib/server/libjvm.so', ]; } else { libjvmLocations = [ 'lib/i386/client/libjvm.so', 'lib/i386/server/libjvm.so', 'jre/lib/i386/client/libjvm.so', - 'jre/lib/i386/server/libjvm.so' + 'jre/lib/i386/server/libjvm.so', ]; } } else if (process.platform === 'darwin') { libjvmLocations = [ 'jre/lib/server/libjvm.dylib', '../Libraries/libjvm.dylib', - 'lib/server/libjvm.dylib' + 'lib/server/libjvm.dylib', ]; } else if (process.platform === 'win32') { - libjvmLocations = [ - 'jre/bin/server/jvm.dll', - 'jre/bin/client/jvm.dll', - 'bin/server/jvm.dll' - ]; + libjvmLocations = ['jre/bin/server/jvm.dll', 'jre/bin/client/jvm.dll', 'bin/server/jvm.dll']; } - return libjvmLocations.some(p => existsSync(resolve(dir, p))); + return libjvmLocations.some((p) => existsSync(resolve(dir, p))); } diff --git a/src/util/logger.js b/src/util/logger.js index d703c8b6e..08c9c749a 100644 --- a/src/util/logger.js +++ b/src/util/logger.js @@ -20,7 +20,7 @@ export class Logger extends EventEmitter { debug: 2, info: 3, warn: 4, - error: 5 + error: 5, // log: 9 // silent: 10 }; @@ -52,7 +52,11 @@ export class Logger extends EventEmitter { }; this.warn = (msg = '', ...args) => { - this.render(4, this.stderr, `${yellow(`[WARN] ${format(msg, ...args).replace(ansiRE, '')}`)}`); + this.render( + 4, + this.stderr, + `${yellow(`[WARN] ${format(msg, ...args).replace(ansiRE, '')}`)}` + ); }; this.error = (msg = '', ...args) => { @@ -104,9 +108,7 @@ export class Logger extends EventEmitter { } setBanner({ name, copyright, version, sdkVersion }) { - this.#banner = `${bold(cyan(name))} v${version}${ - sdkVersion ? ` SDK v${sdkVersion}` : '' - } + this.#banner = `${bold(cyan(name))} v${version}${sdkVersion ? ` SDK v${sdkVersion}` : ''} ${copyright} Please star us on GitHub! ${cyan('https://github.com/tidev/titanium-cli')} diff --git a/src/util/progress.js b/src/util/progress.js index 18c26af34..85fa11dce 100644 --- a/src/util/progress.js +++ b/src/util/progress.js @@ -9,7 +9,7 @@ export class ProgressBar { this.chars = { complete: opts.complete || '=', - incomplete: opts.incomplete || '-' + incomplete: opts.incomplete || '-', }; } @@ -35,7 +35,7 @@ export class ProgressBar { return; } - const percent = this.curr / this.total * 100; + const percent = (this.curr / this.total) * 100; let complete = Math.round(this.width * (this.curr / this.total)); let incomplete = this.width - complete; const elapsed = new Date() - this.start; diff --git a/src/util/prompt.js b/src/util/prompt.js index 45f6bb9fc..a45436c8d 100644 --- a/src/util/prompt.js +++ b/src/util/prompt.js @@ -13,7 +13,7 @@ export async function prompt(opts) { const { value } = await prompt({ ...opts, - name: 'value' + name: 'value', }); if (value === undefined) { // sigint diff --git a/src/util/proxy.js b/src/util/proxy.js index 73261eb5d..587627e0d 100644 --- a/src/util/proxy.js +++ b/src/util/proxy.js @@ -22,11 +22,9 @@ export async function detect() { } } catch {} } - } else if (process.env.https_proxy !== undefined) { // if both configured, https proxy is preferentially returned return parseEnv(process.env.https_proxy); - } else if (process.env.http_proxy !== undefined) { return parseEnv(process.env.http_proxy); } @@ -35,13 +33,17 @@ export async function detect() { } function parseNetSetup(str) { - const m = str.replace(/\n/g, '').match(/Enabled: YesServer: ((?:http|https)+:\/\/.*)Port: (\d*)Authenticated Proxy Enabled: (\S*)/); + const m = str + .replace(/\n/g, '') + .match( + /Enabled: YesServer: ((?:http|https)+:\/\/.*)Port: (\d*)Authenticated Proxy Enabled: (\S*)/ + ); return { valid: !!m, server: m?.[1] || '', port: m?.[2] || '', fullAddress: `${m?.[1] || ''}${m?.[2] ? `:${m[2]}` : ''}`, - authenticated: m?.[3] || '' + authenticated: m?.[3] || '', }; } @@ -53,7 +55,7 @@ function parseEnv(env) { server: url.hostname, port: url.port, fullAddress: url.href, - authenticated: false + authenticated: false, }; } catch {} } diff --git a/src/util/setup-screens.js b/src/util/setup-screens.js index ec8890d68..a459d24ff 100644 --- a/src/util/setup-screens.js +++ b/src/util/setup-screens.js @@ -1,13 +1,13 @@ -import { detect as proxyDetect } from './proxy.js'; -import { prompt } from './prompt.js'; -import chalk from 'chalk'; -import { expand } from './expand.js'; -import { existsSync, unlinkSync, utimesSync, writeFileSync } from 'node:fs'; import { BusyIndicator } from './busyindicator.js'; import { detect } from './detect.js'; -import { request, version } from 'node-titanium-sdk/util'; +import { expand } from './expand.js'; +import { prompt } from './prompt.js'; +import { detect as proxyDetect } from './proxy.js'; +import chalk from 'chalk'; import { detectTitaniumSDKs, getTitaniumReleases } from 'node-titanium-sdk/titanium'; +import { request, version } from 'node-titanium-sdk/util'; import dns from 'node:dns/promises'; +import { existsSync, unlinkSync, utimesSync, writeFileSync } from 'node:fs'; import os from 'node:os'; import { join } from 'node:path'; @@ -19,36 +19,36 @@ export class SetupScreens { screens = { quick: { label: '__q__uick', - desc: 'Quick Setup' + desc: 'Quick Setup', }, check: { label: 'chec__k__', - desc: 'Check Environment' + desc: 'Check Environment', }, user: { label: '__u__ser', - desc: 'User Information' + desc: 'User Information', }, app: { label: 'a__p__p', - desc: 'New App Defaults' + desc: 'New App Defaults', }, network: { label: '__n__etwork', - desc: 'Network Settings' + desc: 'Network Settings', }, cli: { label: '__c__li', - desc: 'Titanium CLI Settings' + desc: 'Titanium CLI Settings', }, android: { label: '__a__ndroid', - desc: 'Android Settings' + desc: 'Android Settings', }, ios: { label: '__i__os', - desc: 'iOS Settings' - } + desc: 'iOS Settings', + }, }; constructor(logger, config, cli) { @@ -73,39 +73,41 @@ export class SetupScreens { } async mainmenuScreen() { - const screens = Object.keys(this.screens).filter(name => name !== 'ios' || process.platform === 'darwin'); + const screens = Object.keys(this.screens).filter( + (name) => name !== 'ios' || process.platform === 'darwin' + ); const lookup = { [screens.length + 1]: 'exit', exit: 'exit', - x: 'exit' + x: 'exit', }; this.logger.log( - screenTitle('Main Menu') + '\n' + - screens - .map((name, i) => { - const { label, desc } = this.screens[name]; - const padding = 7 - (label.length - 4); - const title = cyan( - label.replace(/__(.+)__/, (_s, char) => { - lookup[char] = name; - return bold(char); - }) + - (padding > 0 ? ' '.repeat(padding) : '') - ); - lookup[name] = lookup[i + 1] = name; - return `${String(i + 1).padStart(4)}) ${title} ${desc}`; - }) - .join('\n') + - `\n${String(screens.length + 1).padStart(4)}) ${cyan( - 'e__x__it'.replace(/__(.+)__/, (_s, char) => bold(char))) - } Exit` + screenTitle('Main Menu') + + '\n' + + screens + .map((name, i) => { + const { label, desc } = this.screens[name]; + const padding = 7 - (label.length - 4); + const title = cyan( + label.replace(/__(.+)__/, (_s, char) => { + lookup[char] = name; + return bold(char); + }) + (padding > 0 ? ' '.repeat(padding) : '') + ); + lookup[name] = lookup[i + 1] = name; + return `${String(i + 1).padStart(4)}) ${title} ${desc}`; + }) + .join('\n') + + `\n${String(screens.length + 1).padStart(4)}) ${cyan( + 'e__x__it'.replace(/__(.+)__/, (_s, char) => bold(char)) + )} Exit` ); const value = await prompt({ type: 'text', - message: 'Where do you want to go?' + message: 'Where do you want to go?', }); const next = lookup[value]; @@ -133,14 +135,14 @@ export class SetupScreens { type: 'text', message: 'What do you want as your "author" name?', initial: this.config.get('user.name', ''), - name: 'name' + name: 'name', }, { type: 'text', message: 'Path to your workspace where your projects should be created:', initial: this.config.get('app.workspace', ''), name: 'workspace', - validate: value => { + validate: (value) => { if (!value) { return 'Please specify a workspace directory'; } @@ -149,7 +151,7 @@ export class SetupScreens { return 'Specified workspace directory does not exist'; } return true; - } + }, }, { type: 'toggle', @@ -157,14 +159,14 @@ export class SetupScreens { initial: true, name: 'usingAndroid', active: 'yes', - inactive: 'no' + inactive: 'no', }, { - type: prev => prev ? 'text' : null, + type: (prev) => (prev ? 'text' : null), message: 'Path to the Android SDK', initial: this.config.get('android.sdkPath', data?.android?.sdk?.path), name: 'androidSdkPath', - validate: value => { + validate: (value) => { if (!value) { return 'Please specify the Android SDK directory'; } @@ -175,13 +177,17 @@ export class SetupScreens { if (process.platform === 'win32' && value.includes('&')) { return 'The Android SDK path must not contain ampersands (&) on Windows'; } - const adbExecutable = join(value, 'platform-tools', 'adb' + (process.platform === 'win32' ? '.exe' : '')); + const adbExecutable = join( + value, + 'platform-tools', + 'adb' + (process.platform === 'win32' ? '.exe' : '') + ); if (!existsSync(adbExecutable)) { return 'Invalid Android SDK path: adb not found'; } return true; - } - } + }, + }, ]); this.config.set('user.name', values.name); @@ -210,19 +216,21 @@ export class SetupScreens { try { ({ data } = await detect(this.cli.debugLogger, this.config, this.cli, { all: true })); - data.titaniumCLI.latest = await request('https://registry.npmjs.org/-/package/titanium/dist-tags') - .then(res => res.body.json()) - .then(r => r.latest); + data.titaniumCLI.latest = await request( + 'https://registry.npmjs.org/-/package/titanium/dist-tags' + ) + .then((res) => res.body.json()) + .then((r) => r.latest); data.titaniumSDK = { installed: await detectTitaniumSDKs(this.config), - latest: online && (await getTitaniumReleases())?.[0] || null + latest: (online && (await getTitaniumReleases())?.[0]) || null, }; data.network = { online, proxy: this.config.get('cli.httpProxyServer'), - test: !!data.titaniumSDK.latest?.name + test: !!data.titaniumSDK.latest?.name, }; } finally { busy.stop(); @@ -234,19 +242,29 @@ export class SetupScreens { const starmark = '\u2605'; const xmark = '\u2715'; const ok = (label, status, extra) => { - log(` ${green(checkmark)} ${label.padEnd(labelPadding)} ${status ? green(status) : ''}${extra ? gray(` ${extra}`) : ''}`); + log( + ` ${green(checkmark)} ${label.padEnd(labelPadding)} ${status ? green(status) : ''}${extra ? gray(` ${extra}`) : ''}` + ); }; const warn = (label, status, extra) => { - log(` ${bold(yellow('!'))} ${label.padEnd(labelPadding)} ${status ? yellow(status) : ''}${extra ? gray(` ${extra}`) : ''}`); + log( + ` ${bold(yellow('!'))} ${label.padEnd(labelPadding)} ${status ? yellow(status) : ''}${extra ? gray(` ${extra}`) : ''}` + ); }; const bad = (label, status, extra) => { - log(` ${red(xmark)} ${label.padEnd(labelPadding)} ${status ? red(status) : ''}${extra ? gray(` ${extra}`) : ''}`); + log( + ` ${red(xmark)} ${label.padEnd(labelPadding)} ${status ? red(status) : ''}${extra ? gray(` ${extra}`) : ''}` + ); }; const update = (label, status, extra) => { - log(` ${magenta(starmark)} ${label.padEnd(labelPadding)} ${status ? magenta(status) : ''}${extra ? gray(` ${extra}`) : ''}`); + log( + ` ${magenta(starmark)} ${label.padEnd(labelPadding)} ${status ? magenta(status) : ''}${extra ? gray(` ${extra}`) : ''}` + ); }; const note = (label, status, extra) => { - log(` ${bold(gray('-'))} ${label.padEnd(labelPadding)} ${status ? gray(status) : ''}${extra ? gray(` ${extra}`) : ''}`); + log( + ` ${bold(gray('-'))} ${label.padEnd(labelPadding)} ${status ? gray(status) : ''}${extra ? gray(` ${extra}`) : ''}` + ); }; log('Node.js'); @@ -262,7 +280,11 @@ export class SetupScreens { } else if (version.gt(data.titaniumCLI.version, data.titaniumCLI.latest)) { ok('cli', 'bleeding edge', `(v${data.titaniumCLI.version})`); } else { - update('cli', `new version v${data.titaniumCLI.latest} available`, `(currently v${data.titaniumCLI.version})`); + update( + 'cli', + `new version v${data.titaniumCLI.latest} available`, + `(currently v${data.titaniumCLI.version})` + ); } log(); @@ -271,7 +293,9 @@ export class SetupScreens { note('latest sdk', 'unknown (offline)'); } else if (!data.titaniumSDK.installed.sdks.length) { bad('latest sdk', 'no Titanium SDKs found'); - } else if (data.titaniumSDK.installed.sdks.find(s => s.name === data.titaniumSDK.latest.name)) { + } else if ( + data.titaniumSDK.installed.sdks.find((s) => s.name === data.titaniumSDK.latest.name) + ) { ok('latest sdk', 'installed', `(v${data.titaniumSDK.latest.name})`); } else { update('latest sdk', `new version v${data.titaniumSDK.latest.name} available!`); @@ -296,14 +320,15 @@ export class SetupScreens { const len = distPPLabel.length; if (Object.keys(data.ios.xcode).length) { - ok('Xcode'.padEnd(len), 'installed', `(${ - Object - .keys(data.ios.xcode) - .filter(ver => ver !== '__selected__') - .map(ver => data.ios.xcode[ver].version) + ok( + 'Xcode'.padEnd(len), + 'installed', + `(${Object.keys(data.ios.xcode) + .filter((ver) => ver !== '__selected__') + .map((ver) => data.ios.xcode[ver].version) .sort() - .join(', ') - })`); + .join(', ')})` + ); const iosSdks = {}; for (const ver of Object.keys(data.ios.xcode)) { @@ -361,7 +386,7 @@ export class SetupScreens { warn('distribution cert'.padEnd(len), 'not found'); } - const devPP = data.ios.provisioning.development.filter(i => { + const devPP = data.ios.provisioning.development.filter((i) => { return !Object.hasOwn(i, 'expired') || i.expired === false; }).length; if (devPP) { @@ -370,13 +395,16 @@ export class SetupScreens { warn('dev provisioning'.padEnd(len), 'not found'); } - const distPP = data.ios.provisioning.distribution.filter(i => { - return !Object.hasOwn(i, 'expired') || i.expired === false; - }).length + data.ios.provisioning.adhoc.filter(i => { - return !Object.hasOwn(i, 'expired') || i.expired === false; - }).length + data.ios.provisioning.enterprise.filter(i => { - return !Object.hasOwn(i, 'expired') || i.expired === false; - }).length; + const distPP = + data.ios.provisioning.distribution.filter((i) => { + return !Object.hasOwn(i, 'expired') || i.expired === false; + }).length + + data.ios.provisioning.adhoc.filter((i) => { + return !Object.hasOwn(i, 'expired') || i.expired === false; + }).length + + data.ios.provisioning.enterprise.filter((i) => { + return !Object.hasOwn(i, 'expired') || i.expired === false; + }).length; if (distPP) { ok(distPPLabel, 'installed', `(${distPP} found)`); } else { @@ -396,7 +424,10 @@ export class SetupScreens { if (data.android.sdk.platformTools && data.android.sdk.platformTools.path) { if (data.android.sdk.platformTools.supported === 'maybe') { - warn('platform tools', `untested version ${data.android.sdk.platformTools.version}; may or may not work`); + warn( + 'platform tools', + `untested version ${data.android.sdk.platformTools.version}; may or may not work` + ); } else if (data.android.sdk.platformTools.supported) { ok('platform tools', 'installed', `(v${data.android.sdk.platformTools.version})`); } else { @@ -406,7 +437,10 @@ export class SetupScreens { if (data.android.sdk.buildTools && data.android.sdk.buildTools.path) { if (data.android.sdk.buildTools.supported === 'maybe') { - warn('build tools', `untested version ${data.android.sdk.buildTools.version}; may or may not work`); + warn( + 'build tools', + `untested version ${data.android.sdk.buildTools.version}; may or may not work` + ); } else if (data.android.sdk.buildTools.supported) { ok('build tools', 'installed', `(v${data.android.sdk.buildTools.version})`); } else { @@ -510,7 +544,7 @@ export class SetupScreens { ['~/.titanium', 'titanium config directory'], [this.cli.env.installPath, 'titanium sdk install directory'], [this.config.get('app.workspace'), 'workspace directory'], - [os.tmpdir(), 'temp directory'] + [os.tmpdir(), 'temp directory'], ]; for (let [dir, desc] of dirs) { if (dir) { @@ -535,7 +569,7 @@ export class SetupScreens { type: 'text', message: 'What do you want as your "author" name?', initial: this.config.get('user.name', ''), - name: 'name' + name: 'name', }); if (name) { @@ -554,7 +588,7 @@ export class SetupScreens { message: 'Path to your workspace where your projects should be created:', initial: this.config.get('app.workspace', ''), name: 'workspace', - validate: value => { + validate: (value) => { if (!value) { return 'Please specify a workspace directory'; } @@ -563,26 +597,26 @@ export class SetupScreens { return 'Specified workspace directory does not exist'; } return true; - } + }, }, { type: 'text', message: 'What is your prefix for application IDs? (example: com.mycompany)', initial: this.config.get('app.idprefix'), - name: 'idprefix' + name: 'idprefix', }, { type: 'text', message: 'What is the name of your organization to use as the "publisher"?', initial: this.config.get('app.publisher'), - name: 'publisher' + name: 'publisher', }, { type: 'text', message: 'What is the URL of your organization?', initial: this.config.get('app.url'), - name: 'url' - } + name: 'url', + }, ]); this.config.set('app.workspace', values.workspace); @@ -613,14 +647,14 @@ export class SetupScreens { initial: !!this.config.get('cli.httpProxyServer'), name: 'hasProxy', active: 'yes', - inactive: 'no' + inactive: 'no', }, { - type: prev => prev ? 'text' : null, + type: (prev) => (prev ? 'text' : null), message: 'Proxy server URL', initial: defaultProxy, name: 'httpProxyServer', - validate: value => { + validate: (value) => { try { const u = new URL(value); if (!/^https?:$/.test(u.protocol)) { @@ -633,7 +667,7 @@ export class SetupScreens { } catch (e) { return e.message; } - } + }, }, { type: 'toggle', @@ -641,8 +675,8 @@ export class SetupScreens { initial: !!this.config.get('cli.rejectUnauthorized'), name: 'rejectUnauthorized', active: 'yes', - inactive: 'no' - } + inactive: 'no', + }, ]); this.config.set('cli.httpProxyServer', values.hasProxy ? values.httpProxyServer : ''); @@ -663,7 +697,7 @@ export class SetupScreens { initial: this.config.get('cli.colors', true), name: 'colors', active: 'yes', - inactive: 'no' + inactive: 'no', }, { type: 'toggle', @@ -671,7 +705,7 @@ export class SetupScreens { initial: this.config.get('cli.prompt', true), name: 'prompt', active: 'yes', - inactive: 'no' + inactive: 'no', }, { type: 'toggle', @@ -679,29 +713,32 @@ export class SetupScreens { initial: this.config.get('cli.progressBars', true), name: 'progressBars', active: 'yes', - inactive: 'no' + inactive: 'no', }, { type: 'select', message: 'Output log level', initial: logLevels.indexOf(this.config.get('cli.logLevel', 'info')), name: 'logLevel', - choices: this.logger.getLevels().reverse().map(level => { - return { - title: level, - value: level - }; - }) + choices: this.logger + .getLevels() + .reverse() + .map((level) => { + return { + title: level, + value: level, + }; + }), }, { type: 'number', message: 'What is the width of the Titanium CLI output?', initial: this.config.get('cli.width', 80), name: 'width', - validate: value => { + validate: (value) => { return value !== '' && value < 1 ? 'Please enter a positive number' : true; - } - } + }, + }, ]); this.logger.setLevel(values.logLevel); @@ -734,7 +771,7 @@ export class SetupScreens { message: 'Path to the Android SDK', initial: this.config.get('android.sdkPath', data?.android?.sdk?.path), name: 'androidSdkPath', - validate: value => { + validate: (value) => { if (!value) { return 'Please specify the Android SDK directory'; } @@ -745,12 +782,16 @@ export class SetupScreens { if (process.platform === 'win32' && value.includes('&')) { return 'The Android SDK path must not contain ampersands (&) on Windows'; } - const adbExecutable = join(value, 'platform-tools', `adb${process.platform === 'win32' ? '.exe' : ''}`); + const adbExecutable = join( + value, + 'platform-tools', + `adb${process.platform === 'win32' ? '.exe' : ''}` + ); if (!existsSync(adbExecutable)) { return 'Invalid Android SDK path: adb not found'; } return true; - } + }, }, { type: 'toggle', @@ -758,14 +799,14 @@ export class SetupScreens { initial: false, name: 'modules', active: 'yes', - inactive: 'no' + inactive: 'no', }, { - type: prev => prev ? 'text' : null, + type: (prev) => (prev ? 'text' : null), message: 'Path to the Android NDK', initial: this.config.get('android.ndkPath', data?.android?.ndk?.path), name: 'androidNdkPath', - validate: value => { + validate: (value) => { if (!value) { return 'Please specify the Android NDK directory'; } @@ -773,13 +814,16 @@ export class SetupScreens { if (!existsSync(value)) { return 'Specified Android NDK directory does not exist'; } - const ndkbuildExecutable = join(value, `ndk-build${process.platform === 'win32' ? '.cmd' : ''}`); + const ndkbuildExecutable = join( + value, + `ndk-build${process.platform === 'win32' ? '.cmd' : ''}` + ); if (!existsSync(ndkbuildExecutable)) { return 'Invalid Android NDK path: ndk-build not found'; } return true; - } - } + }, + }, ]); if (values.androidSdkPath !== undefined) { @@ -849,33 +893,30 @@ export class SetupScreens { initial: currentDevName, choices: devList .sort((a, b) => a.name.localeCompare(b.name)) - .map(dev => ({ - title: `${dev.name}${ - dev.expired ? ` ${red('**EXPIRED**')}` : '' - }${ + .map((dev) => ({ + title: `${dev.name}${dev.expired ? ` ${red('**EXPIRED**')}` : ''}${ dev.invalid ? ` ${red('**NOT VALID**')}` : '' }`, - value: dev.name - })) + value: dev.name, + })), }); } if (distList.length) { questions.push({ type: 'select', - message: 'What do you want to be your default iOS distribution cert for App Store and Ad Hoc builds?', + message: + 'What do you want to be your default iOS distribution cert for App Store and Ad Hoc builds?', name: 'distributionName', initial: currentDistName, choices: devList .sort((a, b) => a.name.localeCompare(b.name)) - .map(dev => ({ - title: `${dev.name}${ - dev.expired ? ` ${red('**EXPIRED**')}` : '' - }${ + .map((dev) => ({ + title: `${dev.name}${dev.expired ? ` ${red('**EXPIRED**')}` : ''}${ dev.invalid ? ` ${red('**NOT VALID**')}` : '' }`, - value: dev.name - })) + value: dev.name, + })), }); } @@ -922,11 +963,7 @@ function screenTitle(title) { const margin = width - title.length + 4; const pad = Math.floor(margin / 2); - return `\n${ - gray('┤ '.padStart(pad + 1, '─')) - }${ - bold(title) - }${ - gray(' ├'.padEnd(margin - pad + 1, '─')) - }\n`; + return `\n${gray('┤ '.padStart(pad + 1, '─'))}${bold(title)}${gray( + ' ├'.padEnd(margin - pad + 1, '─') + )}\n`; } diff --git a/src/util/suggest.js b/src/util/suggest.js deleted file mode 100644 index 455d7908b..000000000 --- a/src/util/suggest.js +++ /dev/null @@ -1,43 +0,0 @@ -import chalk from 'chalk'; - -const { cyan } = chalk; - -export function suggest(value, choices, threshold = 3) { - value = `${value}`; - - const suggestions = choices.filter(choice => { - return choice.startsWith(value) || levenshtein(value, choice) <= threshold; - }); - - return suggestions.length - ? `Did you mean this?\n${suggestions.map(s => ` ${cyan(s)}`).join('\n')}\n\n` - : ''; -} - -/** - * Measures the distance between two strings. - * @param {String} s - The first string - * @param {String} c - The second string - * @returns {Number} The distance - */ -function levenshtein(s, c) { - var len1 = (s = s.split('')).length, - len2 = (c = c.split('')).length, - a = [], - i = len1 + 1, - j; - - for (; i; a[--i] = [i]) { - // - } - // eslint-disable-next-line no-cond-assign - for (i = len2 + 1; a[0][--i] = i;) { - // - } - for (i = -1; ++i < len1;) { - for (j = -1; ++j < len2;) { - a[i + 1][j + 1] = Math.min(a[i][j + 1] + 1, a[i + 1][j] + 1, a[i][j] + (s[i] != c[j])); - } - } - return a[len1][len2]; -} diff --git a/src/util/ticonfig.js b/src/util/ticonfig.js index bc2661101..26cb5cef6 100644 --- a/src/util/ticonfig.js +++ b/src/util/ticonfig.js @@ -1,14 +1,14 @@ -import { join } from 'node:path'; -import fs from 'fs-extra'; import { expand } from './expand.js'; import { TiError } from './tierror.js'; +import fs from 'fs-extra'; +import { join } from 'node:path'; export class TiConfig { #configFile = ''; #defaults = { app: { - workspace: '.' + workspace: '.', }, cli: { @@ -16,12 +16,13 @@ export class TiConfig { completion: false, httpProxyServer: '', ignoreDirs: '^(\\.svn|_svn|\\.git|\\.hg|\\.?[Cc][Vv][Ss]|\\.bzr|\\$RECYCLE\\.BIN)$', - ignoreFiles: '^(\\.gitignore|\\.npmignore|\\.cvsignore|\\.DS_Store|\\._.*|[Tt]humbs.db|\\.vspscc|\\.vssscc|\\.sublime-project|\\.sublime-workspace|\\.project|\\.tmproj)$', + ignoreFiles: + '^(\\.gitignore|\\.npmignore|\\.cvsignore|\\.DS_Store|\\._.*|[Tt]humbs.db|\\.vspscc|\\.vssscc|\\.sublime-project|\\.sublime-workspace|\\.project|\\.tmproj)$', logLevel: 'trace', progressBars: true, prompt: true, rejectUnauthorized: true, - width: 80 + width: 80, }, // additional search paths for commands and hooks @@ -29,10 +30,10 @@ export class TiConfig { hooks: [], modules: [], sdks: [], - templates: [] + templates: [], }, - user: {} + user: {}, }; #titaniumConfigFolder = ''; @@ -158,7 +159,7 @@ export class TiConfig { fs.renameSync(tmpFile, this.#configFile); } catch { throw new TiError(`Unable to write config file ${this.#configFile}`, { - after: 'Please ensure the Titanium CLI has access to modify this file' + after: 'Please ensure the Titanium CLI has access to modify this file', }); } } diff --git a/src/util/tihelp.js b/src/util/tihelp.js index 4518da381..86e8ec35d 100644 --- a/src/util/tihelp.js +++ b/src/util/tihelp.js @@ -1,7 +1,7 @@ -import { Command, Help } from 'commander'; import { applyCommandConfig } from './apply-command-config.js'; -import chalk from 'chalk'; import { capitalize } from './capitalize.js'; +import chalk from 'chalk'; +import { Command, Help } from 'commander'; const { cyan, gray } = chalk; @@ -26,7 +26,8 @@ export class TiHelp extends Help { const cmd = new Command(type); cmd.helpOption(false); applyCommandConfig(cli, type, cmd, conf); - const title = `${cli.command.conf.title || cli.command.module?.title || ''} --type=${type}`.trim(); + const title = + `${cli.command.conf.title || cli.command.module?.title || ''} --type=${type}`.trim(); this.typeCmds[title] = cmd; } } @@ -35,7 +36,7 @@ export class TiHelp extends Help { padWidth(cmd, helper) { return Math.max( helper.longestOptionTermLength(cmd, helper), - ...Object.values(this.platformCmds).map(cmd => helper.longestOptionTermLength(cmd, helper)), + ...Object.values(this.platformCmds).map((cmd) => helper.longestOptionTermLength(cmd, helper)), helper.longestGlobalOptionTermLength(cmd, helper), helper.longestSubcommandTermLength(cmd, helper), helper.longestArgumentTermLength(cmd, helper) @@ -45,10 +46,14 @@ export class TiHelp extends Help { argumentDescription(argument) { const extraInfo = []; if (argument.argChoices) { - extraInfo.push(`choices: ${argument.argChoices.map((choice) => JSON.stringify(choice)).join(', ')}`); + extraInfo.push( + `choices: ${argument.argChoices.map((choice) => JSON.stringify(choice)).join(', ')}` + ); } if (argument.defaultValue !== undefined) { - extraInfo.push(`default: ${argument.defaultValueDescription || JSON.stringify(argument.defaultValue)}`); + extraInfo.push( + `default: ${argument.defaultValueDescription || JSON.stringify(argument.defaultValue)}` + ); } if (extraInfo.length > 0) { const extraDescription = gray(`(${extraInfo.join(', ')})`); @@ -64,13 +69,19 @@ export class TiHelp extends Help { const extraInfo = []; if (option.argChoices) { - extraInfo.push(`choices: ${option.argChoices.map((choice) => JSON.stringify(choice)).join(', ')}`); + extraInfo.push( + `choices: ${option.argChoices.map((choice) => JSON.stringify(choice)).join(', ')}` + ); } if (option.defaultValue !== undefined) { - const showDefault = option.required || option.optional || + const showDefault = + option.required || + option.optional || (option.isBoolean() && typeof option.defaultValue === 'boolean'); if (showDefault) { - extraInfo.push(`default: ${option.defaultValueDescription || JSON.stringify(option.defaultValue)}`); + extraInfo.push( + `default: ${option.defaultValueDescription || JSON.stringify(option.defaultValue)}` + ); } } if (option.presetArg !== undefined && option.optional) { @@ -95,7 +106,11 @@ export class TiHelp extends Help { function formatItem(term, description) { if (description) { const fullText = `${cyan(term.padEnd(termWidth + itemSeparatorWidth))}${description}`; - return helper.boxWrap(fullText, helpWidth - itemIndentWidth, termWidth + itemSeparatorWidth); + return helper.boxWrap( + fullText, + helpWidth - itemIndentWidth, + termWidth + itemSeparatorWidth + ); } return cyan(term); } @@ -126,7 +141,11 @@ export class TiHelp extends Help { return formatItem(helper.argumentTerm(argument), helper.argumentDescription(argument)); }); if (argumentList.length > 0) { - output = output.concat([`${cmd.title || capitalize(cmd.name())} Arguments:`, formatList(argumentList), '']); + output = output.concat([ + `${cmd.title || capitalize(cmd.name())} Arguments:`, + formatList(argumentList), + '', + ]); } // Options @@ -134,7 +153,11 @@ export class TiHelp extends Help { return formatItem(helper.optionTerm(option), helper.optionDescription(option)); }); if (optionList.length > 0) { - output = output.concat([`${cmd.title || capitalize(cmd.name())} Options:`, formatList(optionList), '']); + output = output.concat([ + `${cmd.title || capitalize(cmd.name())} Options:`, + formatList(optionList), + '', + ]); } for (const [title, platformCmd] of Object.entries(this.platformCmds)) { diff --git a/src/util/timodule.js b/src/util/timodule.js deleted file mode 100644 index a7f485b77..000000000 --- a/src/util/timodule.js +++ /dev/null @@ -1,264 +0,0 @@ -import { arrayify } from './arrayify.js'; -import { basename, dirname, join, resolve } from 'node:path'; -import { existsSync, unlinkSync } from 'node:fs'; -import { readdir, readFile, stat } from 'node:fs/promises'; -import chalk from 'chalk'; -import * as version from './version.js'; -import { extractZip } from './extract-zip.js'; - -const { cyan } = chalk; - -const platformAliases = { - ipad: 'ios', - iphone: 'ios', -}; - -/** - * Scans search paths for Titanium modules. This function will not scan any paths - * other than the ones explicitly told to scan. - * - * @param {Object} searchPaths - An object of scopes to arrays of paths to search for Titanium modules. - * @param {Object} config - The CLI config. - * @param {Object} [logger] - A logger instance. - * @returns {Promise} - */ -export async function detect(searchPaths, config, logger) { - const results = {}; - - if (searchPaths && typeof searchPaths === 'object') { - for (let [scope, paths] of Object.entries(searchPaths)) { - results[scope] = {}; - for (const searchPath of arrayify(paths, true)) { - results[scope] = await detectModules(searchPath, config, logger); - } - } - } - - return results; -} - -/** - * Searches a directory for Titanium modules. If it encounters a zip file - * that matches module zip filename pattern, it will automatically unzip it and - * remove the zip file prior to detecting modules. - * - * @param {String} modulesDir - A path/dir to search for Titanium modules. - * @param {Object} config - The CLI config. - * @param {Object} [logger] - A logger instance. - * @returns {Promise} - */ -async function detectModules(modulesDir, config, logger) { - const moduleRoot = resolve(modulesDir, '..'); - - // make sure the module's parent dir (the root) exists - if (!existsSync(moduleRoot)) { - return {}; - } - - // auto-unzip zipped modules if we find them - const fileNames = await readdir(moduleRoot); - await Promise.all(fileNames.map(name => unzipIfNecessary(moduleRoot, name, logger))); - - if (!existsSync(modulesDir)) { - return {}; - } - - logger?.trace(`Detecting modules in ${cyan(modulesDir)}`); - - const ignoreDirs = new RegExp(config.get('cli.ignoreDirs', '^(.svn|.git|.hg|.?[Cc][Vv][Ss]|.bzr)$')); - const osNamesRegExp = /^osx|win32|linux$/; - - const subdirs = await readdir(modulesDir); - const modules = await Promise.all(subdirs.map(platform => { - return detectPlatformModules(modulesDir, platform, osNamesRegExp, ignoreDirs, logger); - })); - - return convertArrayOfModulesToHierarchy(modules.flat()); -} - -/** - * Automatically extracts a module zipfile if detect in module root dir. - * @param {string} moduleRoot root directory where we store modules (parent of "modules" dir) - * @param {string} name basename of zip file - * @param {object} [logger] optional logger object - * @return {Promise} - * @private - */ -async function unzipIfNecessary(moduleRoot, name, logger) { - const zipRegExp = /^.+-.+?-.+?\.zip$/; - const file = join(moduleRoot, name); - - if (!zipRegExp.test(name)) { - return; - } - - try { - logger?.log(`Installing module: ${cyan(name)}`); - await extractZip({ - file, - dest: moduleRoot - }); - unlinkSync(file); - } catch (e) { - logger?.error(`Failed to install module: ${e.message}`); - } -} - -/** - * @param {string} modulesDir e.g. '~/Library/APplication Support/Titanium/modules' - * @param {string} platform e.g. 'android' or 'iphone' - * @param {RegExp} osNamesRegExp regexp used to skip certain folder names like 'win32' or 'osx' - * @param {RegExp} ignoreDirs additional regexp used to filter directories - * @param {object} [logger] optional logger object - * @returns {Promise} - * @private - */ -async function detectPlatformModules(modulesDir, platform, osNamesRegExp, ignoreDirs, logger) { - if (osNamesRegExp.test(platform) || ignoreDirs.test(platform)) { - return []; - } - - const platformDir = join(modulesDir, platform); - try { - const st = await stat(platformDir); - if (!st.isDirectory()) { - return []; - } - } catch { - // ignore if can't stat dir - return []; - } - // ok, it's a valid platform dir! - - const moduleNameDirs = await readdir(platformDir); - // here we gather modules per-platform, which gives us object[] for each, so use of Promise.all gives us - // an array of object[], so we need to flatten it once gathered - const modules = await Promise.all(moduleNameDirs.map(moduleName => { - return detectModulesByPlatformAndName(platformDir, moduleName, ignoreDirs, logger); - })); - - return modules.flat(); -} - -/** - * @param {string} platformModulesDir e.g. '~/Library/Application Support/Titanium/modules/android' - * @param {string} moduleName e.g. 'hyperloop' - * @param {RegExp} ignoreDirs regexp used to filter directories traversed - * @param {object} [logger] optional logger object - * @returns {Promise} - * @private - */ -async function detectModulesByPlatformAndName(platformModulesDir, moduleName, ignoreDirs, logger) { - if (ignoreDirs.test(moduleName)) { - return []; - } - - // loop through module names - const modulePath = join(platformModulesDir, moduleName); - try { - const st = await stat(modulePath); - if (!st.isDirectory()) { - return []; - } - } catch { - return []; - } - - const versionDirs = await readdir(modulePath); - const modules = await Promise.all(versionDirs.map(ver => { - return detectModule(modulePath, ver, ignoreDirs, logger); - })); - return modules.filter(Boolean); -} - -/** - * @param {string} modulePath parent directory (path to module dir holding name of module) - * @param {string} ver basename of current dir holding the module (name is version number of module) - * @param {RegExp} ignoreDirs regexp used to filter directories traversed - * @param {object} [logger] optional logger object - * @returns {Promise} - * @private - */ -async function detectModule(modulePath, ver, ignoreDirs, logger) { - if (ignoreDirs.test(ver)) { - return null; - } - - const versionPath = join(modulePath, ver); - const manifestFile = join(versionPath, 'manifest'); - if (!existsSync(manifestFile)) { - return null; - } - - const mod = { - version: ver, - modulePath: versionPath, - manifest: await readManifest(manifestFile) - }; - - if (mod.manifest.version !== undefined) { - mod.version = mod.manifest.version; - } - - let platform = basename(dirname(dirname(versionPath))).toLowerCase(); - if (mod.manifest.platform) { - platform = mod.manifest.platform.trim().toLowerCase(); - platform = platformAliases[platform] || platform; - mod.manifest.platform = platform; - mod.platform = [platform]; - } - - if (!mod.platform) { - return null; - } - - if (!version.isValid(mod.version)) { - return null; - } - - logger?.trace(`Detected ${cyan(mod.platform[0])} module: ${cyan(mod.manifest.moduleid)} @ ${mod.modulePath}`); - return mod; -} - -/** - * Handles converting apiversion to an int, architectures to a string[] - * @param {string} manifestFile path to manifest file - * @returns {object} - */ -async function readManifest(manifestFile) { - const manifest = {}; - const manifestContents = await readFile(manifestFile, 'utf8'); - for (const line of manifestContents.split('\n')) { - const p = line.indexOf(':'); - if (line.charAt(0) !== '#' && p !== -1) { - const key = line.substring(0, p); - let value = line.substring(p + 1).trim(); - if (key === 'apiversion') { - value = parseInt(value); - } else if (key === 'architectures') { - value = value.split(' '); - } - manifest[key] = value; - } - } - return manifest; -} - -/** - * @param {object[]} modules array of all the distinct modules found - * @returns {object} the modules re-aligned into a tree structure: platform -> name -> version -> module object - */ -function convertArrayOfModulesToHierarchy(modules) { - const result = {}; - if (Array.isArray(modules)) { - for (const m of modules) { - const platform = m.platform[0]; - const name = m.manifest.moduleid; - const version = m.version; - result[platform] = (result[platform] || {}); - result[platform][name] = (result[platform][name] || {}); - result[platform][name][version] = m; - } - } - return result; -} diff --git a/src/util/tisdk.js b/src/util/tisdk.js index 55669ab97..930bacb18 100644 --- a/src/util/tisdk.js +++ b/src/util/tisdk.js @@ -1,9 +1,7 @@ -import { join } from 'node:path'; import { arrayify } from './arrayify.js'; import { prompt } from './prompt.js'; import { detectTitaniumSDKs, TiappXML } from 'node-titanium-sdk/titanium'; - -const os = process.platform === 'darwin' ? 'osx' : process.platform; +import { join } from 'node:path'; const sortTypes = ['local', 'nightly', 'beta', 'rc', 'ga']; @@ -12,10 +10,18 @@ export const typeLabels = { nightly: 'Nightly Build', beta: 'Beta', rc: 'Release Candidate', - ga: 'Production Stable' + ga: 'Production Stable', }; -export async function initSDK({ config, cwd, debugLogger, logger, promptingEnabled, selectedSdk, showSDKPrompt }) { +export async function initSDK({ + config, + cwd, + debugLogger, + logger, + promptingEnabled, + selectedSdk, + showSDKPrompt, +}) { let sdkVersion; let tiappSdkVersion; @@ -26,7 +32,9 @@ export async function initSDK({ config, cwd, debugLogger, logger, promptingEnabl await tiapp.load(tiappFile); debugLogger.trace(`Loaded ${tiappFile}`); sdkVersion = tiappSdkVersion = await tiapp.select1('//sdk-version', 'latest'); - debugLogger.trace(` is ${tiappSdkVersion ? `set to ${tiappSdkVersion}` : 'undefined'}`); + debugLogger.trace( + ` is ${tiappSdkVersion ? `set to ${tiappSdkVersion}` : 'undefined'}` + ); } catch { // might not be a project dir or bad tiapp.xml } @@ -34,33 +42,29 @@ export async function initSDK({ config, cwd, debugLogger, logger, promptingEnabl const configSdkPaths = config.get('paths.sdks'); // detect SDKs - const { - installPath, - latest, - sdks, - sdkPaths - } = await detectTitaniumSDKs({ + const { installPath, latest, sdks, sdkPaths } = await detectTitaniumSDKs({ searchPaths: [ config.get('sdk.defaultInstallLocation'), - ...(Array.isArray(configSdkPaths) ? arrayify(configSdkPaths, true) : []) - ] + ...(Array.isArray(configSdkPaths) ? arrayify(configSdkPaths, true) : []), + ], }); let sdk = null; - if (sdks.length) { + if (Object.keys(sdks).length) { // determine version to use - sdkVersion = (Boolean(selectedSdk) === selectedSdk ? null : selectedSdk) || sdkVersion || 'latest'; + sdkVersion = + (Boolean(selectedSdk) === selectedSdk ? null : selectedSdk) || sdkVersion || 'latest'; if (sdkVersion === 'latest') { sdkVersion = latest; } - sdk = sdks.find(s => s.name === sdkVersion); + sdk = sdks[sdkVersion]; if (promptingEnabled && ((selectedSdk && !sdk) || showSDKPrompt)) { logger.banner(); const sdkTypes = {}; - for (const s of sdks) { + for (const s of Object.values(sdks)) { if (!sdkTypes[s.type]) { sdkTypes[s.type] = []; } @@ -68,7 +72,9 @@ export async function initSDK({ config, cwd, debugLogger, logger, promptingEnabl } const choices = []; - for (const t of Object.keys(sdkTypes).sort((a, b) => sortTypes.indexOf(b) - sortTypes.indexOf(a))) { + for (const t of Object.keys(sdkTypes) + .sort((a, b) => sortTypes.indexOf(b) - sortTypes.indexOf(a)) + .reverse()) { for (const s of sdkTypes[t]) { choices.push({ label: s, value: s, description: typeLabels[t] }); } @@ -77,8 +83,8 @@ export async function initSDK({ config, cwd, debugLogger, logger, promptingEnabl sdkVersion = await prompt({ type: 'select', message: 'Which Titanium SDK would you like to use?', - initial: sdk ? choices.find(s => s.name === sdk.name)?.name : undefined, - choices + initial: sdk ? choices.find((s) => s.value === sdk.name)?.value : undefined, + choices, }); if (sdkVersion === undefined) { @@ -88,7 +94,7 @@ export async function initSDK({ config, cwd, debugLogger, logger, promptingEnabl logger.log(); - sdk = sdks.find(s => s.name === sdkVersion); + sdk = sdks[sdkVersion]; } } @@ -96,10 +102,10 @@ export async function initSDK({ config, cwd, debugLogger, logger, promptingEnabl installPath: config.get('sdk.defaultInstallLocation') || installPath, sdk, sdkPaths, - sdks: sdks.reduce((obj, sdk) => { + sdks: Object.values(sdks).reduce((obj, sdk) => { obj[sdk.name] = sdk; return obj; }, {}), - tiappSdkVersion + tiappSdkVersion, }; } diff --git a/src/util/version.js b/src/util/version.js deleted file mode 100644 index 8ee635b1a..000000000 --- a/src/util/version.js +++ /dev/null @@ -1,220 +0,0 @@ -import semver from 'semver'; - -const versionRegExp = /^(\d+)\.(\d+)\.(\d+)(?:\.(\w+))?/i; - -/** - * Compare function for sort(). - * @param {String} a - Version A - * @param {String} b - Version B - * @returns {Number} - */ -export function compare(a, b) { - const [, amajor, aminor, apatch, atag] = format(a, 3).toLowerCase().match(versionRegExp); - const [, bmajor, bminor, bpatch, btag] = format(b, 3).toLowerCase().match(versionRegExp); - - let n = parseInt(amajor) - parseInt(bmajor); - if (n !== 0) { - return n; - } - - n = parseInt(aminor) - parseInt(bminor); - if (n !== 0) { - return n; - } - - n = parseInt(apatch) - parseInt(bpatch); - if (n !== 0) { - return n; - } - - if (atag && btag) { - return atag.localeCompare(btag); - } - - return atag ? 1 : btag ? -1 : 0; -} - -/** - * Formats a version based on a minimum and maximum number of segments. - * @param {String} ver - The version - * @param {Number} [min] - The minimum number of segments - * @param {Number} [max] - The maximum number of segments - * @param {Boolean} [chopDash] - If true, chops off the dash and anything after it - * @returns {String} The formatted version - */ -export function format(ver, min, max, chopDash) { - ver = String(ver || 0); - chopDash && (ver = ver.replace(/(-.*)?$/, '')); - ver = ver.split('.'); - if (min !== undefined) { - while (ver.length < min) { - ver.push('0'); - } - } - if (max !== undefined) { - ver = ver.slice(0, max); - } - return ver.join('.'); -} - -/** - * Converts two versions into 3 segment format, then checks if they are equal to each other. - * @param {String} v1 - The first version to compare - * @param {String} v2 - The second version to compare - * @returns {Boolean} True if the versions are equal - */ -export function eq(v1, v2) { - return semver.eq(format(v1, 3, 3), format(v2, 3, 3)); -} - -/** - * Converts two versions into 3 segment format, then checks if the first version is less than the - * second version. - * @param {String} v1 - The first version to compare - * @param {String} v2 - The second version to compare - * @returns {Boolean} True if the first version is less than the second version - */ -export function lt(v1, v2) { - return semver.lt(format(v1, 3, 3), format(v2, 3, 3)); -} - -/** - * Converts two versions into 3 segment format, then checks if the first version is less than or - * equal to the second version. - * @param {String} v1 - The first version to compare - * @param {String} v2 - The second version to compare - * @returns {Boolean} True if the first version is less than or equal to the second version - */ -export function lte(v1, v2) { - return semver.lte(format(v1, 3, 3), format(v2, 3, 3)); -} - -/** - * Converts two versions into 3 segment format, then checks if the first version is greater than the - * second version. - * @param {String} v1 - The first version to compare - * @param {String} v2 - The second version to compare - * @returns {Boolean} True if the first version is greater than the second version - */ -export function gt(v1, v2) { - return semver.gt(format(v1, 3, 3), format(v2, 3, 3)); -} - -/** - * Converts two versions into 3 segment format, then checks if the first version is greater than or - * equal to the second version. - * @param {String} v1 - The first version to compare - * @param {String} v2 - The second version to compare - * @returns {Boolean} True if the first version is greater than or equal to the second version - */ -export function gte(v1, v2) { - return semver.gte(format(v1, 3, 3), format(v2, 3, 3)); -} - -/** - * Checks if a version is valid. - * @param {String} v - The version to validate - * @returns {Boolean} - */ -export function isValid(v) { - return semver.valid(format(v, 3, 3)); -} - -/** - * Determines the most minimum value of the supplied range. - * @param {String} str - A string contain one or more versions or version ranges - * @returns {String} The minimum version found or undefined - */ -export function parseMin(str) { - let min; - - for (const range of str.split(/\s*\|\|\s*/)) { - const x = range.split(' ').shift().replace(/[^.\d]/g, ''); - if (!min || lt(x, min)) { - min = x.replace(/\.$/, ''); - } - } - - return min; -} - -/** - * Determines the most maximum value of the supplied range. - * @param {String} str - A string contain one or more versions or version ranges - * @param {Boolean} [allowX=false] - When true, treats 'x' as apart of the version - * @returns {String} The maximum version found or undefined - */ -export function parseMax(str, allowX) { - let max, lt; - - for (const range of str.split(/\s*\|\|\s*/)) { - let x = range.split(' '); - x = x.length === 1 ? x.shift() : x.slice(1).shift(); - allowX || (x = x.replace(/.x$/i, '')); - const y = x.replace(allowX ? /[^.xX\d]/g : /[^.\d]/g, ''); - if (!max || gt(y, max)) { - lt = /^<[^=]\d/.test(x); - max = y.replace(/\.$/, ''); - } - } - - return (lt ? '<' : '') + max; -} - -/** - * Checks if a version is in any of the supplied ranges. - * @param {String} ver - The version to check - * @param {String} str - The version ranges to validate against - * @param {Boolean} [maybe] - If true and the version is greater than at least - * one of the ranges, then it will return 'maybe'. - * @returns {Boolean|String} True if the version matches one of the ranges - */ -export function satisfies(ver, str, maybe) { - ver = format(ver, 3, 3, true); - - // if we get 1.x, we force it to 1.99999999 so that we should match - str = str.replace(/(<=?\d+(\.\d+)*?)\.x/g, '$1.99999999').replace(/(>=?\d+(\.\d+)*?)\.x/g, '$1.0'); - - try { - if (str === '*' || eq(ver, str)) { - return true; - } - } catch {} - - const r = str.split(/\s*\|\|\s*/).some(range => { - // semver is picky with the '-' in comparisons and it just so happens when it - // parses versions in the range, it will add '-0' and cause '1.0.0' != '1.0.0-0', - // so we test our version with and without the '-9' - return range === '*' || semver.satisfies(ver, range) || (ver.indexOf('-') === -1 && semver.satisfies(ver + '-0', range)); - }); - - // if true or we don't care if it maybe matches, then return now - if (r || !maybe) { - return r; - } - - // need to determine if the version is greater than any range - const range = new semver.Range(str); - for (let i = 0; i < range.set.length; i++) { - const set = range.set[i]; - for (let j = set.length - 1; j >= 0; j--) { - if (set[j].semver instanceof semver.SemVer) { - if ((set[j].operator === '<' || set[j].operator === '<=') && !semver.cmp(ver, set[j].operator, set[j].semver, set[j].loose)) { - return 'maybe'; - } - break; - } - } - } - - return false; -} - -/** - * Sorts an array of version numbers in ascending order. - * @param {Array} arr - The array of version numbers to sort - * @returns {Array} The sorted versions - */ -export function sort(arr) { - return arr.sort(compare); -} diff --git a/test/cli/hook.test.js b/test/cli/hook.test.js index 104b82f0e..0c18abd56 100644 --- a/test/cli/hook.test.js +++ b/test/cli/hook.test.js @@ -1,6 +1,6 @@ -import { describe, it } from 'node:test'; -import assert from 'node:assert'; import { CLI } from '../../src/cli.js'; +import assert from 'node:assert'; +import { describe, it } from 'node:test'; import { setTimeout } from 'timers/promises'; describe('CLI hooks', () => { @@ -11,7 +11,7 @@ describe('CLI hooks', () => { let bazPreCounter = 0; let bazPostCounter = 0; - cli.on('foo', data => { + cli.on('foo', (data) => { fooCounter += data.count; }); @@ -25,7 +25,7 @@ describe('CLI hooks', () => { }, post() { bazPostCounter++; - } + }, }); await cli.emit('foo', { count: 2 }); @@ -44,7 +44,7 @@ describe('CLI hooks', () => { let fooCounter = 0; - cli.on('foo', async data => { + cli.on('foo', async (data) => { await setTimeout(1); fooCounter += data.count; }); @@ -67,7 +67,7 @@ describe('CLI hooks', () => { const cli = new CLI(); let fooCounter = 0; - cli.on('foo', data => { + cli.on('foo', (data) => { fooCounter += data.ctx.count; }); @@ -76,7 +76,7 @@ describe('CLI hooks', () => { fooCounter += this.count; cb(); }); - await new Promise(resolve => foo(3, resolve)); + await new Promise((resolve) => foo(3, resolve)); assert.strictEqual(fooCounter, 7); let barCounter = 0; @@ -87,21 +87,21 @@ describe('CLI hooks', () => { }, post(data) { barCounter += data.result[0]; - } + }, }); - const bar = cli.createHook('bar', cb => { + const bar = cli.createHook('bar', (cb) => { barCounter += 3; cb(9); }); - await new Promise(resolve => bar(resolve)); + await new Promise((resolve) => bar(resolve)); assert.strictEqual(barCounter, 13); }); it('should fire event hook with a data payload', async () => { const cli = new CLI(); let foo = { - counter: 0 + counter: 0, }; cli.on('foo', { @@ -109,11 +109,11 @@ describe('CLI hooks', () => { post: async (data, callback) => { data.counter++; callback(); - } + }, }); await new Promise((resolve, reject) => { - cli.emit('foo', foo, err => { + cli.emit('foo', foo, (err) => { if (err) { reject(err); } else { diff --git a/test/commands/custom.test.js b/test/commands/custom.test.js index a9233695e..b81efc33f 100644 --- a/test/commands/custom.test.js +++ b/test/commands/custom.test.js @@ -1,52 +1,56 @@ -import { describe, it } from 'node:test'; -import assert from 'node:assert'; import { initCLI } from '../helpers/init-cli.js'; import { stripColor } from '../helpers/strip-color.js'; +import assert from 'node:assert'; import { dirname, join } from 'node:path'; +import { describe, it } from 'node:test'; import { fileURLToPath } from 'node:url'; const __dirname = dirname(fileURLToPath(import.meta.url)); describe('Custom command', () => { - it('should load custom command', initCLI(async ({ run }) => { - const { exitCode, stdout } = await run([ - '--config', JSON.stringify({ - paths: { - commands: [ - join(__dirname, 'fixtures', 'custom') - ] - } - }) - ]); + it( + 'should load custom command', + initCLI(async ({ run }) => { + const { exitCode, stdout } = await run([ + '--config', + JSON.stringify({ + paths: { + commands: [join(__dirname, 'fixtures', 'custom')], + }, + }), + ]); - const output = stripColor(stdout); - assert.match(output, /Titanium Command-Line Interface/); - assert.match(output, /Usage: titanium/); - assert.match(output, /Commands:/); - assert.match(output, /foo/); - // assert.match(output, /an example of a custom command/); - assert.match(output, /Global Options:/); - assert.match(output, /-h, --help/); + const output = stripColor(stdout); + assert.match(output, /Titanium Command-Line Interface/); + assert.match(output, /Usage: titanium/); + assert.match(output, /Commands:/); + assert.match(output, /foo/); + // assert.match(output, /an example of a custom command/); + assert.match(output, /Global Options:/); + assert.match(output, /-h, --help/); - assert.strictEqual(exitCode, 0); - })); + assert.strictEqual(exitCode, 0); + }) + ); - it('should run custom command', initCLI(async ({ run }) => { - const { exitCode, stdout } = await run([ - '--config', JSON.stringify({ - paths: { - commands: [ - join(__dirname, 'fixtures', 'custom') - ] - } - }), - 'foo' - ]); + it( + 'should run custom command', + initCLI(async ({ run }) => { + const { exitCode, stdout } = await run([ + '--config', + JSON.stringify({ + paths: { + commands: [join(__dirname, 'fixtures', 'custom')], + }, + }), + 'foo', + ]); - const output = stripColor(stdout); - assert.match(output, /Titanium Command-Line Interface/); - assert.match(output, /Foo!/); + const output = stripColor(stdout); + assert.match(output, /Titanium Command-Line Interface/); + assert.match(output, /Foo!/); - assert.strictEqual(exitCode, 0); - })); + assert.strictEqual(exitCode, 0); + }) + ); }); diff --git a/test/commands/ti-build.test.js b/test/commands/ti-build.test.js index f11485987..f8eb49aef 100644 --- a/test/commands/ti-build.test.js +++ b/test/commands/ti-build.test.js @@ -1,19 +1,22 @@ -import { describe, it } from 'node:test'; -import assert from 'node:assert'; -import { stripColor } from '../helpers/strip-color.js'; import { initMockSDKHome } from '../helpers/init-sdk-home.js'; +import { stripColor } from '../helpers/strip-color.js'; +import assert from 'node:assert'; +import { describe, it } from 'node:test'; describe('ti build', () => { - it('should show help', initMockSDKHome(async ({ run }) => { - const { exitCode, stdout } = await run(['build', '-h']); + it( + 'should show help', + initMockSDKHome(async ({ run }) => { + const { exitCode, stdout } = await run(['build', '-h']); - const output = stripColor(stdout); - assert.match(output, /Titanium Command-Line Interface/); - assert.match(output, /Usage: titanium build \[options\]/); - assert.match(output, /Builds an existing app or module project./); - assert.match(output, /Build Options:/); - assert.match(output, /Global Options:/); + const output = stripColor(stdout); + assert.match(output, /Titanium Command-Line Interface/); + assert.match(output, /Usage: titanium build \[options\]/); + assert.match(output, /Builds an existing app or module project./); + assert.match(output, /Build Options:/); + assert.match(output, /Global Options:/); - assert.strictEqual(exitCode, 0); - })); + assert.strictEqual(exitCode, 0); + }) + ); }); diff --git a/test/commands/ti-clean.test.js b/test/commands/ti-clean.test.js index d11f6a2fc..037fc350a 100644 --- a/test/commands/ti-clean.test.js +++ b/test/commands/ti-clean.test.js @@ -1,19 +1,22 @@ -import { describe, it } from 'node:test'; -import assert from 'node:assert'; -import { stripColor } from '../helpers/strip-color.js'; import { initMockSDKHome } from '../helpers/init-sdk-home.js'; +import { stripColor } from '../helpers/strip-color.js'; +import assert from 'node:assert'; +import { describe, it } from 'node:test'; describe('ti clean', () => { - it('should show help', initMockSDKHome(async ({ run }) => { - const { exitCode, stdout } = await run(['clean', '-h']); + it( + 'should show help', + initMockSDKHome(async ({ run }) => { + const { exitCode, stdout } = await run(['clean', '-h']); - const output = stripColor(stdout); - assert.match(output, /Titanium Command-Line Interface/); - assert.match(output, /Usage: titanium clean \[options\]/); - assert.match(output, /Removes previous build directories./); - assert.match(output, /Clean Options:/); - assert.match(output, /Global Options:/); + const output = stripColor(stdout); + assert.match(output, /Titanium Command-Line Interface/); + assert.match(output, /Usage: titanium clean \[options\]/); + assert.match(output, /Removes previous build directories./); + assert.match(output, /Clean Options:/); + assert.match(output, /Global Options:/); - assert.strictEqual(exitCode, 0); - })); + assert.strictEqual(exitCode, 0); + }) + ); }); diff --git a/test/commands/ti-config.test.js b/test/commands/ti-config.test.js index 7b7a0d7c2..ee9d2e506 100644 --- a/test/commands/ti-config.test.js +++ b/test/commands/ti-config.test.js @@ -1,250 +1,306 @@ -import { describe, it } from 'node:test'; -import assert from 'node:assert'; import { initCLI } from '../helpers/init-cli.js'; import { stripColor } from '../helpers/strip-color.js'; +import assert from 'node:assert'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; +import { describe, it } from 'node:test'; describe('ti config', () => { - it('should show help', initCLI(async ({ run }) => { - const { exitCode, stdout } = await run(['config', '-h']); - - const output = stripColor(stdout); - assert.match(output, /Titanium Command-Line Interface/); - assert.match(output, /Usage: titanium config \[options\] \[key\] \[value\]/); - assert.match(output, /Config Arguments:/); - assert.match(output, /Config Options:/); - assert.match(output, /Global Options:/); - assert.strictEqual(exitCode, 0); - })); - - it('should show all config settings', initCLI(async ({ run }) => { - const { exitCode, stdout } = await run(['config']); - - const output = stripColor(stdout); - assert.match(output, /cli.colors\s+= (?:true|false)/); - assert.strictEqual(exitCode, 0); - })); - - it('should show all config settings as JSON', initCLI(async ({ run }) => { - const { exitCode, stdout } = await run(['config', '--json']); - - const json = JSON.parse(stdout); - assert(Object.hasOwn(json, 'cli')); - assert(Object.hasOwn(json.cli, 'colors')); - assert.strictEqual(exitCode, 0); - })); - - it('should get all config settings matching a namespace', initCLI(async ({ run }) => { - const { exitCode, stdout } = await run(['config', 'cli']); - - const output = stripColor(stdout); - assert.match(output, /cli.colors\s+= (?:true|false)/); - assert.strictEqual(exitCode, 0); - })); - - it('should get all config settings matching a namespace as JSON', initCLI(async ({ run }) => { - const { exitCode, stdout } = await run(['config', 'cli', '--json']); - - const json = JSON.parse(stdout); - assert(Object.hasOwn(json, 'colors')); - assert.strictEqual(exitCode, 0); - })); - - it('should get a single config setting', initCLI(async ({ run }) => { - const { exitCode, stdout } = await run(['config', 'cli.colors']); - - const output = stripColor(stdout); - assert.match(output, /true|false/); - assert.strictEqual(exitCode, 0); - })); - - it('should get a single config setting as JSON', initCLI(async ({ run }) => { - const { exitCode, stdout } = await run(['config', 'cli.colors', '--json']); - - assert.match(stdout, /true|false/); - assert.strictEqual(exitCode, 0); - })); - - it('should error if key is invalid', initCLI(async ({ run }) => { - const { exitCode, stderr } = await run(['config', '123']); - - const output = stripColor(stderr); - assert.match(output, /Invalid key "123"/); - assert.strictEqual(exitCode, 1); - })); - - it('should error if setting is not found', initCLI(async ({ run }) => { - const { exitCode, stderr } = await run(['config', 'does_not_exist']); - - const output = stripColor(stderr); - assert.match(output, /Key "does_not_exist" not found/); - assert.strictEqual(exitCode, 1); - })); - - it('should error as JSON if setting is not found', initCLI(async ({ run }) => { - const { exitCode, stderr } = await run(['config', 'does_not_exist', '--json']); - - const json = JSON.parse(stderr); - assert.deepStrictEqual(json, { - success: false, - error: 'Key "does_not_exist" not found' - }); - assert.strictEqual(exitCode, 1); - })); - - it('should set a single config setting', initCLI(async ({ run }) => { - let { exitCode, stdout } = await run(['config', 'foo', 'bar']); - - let output = stripColor(stdout); - assert.match(output, /foo saved/); - assert.strictEqual(exitCode, 0); - - ({ exitCode, stdout } = await run(['config', 'foo'])); - output = stripColor(stdout); - assert.match(output, /bar/); - assert.strictEqual(exitCode, 0); - })); - - it('should set a nested config setting', initCLI(async ({ run }) => { - let { exitCode, stdout } = await run(['config', 'foo.bar', 'baz']); - - let output = stripColor(stdout); - assert.match(output, /foo.bar saved/); - assert.strictEqual(exitCode, 0); - - ({ exitCode, stdout } = await run(['config'])); - output = stripColor(stdout); - assert.match(output, /foo.bar\s+= "baz"/); - assert.strictEqual(exitCode, 0); - })); - - it('should delete a single config setting', initCLI(async ({ run }) => { - let { exitCode, stdout, stderr } = await run(['config', 'foo', 'bar']); - - let output = stripColor(stdout); - assert.match(output, /foo saved/); - assert.strictEqual(exitCode, 0); - - ({ exitCode, stdout } = await run(['config', 'foo'])); - output = stripColor(stdout); - assert.match(output, /bar/); - assert.strictEqual(exitCode, 0); - - ({ exitCode, stdout } = await run(['config', 'foo', '--remove'])); - output = stripColor(stdout); - assert.match(output, /"foo" removed/); - assert.strictEqual(exitCode, 0); - - ({ exitCode, stderr } = await run(['config', 'foo'])); - output = stripColor(stderr); - assert.match(output, /Key "foo" not found/); - assert.strictEqual(exitCode, 1); - })); - - it('should delete a nested config setting', initCLI(async ({ run }) => { - let { exitCode, stdout, stderr } = await run(['config', 'foo.bar', 'baz']); - - let output = stripColor(stdout); - assert.match(output, /foo.bar saved/); - assert.strictEqual(exitCode, 0); - - ({ exitCode, stdout } = await run(['config'])); - output = stripColor(stdout); - assert.match(output, /foo.bar\s+= "baz"/); - assert.strictEqual(exitCode, 0); - - ({ exitCode, stdout } = await run(['config', 'foo', '--remove'])); - output = stripColor(stdout); - assert.match(output, /"foo" removed/); - assert.strictEqual(exitCode, 0); - - ({ exitCode, stderr } = await run(['config', 'foo'])); - output = stripColor(stderr); - assert.match(output, /Key "foo" not found/); - assert.strictEqual(exitCode, 1); - })); - - it('should error deleting without a key', initCLI(async ({ run }) => { - const { exitCode, stderr } = await run(['config', '--remove']); - - const output = stripColor(stderr); - assert.match(output, /Missing key of the config setting to remove/); - - assert.strictEqual(exitCode, 1); - })); - - it('should error deleting with too many args', initCLI(async ({ run }) => { - const { exitCode, stderr } = await run(['config', 'foo', 'bar', '--remove']); - - const output = stripColor(stderr); - assert.match(output, /Too many arguments for "--remove" flag/); - - assert.strictEqual(exitCode, 1); - })); - - it('should error setting an unsupported path', initCLI(async ({ run }) => { - const { exitCode, stderr } = await run(['config', 'paths.foo', 'bar']); - - const output = stripColor(stderr); - assert.match(output, /Unsupported key "paths.foo"/); - assert.strictEqual(exitCode, 1); - })); - - it('should set a path setting', initCLI(async ({ run }) => { - const fooPath = join(tmpdir(), 'foo'); - const barPath = join(tmpdir(), 'bar'); - const bazPath = join(tmpdir(), 'baz'); - - let { exitCode, stdout } = await run(['config', 'paths.modules', fooPath]); - let output = stripColor(stdout); - assert.match(output, /paths.modules saved/); - assert.strictEqual(exitCode, 0); - - ({ exitCode, stdout } = await run(['config', 'paths.modules', '--json'])); - let json = JSON.parse(stdout); - assert.deepStrictEqual(json, [fooPath]); - assert.strictEqual(exitCode, 0); - - ({ exitCode, stdout } = await run(['config', 'paths.modules', barPath, '--json'])); - json = JSON.parse(stdout); - assert.deepStrictEqual(json, { success: true }); - assert.strictEqual(exitCode, 0); - - ({ exitCode, stdout } = await run(['config', 'paths'])); - output = stripColor(stdout); - assert.match(output, new RegExp(`= "${barPath.replace(/\\/g, '\\\\\\\\')}"`)); - assert.strictEqual(exitCode, 0); - - ({ exitCode, stdout } = await run(['config', 'paths.modules', bazPath, '--append'])); - output = stripColor(stdout); - assert.match(output, /paths.modules saved/); - assert.strictEqual(exitCode, 0); - - ({ exitCode, stdout } = await run(['config', 'paths.modules'])); - output = stripColor(stdout); - assert.match(output, new RegExp(`${barPath.replace(/\\/g, '\\\\')}\n${bazPath.replace(/\\/g, '\\\\')}`)); - assert.strictEqual(exitCode, 0); - - ({ exitCode, stdout } = await run(['config', 'paths.modules', '--json'])); - json = JSON.parse(stdout); - assert.deepStrictEqual(json, [barPath, bazPath]); - assert.strictEqual(exitCode, 0); - - ({ exitCode, stdout } = await run(['config', 'paths.modules', barPath, '--remove'])); - output = stripColor(stdout); - assert.match(output, /paths.modules saved/); - assert.strictEqual(exitCode, 0); - - ({ exitCode, stdout } = await run(['config', 'paths.modules', '--json'])); - json = JSON.parse(stdout); - assert.deepStrictEqual(json, [bazPath]); - assert.strictEqual(exitCode, 0); - - ({ exitCode, stdout } = await run(['config', 'paths.modules', barPath, '--remove'])); - output = stripColor(stdout); - assert.match(output, /paths.modules saved/); - assert.strictEqual(exitCode, 0); - })); - + it( + 'should show help', + initCLI(async ({ run }) => { + const { exitCode, stdout } = await run(['config', '-h']); + + const output = stripColor(stdout); + assert.match(output, /Titanium Command-Line Interface/); + assert.match(output, /Usage: titanium config \[options\] \[key\] \[value\]/); + assert.match(output, /Config Arguments:/); + assert.match(output, /Config Options:/); + assert.match(output, /Global Options:/); + assert.strictEqual(exitCode, 0); + }) + ); + + it( + 'should show all config settings', + initCLI(async ({ run }) => { + const { exitCode, stdout } = await run(['config']); + + const output = stripColor(stdout); + assert.match(output, /cli.colors\s+= (?:true|false)/); + assert.strictEqual(exitCode, 0); + }) + ); + + it( + 'should show all config settings as JSON', + initCLI(async ({ run }) => { + const { exitCode, stdout } = await run(['config', '--json']); + + const json = JSON.parse(stdout); + assert(Object.hasOwn(json, 'cli')); + assert(Object.hasOwn(json.cli, 'colors')); + assert.strictEqual(exitCode, 0); + }) + ); + + it( + 'should get all config settings matching a namespace', + initCLI(async ({ run }) => { + const { exitCode, stdout } = await run(['config', 'cli']); + + const output = stripColor(stdout); + assert.match(output, /cli.colors\s+= (?:true|false)/); + assert.strictEqual(exitCode, 0); + }) + ); + + it( + 'should get all config settings matching a namespace as JSON', + initCLI(async ({ run }) => { + const { exitCode, stdout } = await run(['config', 'cli', '--json']); + + const json = JSON.parse(stdout); + assert(Object.hasOwn(json, 'colors')); + assert.strictEqual(exitCode, 0); + }) + ); + + it( + 'should get a single config setting', + initCLI(async ({ run }) => { + const { exitCode, stdout } = await run(['config', 'cli.colors']); + + const output = stripColor(stdout); + assert.match(output, /true|false/); + assert.strictEqual(exitCode, 0); + }) + ); + + it( + 'should get a single config setting as JSON', + initCLI(async ({ run }) => { + const { exitCode, stdout } = await run(['config', 'cli.colors', '--json']); + + assert.match(stdout, /true|false/); + assert.strictEqual(exitCode, 0); + }) + ); + + it( + 'should error if key is invalid', + initCLI(async ({ run }) => { + const { exitCode, stderr } = await run(['config', '123']); + + const output = stripColor(stderr); + assert.match(output, /Invalid key "123"/); + assert.strictEqual(exitCode, 1); + }) + ); + + it( + 'should error if setting is not found', + initCLI(async ({ run }) => { + const { exitCode, stderr } = await run(['config', 'does_not_exist']); + + const output = stripColor(stderr); + assert.match(output, /Key "does_not_exist" not found/); + assert.strictEqual(exitCode, 1); + }) + ); + + it( + 'should error as JSON if setting is not found', + initCLI(async ({ run }) => { + const { exitCode, stderr } = await run(['config', 'does_not_exist', '--json']); + + const json = JSON.parse(stderr); + assert.deepStrictEqual(json, { + success: false, + error: 'Key "does_not_exist" not found', + }); + assert.strictEqual(exitCode, 1); + }) + ); + + it( + 'should set a single config setting', + initCLI(async ({ run }) => { + let { exitCode, stdout } = await run(['config', 'foo', 'bar']); + + let output = stripColor(stdout); + assert.match(output, /foo saved/); + assert.strictEqual(exitCode, 0); + + ({ exitCode, stdout } = await run(['config', 'foo'])); + output = stripColor(stdout); + assert.match(output, /bar/); + assert.strictEqual(exitCode, 0); + }) + ); + + it( + 'should set a nested config setting', + initCLI(async ({ run }) => { + let { exitCode, stdout } = await run(['config', 'foo.bar', 'baz']); + + let output = stripColor(stdout); + assert.match(output, /foo.bar saved/); + assert.strictEqual(exitCode, 0); + + ({ exitCode, stdout } = await run(['config'])); + output = stripColor(stdout); + assert.match(output, /foo.bar\s+= "baz"/); + assert.strictEqual(exitCode, 0); + }) + ); + + it( + 'should delete a single config setting', + initCLI(async ({ run }) => { + let { exitCode, stdout, stderr } = await run(['config', 'foo', 'bar']); + + let output = stripColor(stdout); + assert.match(output, /foo saved/); + assert.strictEqual(exitCode, 0); + + ({ exitCode, stdout } = await run(['config', 'foo'])); + output = stripColor(stdout); + assert.match(output, /bar/); + assert.strictEqual(exitCode, 0); + + ({ exitCode, stdout } = await run(['config', 'foo', '--remove'])); + output = stripColor(stdout); + assert.match(output, /"foo" removed/); + assert.strictEqual(exitCode, 0); + + ({ exitCode, stderr } = await run(['config', 'foo'])); + output = stripColor(stderr); + assert.match(output, /Key "foo" not found/); + assert.strictEqual(exitCode, 1); + }) + ); + + it( + 'should delete a nested config setting', + initCLI(async ({ run }) => { + let { exitCode, stdout, stderr } = await run(['config', 'foo.bar', 'baz']); + + let output = stripColor(stdout); + assert.match(output, /foo.bar saved/); + assert.strictEqual(exitCode, 0); + + ({ exitCode, stdout } = await run(['config'])); + output = stripColor(stdout); + assert.match(output, /foo.bar\s+= "baz"/); + assert.strictEqual(exitCode, 0); + + ({ exitCode, stdout } = await run(['config', 'foo', '--remove'])); + output = stripColor(stdout); + assert.match(output, /"foo" removed/); + assert.strictEqual(exitCode, 0); + + ({ exitCode, stderr } = await run(['config', 'foo'])); + output = stripColor(stderr); + assert.match(output, /Key "foo" not found/); + assert.strictEqual(exitCode, 1); + }) + ); + + it( + 'should error deleting without a key', + initCLI(async ({ run }) => { + const { exitCode, stderr } = await run(['config', '--remove']); + + const output = stripColor(stderr); + assert.match(output, /Missing key of the config setting to remove/); + + assert.strictEqual(exitCode, 1); + }) + ); + + it( + 'should error deleting with too many args', + initCLI(async ({ run }) => { + const { exitCode, stderr } = await run(['config', 'foo', 'bar', '--remove']); + + const output = stripColor(stderr); + assert.match(output, /Too many arguments for "--remove" flag/); + + assert.strictEqual(exitCode, 1); + }) + ); + + it( + 'should error setting an unsupported path', + initCLI(async ({ run }) => { + const { exitCode, stderr } = await run(['config', 'paths.foo', 'bar']); + + const output = stripColor(stderr); + assert.match(output, /Unsupported key "paths.foo"/); + assert.strictEqual(exitCode, 1); + }) + ); + + it( + 'should set a path setting', + initCLI(async ({ run }) => { + const fooPath = join(tmpdir(), 'foo'); + const barPath = join(tmpdir(), 'bar'); + const bazPath = join(tmpdir(), 'baz'); + + let { exitCode, stdout } = await run(['config', 'paths.modules', fooPath]); + let output = stripColor(stdout); + assert.match(output, /paths.modules saved/); + assert.strictEqual(exitCode, 0); + + ({ exitCode, stdout } = await run(['config', 'paths.modules', '--json'])); + let json = JSON.parse(stdout); + assert.deepStrictEqual(json, [fooPath]); + assert.strictEqual(exitCode, 0); + + ({ exitCode, stdout } = await run(['config', 'paths.modules', barPath, '--json'])); + json = JSON.parse(stdout); + assert.deepStrictEqual(json, { success: true }); + assert.strictEqual(exitCode, 0); + + ({ exitCode, stdout } = await run(['config', 'paths'])); + output = stripColor(stdout); + assert.match(output, new RegExp(`= "${barPath.replace(/\\/g, '\\\\\\\\')}"`)); + assert.strictEqual(exitCode, 0); + + ({ exitCode, stdout } = await run(['config', 'paths.modules', bazPath, '--append'])); + output = stripColor(stdout); + assert.match(output, /paths.modules saved/); + assert.strictEqual(exitCode, 0); + + ({ exitCode, stdout } = await run(['config', 'paths.modules'])); + output = stripColor(stdout); + assert.match( + output, + new RegExp(`${barPath.replace(/\\/g, '\\\\')}\n${bazPath.replace(/\\/g, '\\\\')}`) + ); + assert.strictEqual(exitCode, 0); + + ({ exitCode, stdout } = await run(['config', 'paths.modules', '--json'])); + json = JSON.parse(stdout); + assert.deepStrictEqual(json, [barPath, bazPath]); + assert.strictEqual(exitCode, 0); + + ({ exitCode, stdout } = await run(['config', 'paths.modules', barPath, '--remove'])); + output = stripColor(stdout); + assert.match(output, /paths.modules saved/); + assert.strictEqual(exitCode, 0); + + ({ exitCode, stdout } = await run(['config', 'paths.modules', '--json'])); + json = JSON.parse(stdout); + assert.deepStrictEqual(json, [bazPath]); + assert.strictEqual(exitCode, 0); + + ({ exitCode, stdout } = await run(['config', 'paths.modules', barPath, '--remove'])); + output = stripColor(stdout); + assert.match(output, /paths.modules saved/); + assert.strictEqual(exitCode, 0); + }) + ); }); diff --git a/test/commands/ti-create.test.js b/test/commands/ti-create.test.js index 9f6e708dc..eca263666 100644 --- a/test/commands/ti-create.test.js +++ b/test/commands/ti-create.test.js @@ -1,21 +1,27 @@ -import { describe, it } from 'node:test'; -import assert from 'node:assert'; -import { stripColor } from '../helpers/strip-color.js'; import { initMockSDKHome } from '../helpers/init-sdk-home.js'; +import { stripColor } from '../helpers/strip-color.js'; +import assert from 'node:assert'; +import { describe, it } from 'node:test'; describe('ti create', () => { - it('should show help', initMockSDKHome(async ({ run }) => { - const { exitCode, stdout } = await run(['create', '-h']); + it( + 'should show help', + initMockSDKHome(async ({ run }) => { + const { exitCode, stdout } = await run(['create', '-h']); - const output = stripColor(stdout); - assert.match(output, /Titanium Command-Line Interface/); - assert.match(output, /Usage: titanium create \[options\]/); - assert.match(output, /Creates a new Titanium application, native module, or Apple Watch™ app./); - assert.match(output, /Create Options:/); - assert.match(output, /Create --type=app Options:/); - assert.match(output, /Create --type=module Options:/); - assert.match(output, /Global Options:/); + const output = stripColor(stdout); + assert.match(output, /Titanium Command-Line Interface/); + assert.match(output, /Usage: titanium create \[options\]/); + assert.match( + output, + /Creates a new Titanium application, native module, or Apple Watch™ app./ + ); + assert.match(output, /Create Options:/); + assert.match(output, /Create --type=app Options:/); + assert.match(output, /Create --type=module Options:/); + assert.match(output, /Global Options:/); - assert.strictEqual(exitCode, 0); - })); + assert.strictEqual(exitCode, 0); + }) + ); }); diff --git a/test/commands/ti-info.test.js b/test/commands/ti-info.test.js index dc55c8fdc..1643d194c 100644 --- a/test/commands/ti-info.test.js +++ b/test/commands/ti-info.test.js @@ -1,153 +1,212 @@ -import { describe, it } from 'node:test'; +import { initCLI } from '../helpers/init-cli.js'; +import { stripColor } from '../helpers/strip-color.js'; +import fs from 'fs-extra'; import assert from 'node:assert'; import { dirname, join } from 'node:path'; +import { describe, it } from 'node:test'; import { fileURLToPath } from 'node:url'; -import fs from 'fs-extra'; -import { initCLI } from '../helpers/init-cli.js'; -import { stripColor } from '../helpers/strip-color.js'; const __dirname = dirname(fileURLToPath(import.meta.url)); const pkgJson = fs.readJsonSync(join(__dirname, '../../package.json')); describe('ti info', () => { - it('should show help', initCLI(async ({ run }) => { - const { exitCode, stdout } = await run(['info', '-h']); - - const output = stripColor(stdout); - assert.match(output, /Titanium Command-Line Interface/); - assert.match(output, /Usage: titanium info/); - assert.match(output, /Info Options:/); - assert.match(output, /Global Options:/); - - assert.strictEqual(exitCode, 0); - })); - - it('should show all info', initCLI(async ({ run }) => { - const { exitCode, stdout } = await run(['info']); - - const output = stripColor(stdout); - assert.match(output, /Titanium Command-Line Interface/); - assert.match(output, /Operating System/); - assert.match(output, new RegExp(`Node.js\n\\s*Node.js Version\\s*= ${process.versions.node}`)); - assert.match(output, new RegExp(`Titanium CLI\n\\s*CLI Version\\s*= ${pkgJson.version}`)); - assert.match(output, /Titanium SDKs/); - assert.match(output, /Java Development Kit/); - assert.match(output, /Issues/); - - assert.strictEqual(exitCode, 0); - }), 60000); - - it('should show all info as JSON', initCLI(async ({ run }) => { - let { exitCode, stdout } = await run(['info', '--json']); - - let json = JSON.parse(stdout); - assert(Object.hasOwn(json, 'os')); - assert(Object.hasOwn(json, 'node')); - assert(Object.hasOwn(json, 'npm')); - assert(Object.hasOwn(json, 'titanium')); - assert(Object.hasOwn(json, 'titaniumCLI')); - assert(Object.hasOwn(json, 'jdk')); - - // legacy - ({ exitCode, stdout } = await run(['info', '--output', 'json'])); - - json = JSON.parse(stdout); - assert(Object.hasOwn(json, 'os')); - assert(Object.hasOwn(json, 'node')); - assert(Object.hasOwn(json, 'npm')); - assert(Object.hasOwn(json, 'titanium')); - assert(Object.hasOwn(json, 'titaniumCLI')); - assert(Object.hasOwn(json, 'jdk')); - - assert.strictEqual(exitCode, 0); - }), 60000); - - it('should only show "os" info', initCLI(async ({ run }) => { - const { exitCode, stdout } = await run(['info', '--types', 'os']); - - const output = stripColor(stdout); - assert.match(output, /Titanium Command-Line Interface/); - assert.match(output, /Operating System/); - assert.doesNotMatch(output, new RegExp(`Node.js\n\\s*Node.js Version\\s*= ${process.versions.node}`)); - assert.doesNotMatch(output, new RegExp(`Titanium CLI\n\\s*CLI Version\\s*= ${pkgJson.version}`)); - assert.doesNotMatch(output, /Titanium SDKs/); - assert.doesNotMatch(output, /Java Development Kit/); - assert.match(output, /Issues/); - - assert.strictEqual(exitCode, 0); - }), 60000); - - it('should only show "os" info as JSON', initCLI(async ({ run }) => { - const { exitCode, stdout } = await run(['info', '--types', 'os', '--json']); - - const json = JSON.parse(stdout); - assert(Object.hasOwn(json, 'os')); - assert(!Object.hasOwn(json, 'node')); - assert(!Object.hasOwn(json, 'npm')); - assert(!Object.hasOwn(json, 'titanium')); - assert(!Object.hasOwn(json, 'titaniumCLI')); - assert(!Object.hasOwn(json, 'jdk')); - - assert.strictEqual(exitCode, 0); - }), 60000); - - it('should only show "nodejs" info', initCLI(async ({ run }) => { - const { exitCode, stdout } = await run(['info', '--types', 'nodejs']); - - const output = stripColor(stdout); - assert.match(output, /Titanium Command-Line Interface/); - assert.doesNotMatch(output, /Operating System/); - assert.match(output, new RegExp(`Node.js\n\\s*Node.js Version\\s*= ${process.versions.node}`)); - assert.doesNotMatch(output, new RegExp(`Titanium CLI\n\\s*CLI Version\\s*= ${pkgJson.version}`)); - assert.doesNotMatch(output, /Titanium SDKs/); - assert.doesNotMatch(output, /Java Development Kit/); - assert.match(output, /Issues/); - - assert.strictEqual(exitCode, 0); - }), 60000); - - it('should only show "nodejs" info as JSON', initCLI(async ({ run }) => { - const { exitCode, stdout } = await run(['info', '--types', 'nodejs', '--json']); - - const json = JSON.parse(stdout); - assert(!Object.hasOwn(json, 'os')); - assert(Object.hasOwn(json, 'node')); - assert.strictEqual(json.node.version, process.versions.node); - assert(Object.hasOwn(json, 'npm')); - assert(!Object.hasOwn(json, 'titanium')); - assert(!Object.hasOwn(json, 'titaniumCLI')); - assert(!Object.hasOwn(json, 'jdk')); - - assert.strictEqual(exitCode, 0); - }), 60000); - - it('should only show "titanium" info', initCLI(async ({ run }) => { - const { exitCode, stdout } = await run(['info', '--types', 'titanium']); - - const output = stripColor(stdout); - assert.match(output, /Titanium Command-Line Interface/); - assert.doesNotMatch(output, /Operating System/); - assert.doesNotMatch(output, new RegExp(`Node.js\n\\s*Node.js Version\\s*= ${process.versions.node}`)); - assert.match(output, new RegExp(`Titanium CLI\n\\s*CLI Version\\s*= ${pkgJson.version}`)); - assert.match(output, /Titanium SDKs/); - assert.doesNotMatch(output, /Java Development Kit/); - assert.match(output, /Issues/); - - assert.strictEqual(exitCode, 0); - }), 60000); - - it('should only show "jdk" info', initCLI(async ({ run }) => { - const { exitCode, stdout } = await run(['info', '--types', 'jdk']); - - const output = stripColor(stdout); - assert.match(output, /Titanium Command-Line Interface/); - assert.doesNotMatch(output, /Operating System/); - assert.doesNotMatch(output, new RegExp(`Node.js\n\\s*Node.js Version\\s*= ${process.versions.node}`)); - assert.doesNotMatch(output, new RegExp(`Titanium CLI\n\\s*CLI Version\\s*= ${pkgJson.version}`)); - assert.doesNotMatch(output, /Titanium SDKs/); - assert.match(output, /Java Development Kit/); - assert.match(output, /Issues/); - - assert.strictEqual(exitCode, 0); - }), 60000); + it( + 'should show help', + initCLI(async ({ run }) => { + const { exitCode, stdout } = await run(['info', '-h']); + + const output = stripColor(stdout); + assert.match(output, /Titanium Command-Line Interface/); + assert.match(output, /Usage: titanium info/); + assert.match(output, /Info Options:/); + assert.match(output, /Global Options:/); + + assert.strictEqual(exitCode, 0); + }) + ); + + it( + 'should show all info', + initCLI(async ({ run }) => { + const { exitCode, stdout } = await run(['info']); + + const output = stripColor(stdout); + assert.match(output, /Titanium Command-Line Interface/); + assert.match(output, /Operating System/); + assert.match( + output, + new RegExp(`Node.js\n\\s*Node.js Version\\s*= ${process.versions.node}`) + ); + assert.match(output, new RegExp(`Titanium CLI\n\\s*CLI Version\\s*= ${pkgJson.version}`)); + assert.match(output, /Titanium SDKs/); + assert.match(output, /Java Development Kit/); + assert.match(output, /Issues/); + + assert.strictEqual(exitCode, 0); + }), + 60000 + ); + + it( + 'should show all info as JSON', + initCLI(async ({ run }) => { + let { exitCode, stdout } = await run(['info', '--json']); + + let json = JSON.parse(stdout); + assert(Object.hasOwn(json, 'os')); + assert(Object.hasOwn(json, 'node')); + assert(Object.hasOwn(json, 'npm')); + assert(Object.hasOwn(json, 'titanium')); + assert(Object.hasOwn(json, 'titaniumCLI')); + assert(Object.hasOwn(json, 'jdk')); + + // legacy + ({ exitCode, stdout } = await run(['info', '--output', 'json'])); + + json = JSON.parse(stdout); + assert(Object.hasOwn(json, 'os')); + assert(Object.hasOwn(json, 'node')); + assert(Object.hasOwn(json, 'npm')); + assert(Object.hasOwn(json, 'titanium')); + assert(Object.hasOwn(json, 'titaniumCLI')); + assert(Object.hasOwn(json, 'jdk')); + + assert.strictEqual(exitCode, 0); + }), + 60000 + ); + + it( + 'should only show "os" info', + initCLI(async ({ run }) => { + const { exitCode, stdout } = await run(['info', '--types', 'os']); + + const output = stripColor(stdout); + assert.match(output, /Titanium Command-Line Interface/); + assert.match(output, /Operating System/); + assert.doesNotMatch( + output, + new RegExp(`Node.js\n\\s*Node.js Version\\s*= ${process.versions.node}`) + ); + assert.doesNotMatch( + output, + new RegExp(`Titanium CLI\n\\s*CLI Version\\s*= ${pkgJson.version}`) + ); + assert.doesNotMatch(output, /Titanium SDKs/); + assert.doesNotMatch(output, /Java Development Kit/); + assert.match(output, /Issues/); + + assert.strictEqual(exitCode, 0); + }), + 60000 + ); + + it( + 'should only show "os" info as JSON', + initCLI(async ({ run }) => { + const { exitCode, stdout } = await run(['info', '--types', 'os', '--json']); + + const json = JSON.parse(stdout); + assert(Object.hasOwn(json, 'os')); + assert(!Object.hasOwn(json, 'node')); + assert(!Object.hasOwn(json, 'npm')); + assert(!Object.hasOwn(json, 'titanium')); + assert(!Object.hasOwn(json, 'titaniumCLI')); + assert(!Object.hasOwn(json, 'jdk')); + + assert.strictEqual(exitCode, 0); + }), + 60000 + ); + + it( + 'should only show "nodejs" info', + initCLI(async ({ run }) => { + const { exitCode, stdout } = await run(['info', '--types', 'nodejs']); + + const output = stripColor(stdout); + assert.match(output, /Titanium Command-Line Interface/); + assert.doesNotMatch(output, /Operating System/); + assert.match( + output, + new RegExp(`Node.js\n\\s*Node.js Version\\s*= ${process.versions.node}`) + ); + assert.doesNotMatch( + output, + new RegExp(`Titanium CLI\n\\s*CLI Version\\s*= ${pkgJson.version}`) + ); + assert.doesNotMatch(output, /Titanium SDKs/); + assert.doesNotMatch(output, /Java Development Kit/); + assert.match(output, /Issues/); + + assert.strictEqual(exitCode, 0); + }), + 60000 + ); + + it( + 'should only show "nodejs" info as JSON', + initCLI(async ({ run }) => { + const { exitCode, stdout } = await run(['info', '--types', 'nodejs', '--json']); + + const json = JSON.parse(stdout); + assert(!Object.hasOwn(json, 'os')); + assert(Object.hasOwn(json, 'node')); + assert.strictEqual(json.node.version, process.versions.node); + assert(Object.hasOwn(json, 'npm')); + assert(!Object.hasOwn(json, 'titanium')); + assert(!Object.hasOwn(json, 'titaniumCLI')); + assert(!Object.hasOwn(json, 'jdk')); + + assert.strictEqual(exitCode, 0); + }), + 60000 + ); + + it( + 'should only show "titanium" info', + initCLI(async ({ run }) => { + const { exitCode, stdout } = await run(['info', '--types', 'titanium']); + + const output = stripColor(stdout); + assert.match(output, /Titanium Command-Line Interface/); + assert.doesNotMatch(output, /Operating System/); + assert.doesNotMatch( + output, + new RegExp(`Node.js\n\\s*Node.js Version\\s*= ${process.versions.node}`) + ); + assert.match(output, new RegExp(`Titanium CLI\n\\s*CLI Version\\s*= ${pkgJson.version}`)); + assert.match(output, /Titanium SDKs/); + assert.doesNotMatch(output, /Java Development Kit/); + assert.match(output, /Issues/); + + assert.strictEqual(exitCode, 0); + }), + 60000 + ); + + it( + 'should only show "jdk" info', + initCLI(async ({ run }) => { + const { exitCode, stdout } = await run(['info', '--types', 'jdk']); + + const output = stripColor(stdout); + assert.match(output, /Titanium Command-Line Interface/); + assert.doesNotMatch(output, /Operating System/); + assert.doesNotMatch( + output, + new RegExp(`Node.js\n\\s*Node.js Version\\s*= ${process.versions.node}`) + ); + assert.doesNotMatch( + output, + new RegExp(`Titanium CLI\n\\s*CLI Version\\s*= ${pkgJson.version}`) + ); + assert.doesNotMatch(output, /Titanium SDKs/); + assert.match(output, /Java Development Kit/); + assert.match(output, /Issues/); + + assert.strictEqual(exitCode, 0); + }), + 60000 + ); }); diff --git a/test/commands/ti-module.test.js b/test/commands/ti-module.test.js index 285ed709b..7843b63d5 100644 --- a/test/commands/ti-module.test.js +++ b/test/commands/ti-module.test.js @@ -1,76 +1,90 @@ -import { describe, it } from 'node:test'; -import assert from 'node:assert'; import { initCLI } from '../helpers/init-cli.js'; import { stripColor } from '../helpers/strip-color.js'; -import { fileURLToPath } from 'node:url'; +import assert from 'node:assert'; import { join } from 'node:path'; +import { describe, it } from 'node:test'; +import { fileURLToPath } from 'node:url'; const fixturesDir = join(fileURLToPath(import.meta.url), '../fixtures'); describe('ti module', () => { - it('should show help', initCLI(async ({ run }) => { - const { exitCode, stdout } = await run(['module', '-h']); - - const output = stripColor(stdout); - assert.match(output, /Titanium Command-Line Interface/); - assert.match(output, /Usage: titanium module \[options\] \[command\]/); - assert.match(output, /Commands:/); - assert.match(output, /Module Options:/); - assert.match(output, /Global Options:/); - - assert.strictEqual(exitCode, 0); - })); - - it('should show list help', initCLI(async ({ run }) => { - const { exitCode, stdout } = await run(['module', 'list', '-h']); - - const output = stripColor(stdout); - assert.match(output, /Titanium Command-Line Interface/); - assert.match(output, /Usage: titanium module list|ls/); - assert.match(output, /List Options:/); - assert.match(output, /Global Options:/); - - assert.strictEqual(exitCode, 0); - })); - - it('should list no installed modules', initCLI(async ({ run }) => { - const { exitCode, stdout } = await run(['module']); - - const output = stripColor(stdout); - assert.match(output, /Configured Path Modules/); - assert.match(output, /Global Modules/); - - assert.strictEqual(exitCode, 0); - })); - - it('should list no installed modules as JSON', initCLI(async ({ run }) => { - const { exitCode, stdout } = await run(['module', '--json']); - - const json = JSON.parse(stdout); - assert.deepStrictEqual(json, { - project: {}, - config: {}, - global: {} - }); - - assert.strictEqual(exitCode, 0); - })); - - it('should list installed modules', initCLI(async ({ run }) => { - const { exitCode, stdout } = await run([ - 'module', - '--config', - JSON.stringify({ - paths: { - modules: [ - join(fixturesDir, 'module') - ] - } - }) - ]); - - const output = stripColor(stdout); - assert.match(output, new RegExp(`Configured Path Modules + it( + 'should show help', + initCLI(async ({ run }) => { + const { exitCode, stdout } = await run(['module', '-h']); + + const output = stripColor(stdout); + assert.match(output, /Titanium Command-Line Interface/); + assert.match(output, /Usage: titanium module \[options\] \[command\]/); + assert.match(output, /Commands:/); + assert.match(output, /Module Options:/); + assert.match(output, /Global Options:/); + + assert.strictEqual(exitCode, 0); + }) + ); + + it( + 'should show list help', + initCLI(async ({ run }) => { + const { exitCode, stdout } = await run(['module', 'list', '-h']); + + const output = stripColor(stdout); + assert.match(output, /Titanium Command-Line Interface/); + assert.match(output, /Usage: titanium module list|ls/); + assert.match(output, /List Options:/); + assert.match(output, /Global Options:/); + + assert.strictEqual(exitCode, 0); + }) + ); + + it( + 'should list no installed modules', + initCLI(async ({ run }) => { + const { exitCode, stdout } = await run(['module']); + + const output = stripColor(stdout); + assert.match(output, /Configured Path Modules/); + assert.match(output, /Global Modules/); + + assert.strictEqual(exitCode, 0); + }) + ); + + it( + 'should list no installed modules as JSON', + initCLI(async ({ run }) => { + const { exitCode, stdout } = await run(['module', '--json']); + + const json = JSON.parse(stdout); + assert.deepStrictEqual(json, { + project: {}, + config: {}, + global: {}, + }); + + assert.strictEqual(exitCode, 0); + }) + ); + + it( + 'should list installed modules', + initCLI(async ({ run }) => { + const { exitCode, stdout } = await run([ + 'module', + '--config', + JSON.stringify({ + paths: { + modules: [join(fixturesDir, 'module')], + }, + }), + ]); + + const output = stripColor(stdout); + assert.match( + output, + new RegExp(`Configured Path Modules Android com.test.module 1.0.0 ${join(fixturesDir, 'module', 'android', 'test-module', '1.0.0').replace(/\\/g, '\\\\')} @@ -89,168 +103,152 @@ describe('ti module', () => { 1.0.0 ${join(fixturesDir, 'module', 'windows', 'test-module', '1.0.0').replace(/\\/g, '\\\\')} Global Modules - No modules found`)); - - assert.strictEqual(exitCode, 0); - })); - - it('should list installed modules as JSON', initCLI(async ({ run }) => { - const { exitCode, stdout } = await run([ - 'module', - '--config', - JSON.stringify({ - paths: { - modules: [ - join(fixturesDir, 'module') - ] - } - }), - '--json' - ]); - - const json = JSON.parse(stdout); - assert.deepStrictEqual(json, { - project: {}, - config: { - android: { - 'com.test.module': { - '1.0.0': { - version: '1.0.0', - modulePath: join(fixturesDir, 'module', 'android', 'test-module', '1.0.0'), - manifest: { + No modules found`) + ); + + assert.strictEqual(exitCode, 0); + }) + ); + + it( + 'should list installed modules as JSON', + initCLI(async ({ run }) => { + const { exitCode, stdout } = await run([ + 'module', + '--config', + JSON.stringify({ + paths: { + modules: [join(fixturesDir, 'module')], + }, + }), + '--json', + ]); + + const json = JSON.parse(stdout); + assert.deepStrictEqual(json, { + project: {}, + config: { + android: { + 'com.test.module': { + '1.0.0': { version: '1.0.0', - apiversion: 4, - architectures: [ - 'arm64-v8a', - 'armeabi-v7a', - 'x86' - ], - description: 'testModule', - author: 'Your Name', - license: 'Specify your license', - copyright: 'Copyright (c) 2018 by Your Company', - name: 'testModule', - moduleid: 'com.test.module', - guid: 'dcaea77e-2860-42c1-a57b-319f81da10e0', - platform: 'android', - minsdk: '7.2.0' + modulePath: join(fixturesDir, 'module', 'android', 'test-module', '1.0.0'), + manifest: { + version: '1.0.0', + apiversion: 4, + architectures: ['arm64-v8a', 'armeabi-v7a', 'x86'], + description: 'testModule', + author: 'Your Name', + license: 'Specify your license', + copyright: 'Copyright (c) 2018 by Your Company', + name: 'testModule', + moduleid: 'com.test.module', + guid: 'dcaea77e-2860-42c1-a57b-319f81da10e0', + platform: 'android', + minsdk: '7.2.0', + }, + platform: ['android'], }, - platform: [ - 'android' - ] - } - } - }, - commonjs: { - 'com.test.module': { - '1.0': { - version: '1.0', - modulePath: join(fixturesDir, 'module', 'commonjs', 'invalid-version', '1.0.1'), - manifest: { + }, + }, + commonjs: { + 'com.test.module': { + '1.0': { version: '1.0', - description: 'testModule', - author: 'Your Name', - license: 'Specify your license', - copyright: 'Copyright (c) 2018 by Your Company', - name: 'testModule', - moduleid: 'com.test.module', - guid: 'dcaea77e-2860-42c1-a57b-319f81da10e0', - platform: 'commonjs', - minsdk: '7.2.0' + modulePath: join(fixturesDir, 'module', 'commonjs', 'invalid-version', '1.0.1'), + manifest: { + version: '1.0', + description: 'testModule', + author: 'Your Name', + license: 'Specify your license', + copyright: 'Copyright (c) 2018 by Your Company', + name: 'testModule', + moduleid: 'com.test.module', + guid: 'dcaea77e-2860-42c1-a57b-319f81da10e0', + platform: 'commonjs', + minsdk: '7.2.0', + }, + platform: ['commonjs'], }, - platform: [ - 'commonjs' - ] - }, - '1.0.0': { - version: '1.0.0', - modulePath: join(fixturesDir, 'module', 'commonjs', 'test-module', '1.0.0'), - manifest: { + '1.0.0': { version: '1.0.0', - description: 'testModule', - author: 'Your Name', - license: 'Specify your license', - copyright: 'Copyright (c) 2018 by Your Company', - name: 'testModule', - moduleid: 'com.test.module', - guid: 'dcaea77e-2860-42c1-a57b-319f81da10e0', - platform: 'commonjs', - minsdk: '7.2.0' + modulePath: join(fixturesDir, 'module', 'commonjs', 'test-module', '1.0.0'), + manifest: { + version: '1.0.0', + description: 'testModule', + author: 'Your Name', + license: 'Specify your license', + copyright: 'Copyright (c) 2018 by Your Company', + name: 'testModule', + moduleid: 'com.test.module', + guid: 'dcaea77e-2860-42c1-a57b-319f81da10e0', + platform: 'commonjs', + minsdk: '7.2.0', + }, + platform: ['commonjs'], }, - platform: [ - 'commonjs' - ] - } - } - }, - ios: { - 'com.test.module': { - '1.0.0': { - version: '1.0.0', - modulePath: join(fixturesDir, 'module', 'iphone', 'test-module', '1.0.0'), - manifest: { + }, + }, + ios: { + 'com.test.module': { + '1.0.0': { version: '1.0.0', - apiversion: 2, - architectures: [ - 'armv7', - 'arm64', - 'i386', - 'x86_64' - ], - description: 'testModule', - author: 'Your Name', - license: 'Specify your license', - copyright: 'Copyright (c) 2018 by Your Company', - name: 'testModule', - moduleid: 'com.test.module', - guid: 'dcaea77e-2860-42c1-a57b-319f81da10e0', - platform: 'ios', - minsdk: '7.2.0' + modulePath: join(fixturesDir, 'module', 'iphone', 'test-module', '1.0.0'), + manifest: { + version: '1.0.0', + apiversion: 2, + architectures: ['armv7', 'arm64', 'i386', 'x86_64'], + description: 'testModule', + author: 'Your Name', + license: 'Specify your license', + copyright: 'Copyright (c) 2018 by Your Company', + name: 'testModule', + moduleid: 'com.test.module', + guid: 'dcaea77e-2860-42c1-a57b-319f81da10e0', + platform: 'ios', + minsdk: '7.2.0', + }, + platform: ['ios'], }, - platform: [ - 'ios' - ] - } - } - }, - windows: { - 'com.test.module': { - '1.0.0': { - version: '1.0.0', - modulePath: join(fixturesDir, 'module', 'windows', 'test-module', '1.0.0'), - manifest: { + }, + }, + windows: { + 'com.test.module': { + '1.0.0': { version: '1.0.0', - apiversion: 4, - architectures: [ - 'ARM', - 'x86' - ], - description: 'testModule', - author: 'Your Name', - license: 'Specify your license', - copyright: 'Copyright (c) 2018 by Your Company', - name: 'testModule', - moduleid: 'com.test.module', - guid: 'dcaea77e-2860-42c1-a57b-319f81da10e0', - platform: 'windows', - minsdk: '7.2.0' + modulePath: join(fixturesDir, 'module', 'windows', 'test-module', '1.0.0'), + manifest: { + version: '1.0.0', + apiversion: 4, + architectures: ['ARM', 'x86'], + description: 'testModule', + author: 'Your Name', + license: 'Specify your license', + copyright: 'Copyright (c) 2018 by Your Company', + name: 'testModule', + moduleid: 'com.test.module', + guid: 'dcaea77e-2860-42c1-a57b-319f81da10e0', + platform: 'windows', + minsdk: '7.2.0', + }, + platform: ['windows'], }, - platform: [ - 'windows' - ] - } - } - } - }, - global: {} - }); + }, + }, + }, + global: {}, + }); - assert.strictEqual(exitCode, 0); - })); + assert.strictEqual(exitCode, 0); + }) + ); - it('should install module during detection', initCLI(async ({ run }) => { - const { exitCode, _stdout } = await run(['module']); + it( + 'should install module during detection', + initCLI(async ({ run }) => { + const { exitCode, _stdout } = await run(['module']); - assert.strictEqual(exitCode, 0); - })); + assert.strictEqual(exitCode, 0); + }) + ); }); diff --git a/test/commands/ti-project.test.js b/test/commands/ti-project.test.js index d51d13fbb..366a89945 100644 --- a/test/commands/ti-project.test.js +++ b/test/commands/ti-project.test.js @@ -1,20 +1,23 @@ -import { describe, it } from 'node:test'; -import assert from 'node:assert'; -import { stripColor } from '../helpers/strip-color.js'; import { initMockSDKHome } from '../helpers/init-sdk-home.js'; +import { stripColor } from '../helpers/strip-color.js'; +import assert from 'node:assert'; +import { describe, it } from 'node:test'; describe('ti project', () => { - it('should show help', initMockSDKHome(async ({ run, _tmpSDKDir }) => { - const { exitCode, stdout } = await run(['project', '-h']); + it( + 'should show help', + initMockSDKHome(async ({ run, _tmpSDKDir }) => { + const { exitCode, stdout } = await run(['project', '-h']); - const output = stripColor(stdout); - assert.match(output, /Titanium Command-Line Interface/); - assert.match(output, /Usage: titanium project \[options\]/); - assert.match(output, /Get and set tiapp.xml settings./); - assert.match(output, /Project Arguments:/); - assert.match(output, /Project Options:/); - assert.match(output, /Global Options:/); + const output = stripColor(stdout); + assert.match(output, /Titanium Command-Line Interface/); + assert.match(output, /Usage: titanium project \[options\]/); + assert.match(output, /Get and set tiapp.xml settings./); + assert.match(output, /Project Arguments:/); + assert.match(output, /Project Options:/); + assert.match(output, /Global Options:/); - assert.strictEqual(exitCode, 0); - })); + assert.strictEqual(exitCode, 0); + }) + ); }); diff --git a/test/commands/ti-sdk.test.js b/test/commands/ti-sdk.test.js index 429b485b6..552fec82a 100644 --- a/test/commands/ti-sdk.test.js +++ b/test/commands/ti-sdk.test.js @@ -1,10 +1,10 @@ -import { describe, it } from 'node:test'; -import assert from 'node:assert'; -import fs from 'fs-extra'; import { initCLI } from '../helpers/init-cli.js'; import { initSDKHome, initMockSDKHome } from '../helpers/init-sdk-home.js'; import { stripColor } from '../helpers/strip-color.js'; +import fs from 'fs-extra'; +import assert from 'node:assert'; import { join } from 'node:path'; +import { describe, it } from 'node:test'; import { fileURLToPath, pathToFileURL } from 'node:url'; const fixturesDir = join(fileURLToPath(import.meta.url), '../fixtures/sdk'); @@ -19,322 +19,420 @@ if (process.platform === 'darwin') { describe('ti sdk', () => { describe('help', () => { - it('should show help', initCLI(async ({ run }) => { - const { exitCode, stdout } = await run(['sdk', '-h']); - - const output = stripColor(stdout); - assert.match(output, /Titanium Command-Line Interface/); - assert.match(output, /Usage: titanium sdk/); - assert.match(output, /Commands:/); - assert.match(output, /SDK Options:/); - assert.match(output, /Global Options:/); - - assert.strictEqual(exitCode, 0); - })); - - it('should show install help', initCLI(async ({ run }) => { - const { exitCode, stdout } = await run(['sdk', 'install', '-h']); - - const output = stripColor(stdout); - assert.match(output, /Titanium Command-Line Interface/); - assert.match(output, /Usage: titanium sdk install|i/); - assert.match(output, /Install Arguments:/); - assert.match(output, /Install Options:/); - assert.match(output, /Global Options:/); - - assert.strictEqual(exitCode, 0); - })); - - it('should show list help', initCLI(async ({ run }) => { - const { exitCode, stdout } = await run(['sdk', 'list', '-h']); - - const output = stripColor(stdout); - assert.match(output, /Titanium Command-Line Interface/); - assert.match(output, /Usage: titanium sdk list|ls/); - assert.match(output, /List Options:/); - assert.match(output, /Global Options:/); - - assert.strictEqual(exitCode, 0); - })); - - it('should show uninstall help', initCLI(async ({ run }) => { - const { exitCode, stdout } = await run(['sdk', 'uninstall', '-h']); - - const output = stripColor(stdout); - assert.match(output, /Titanium Command-Line Interface/); - assert.match(output, /Usage: titanium sdk uninstall|rm/); - assert.match(output, /Uninstall Arguments:/); - assert.match(output, /Uninstall Options:/); - assert.match(output, /Global Options:/); - - assert.strictEqual(exitCode, 0); - })); + it( + 'should show help', + initCLI(async ({ run }) => { + const { exitCode, stdout } = await run(['sdk', '-h']); + + const output = stripColor(stdout); + assert.match(output, /Titanium Command-Line Interface/); + assert.match(output, /Usage: titanium sdk/); + assert.match(output, /Commands:/); + assert.match(output, /SDK Options:/); + assert.match(output, /Global Options:/); + + assert.strictEqual(exitCode, 0); + }) + ); + + it( + 'should show install help', + initCLI(async ({ run }) => { + const { exitCode, stdout } = await run(['sdk', 'install', '-h']); + + const output = stripColor(stdout); + assert.match(output, /Titanium Command-Line Interface/); + assert.match(output, /Usage: titanium sdk install|i/); + assert.match(output, /Install Arguments:/); + assert.match(output, /Install Options:/); + assert.match(output, /Global Options:/); + + assert.strictEqual(exitCode, 0); + }) + ); + + it( + 'should show list help', + initCLI(async ({ run }) => { + const { exitCode, stdout } = await run(['sdk', 'list', '-h']); + + const output = stripColor(stdout); + assert.match(output, /Titanium Command-Line Interface/); + assert.match(output, /Usage: titanium sdk list|ls/); + assert.match(output, /List Options:/); + assert.match(output, /Global Options:/); + + assert.strictEqual(exitCode, 0); + }) + ); + + it( + 'should show uninstall help', + initCLI(async ({ run }) => { + const { exitCode, stdout } = await run(['sdk', 'uninstall', '-h']); + + const output = stripColor(stdout); + assert.match(output, /Titanium Command-Line Interface/); + assert.match(output, /Usage: titanium sdk uninstall|rm/); + assert.match(output, /Uninstall Arguments:/); + assert.match(output, /Uninstall Options:/); + assert.match(output, /Global Options:/); + + assert.strictEqual(exitCode, 0); + }) + ); }); describe('install', () => { - it('should install an SDK and remove it', initSDKHome(async ({ run, tmpHome, tmpSDKDir }) => { - const sdkPath = join(tmpSDKDir, 'mobilesdk', os, sdkName); - - // list SDKs (no SDKs installed) - // eslint-disable-next-line no-unused-vars - let { exitCode, stdout, stderr } = await run(['sdk']); // no `ls` to test default subcommand - let output = stripColor(stdout); - assert.match(output, /Titanium Command-Line Interface/); - assert.match(output, new RegExp(`SDK Install Locations:\n[\\s\\S]*${tmpSDKDir.replace(/\\/g, '\\\\')}`)); - assert.match(output, /No Titanium SDKs found/); - - // list SDKs as JSON (no SDKs installed) - ({ exitCode, stdout } = await run(['sdk', 'ls', '--json'])); - let json = JSON.parse(stdout); - assert(json.installLocations.includes(tmpSDKDir)); - assert.deepStrictEqual(json, { - branch: {}, - branches: { - defaultBranch: 'main', - branches: [] - }, - defaultInstallLocation: tmpSDKDir, - installLocations: json.installLocations, - installed: {}, - releases: {}, - sdks: {} - }); - assert.strictEqual(exitCode, 0); - - // install an SDK - ({ exitCode, stdout, stderr } = await run(['sdk', 'install', sdkName, '--no-progress-bars', '--keep-files'])); - assert.match(stdout, /successfully installed/); - assert.strictEqual(exitCode, 0); - - // find the downloaded file and move it to the tmp dir for subsequent tests - const src = join(tmpHome, '.titanium', 'downloads', sdkFilename); - if (fs.existsSync(src)) { - await fs.remove(src); - } else { - throw new Error(`SDK file does not exist: ${src}`); - } - - // list SDKs - ({ exitCode, stdout } = await run(['sdk', 'ls'])); - output = stripColor(stdout); - assert.match(output, /Titanium Command-Line Interface/); - assert.match(output, new RegExp(`SDK Install Locations:\n[\\s\\S]*${tmpSDKDir.replace(/\\/g, '\\\\')}`)); - assert.match(output, new RegExp(`Installed SDKs:\n\\s*${sdkName}\\s+${sdkVersion}\\s+${ - sdkPath.replace(/\\/g, '\\\\') - }`)); - assert.strictEqual(exitCode, 0); - - // list SDKs as JSON - ({ exitCode, stdout } = await run(['sdk', 'ls', '--json'])); - json = JSON.parse(stdout); - assert.deepStrictEqual(json, { - branch: {}, - branches: { - defaultBranch: 'main', - branches: [] - }, - defaultInstallLocation: tmpSDKDir, - installLocations: json.installLocations, - installed: { - [sdkName]: sdkPath - }, - releases: {}, - sdks: { - [sdkName]: { - name: sdkName, - manifest: { + it( + 'should install an SDK and remove it', + initSDKHome(async ({ run, tmpHome, tmpSDKDir }) => { + const sdkPath = join(tmpSDKDir, 'mobilesdk', os, sdkName); + + // list SDKs (no SDKs installed) + // eslint-disable-next-line no-unused-vars + let { exitCode, stdout, stderr } = await run(['sdk']); // no `ls` to test default subcommand + let output = stripColor(stdout); + assert.match(output, /Titanium Command-Line Interface/); + assert.match( + output, + new RegExp(`SDK Install Locations:\n[\\s\\S]*${tmpSDKDir.replace(/\\/g, '\\\\')}`) + ); + assert.match(output, /No Titanium SDKs found/); + + // list SDKs as JSON (no SDKs installed) + ({ exitCode, stdout } = await run(['sdk', 'ls', '--json'])); + let json = JSON.parse(stdout); + assert(json.installLocations.includes(tmpSDKDir)); + assert.deepStrictEqual(json, { + branch: {}, + branches: { + defaultBranch: 'main', + branches: [], + }, + defaultInstallLocation: tmpSDKDir, + installLocations: json.installLocations, + installed: {}, + releases: {}, + sdks: {}, + }); + assert.strictEqual(exitCode, 0); + + // install an SDK + ({ exitCode, stdout, stderr } = await run([ + 'sdk', + 'install', + sdkName, + '--no-progress-bars', + '--keep-files', + ])); + assert.match(stdout, /successfully installed/); + assert.strictEqual(exitCode, 0); + + // find the downloaded file and move it to the tmp dir for subsequent tests + const src = join(tmpHome, '.titanium', 'downloads', sdkFilename); + if (fs.existsSync(src)) { + await fs.remove(src); + } else { + throw new Error(`SDK file does not exist: ${src}`); + } + + // list SDKs + ({ exitCode, stdout } = await run(['sdk', 'ls'])); + output = stripColor(stdout); + assert.match(output, /Titanium Command-Line Interface/); + assert.match( + output, + new RegExp(`SDK Install Locations:\n[\\s\\S]*${tmpSDKDir.replace(/\\/g, '\\\\')}`) + ); + assert.match( + output, + new RegExp( + `Installed SDKs:\n\\s*${sdkName}\\s+${sdkVersion}\\s+${sdkPath.replace(/\\/g, '\\\\')}` + ) + ); + assert.strictEqual(exitCode, 0); + + // list SDKs as JSON + ({ exitCode, stdout } = await run(['sdk', 'ls', '--json'])); + json = JSON.parse(stdout); + assert.deepStrictEqual(json, { + branch: {}, + branches: { + defaultBranch: 'main', + branches: [], + }, + defaultInstallLocation: tmpSDKDir, + installLocations: json.installLocations, + installed: { + [sdkName]: sdkPath, + }, + releases: {}, + sdks: { + [sdkName]: { name: sdkName, - version: sdkName.replace(/.GA$/, ''), - moduleAPIVersion: { - iphone: '2', - android: '4' + manifest: { + name: sdkName, + version: sdkName.replace(/.GA$/, ''), + moduleAPIVersion: { + iphone: '2', + android: '4', + }, + timestamp: '9/15/2023 09:06', + githash: '03d8a831eb', + platforms, }, - timestamp: '9/15/2023 09:06', - githash: '03d8a831eb', - platforms + path: sdkPath, + type: 'ga', + version: sdkName.replace(/.GA$/, ''), }, - path: sdkPath, - type: 'ga', - version: sdkName.replace(/.GA$/, '') }, - } - }); - assert.strictEqual(exitCode, 0); - - // remove the SDK - ({ exitCode, stdout } = await run(['sdk', 'uninstall', sdkName, '--force'])); - assert.match(stdout, /removed/); - assert.strictEqual(exitCode, 0); - - // verify removed - ({ exitCode, stdout } = await run(['sdk', 'ls', '--json'])); - json = JSON.parse(stdout); - assert.deepStrictEqual(json, { - branch: {}, - branches: { - defaultBranch: 'main', - branches: [] - }, - defaultInstallLocation: tmpSDKDir, - installLocations: json.installLocations, - installed: {}, - releases: {}, - sdks: {} - }); - assert.strictEqual(exitCode, 0); - }), 240000); - - it('should install an SDK from a local zip', initSDKHome(async ({ run, tmpSDKDir }) => { - const sdkZipFile = join(fixturesDir, 'mock-sdk.zip'); - const sdkName = '0.0.0.GA'; - const sdkPath = join(tmpSDKDir, 'mobilesdk', os, sdkName); - let { exitCode, stdout } = await run(['sdk', 'install', sdkZipFile, '--no-progress-bars']); - assert.match(stdout, /successfully installed/); - assert.strictEqual(exitCode, 0); - - ({ exitCode, stdout } = await run(['sdk', 'ls', '--json'])); - const json = JSON.parse(stdout); - assert.deepStrictEqual(json, { - branch: {}, - branches: { - defaultBranch: 'main', - branches: [] - }, - defaultInstallLocation: tmpSDKDir, - installLocations: json.installLocations, - installed: { - [sdkName]: sdkPath - }, - releases: {}, - sdks: { - [sdkName]: { - name: sdkName, - manifest: { + }); + assert.strictEqual(exitCode, 0); + + // remove the SDK + ({ exitCode, stdout } = await run(['sdk', 'uninstall', sdkName, '--force'])); + assert.match(stdout, /removed/); + assert.strictEqual(exitCode, 0); + + // verify removed + ({ exitCode, stdout } = await run(['sdk', 'ls', '--json'])); + json = JSON.parse(stdout); + assert.deepStrictEqual(json, { + branch: {}, + branches: { + defaultBranch: 'main', + branches: [], + }, + defaultInstallLocation: tmpSDKDir, + installLocations: json.installLocations, + installed: {}, + releases: {}, + sdks: {}, + }); + assert.strictEqual(exitCode, 0); + }), + 240000 + ); + + it( + 'should install an SDK from a local zip', + initSDKHome(async ({ run, tmpSDKDir }) => { + const sdkZipFile = join(fixturesDir, 'mock-sdk.zip'); + const sdkName = '0.0.0.GA'; + const sdkPath = join(tmpSDKDir, 'mobilesdk', os, sdkName); + let { exitCode, stdout } = await run(['sdk', 'install', sdkZipFile, '--no-progress-bars']); + assert.match(stdout, /successfully installed/); + assert.strictEqual(exitCode, 0); + + ({ exitCode, stdout } = await run(['sdk', 'ls', '--json'])); + const json = JSON.parse(stdout); + assert.deepStrictEqual(json, { + branch: {}, + branches: { + defaultBranch: 'main', + branches: [], + }, + defaultInstallLocation: tmpSDKDir, + installLocations: json.installLocations, + installed: { + [sdkName]: sdkPath, + }, + releases: {}, + sdks: { + [sdkName]: { name: sdkName, - version: sdkName.replace(/.GA$/, ''), - moduleAPIVersion: { - iphone: '2', - android: '4', - windows: '6' + manifest: { + name: sdkName, + version: sdkName.replace(/.GA$/, ''), + moduleAPIVersion: { + iphone: '2', + android: '4', + windows: '6', + }, + githash: '1234567890', + platforms: ['android'], }, - githash: '1234567890', - platforms: ['android'] + path: sdkPath, + type: 'ga', + version: sdkName.replace(/.GA$/, ''), }, - path: sdkPath, - type: 'ga', - version: sdkName.replace(/.GA$/, '') }, - } - }); - assert.strictEqual(exitCode, 0); - }), 120000); - - it('should error if local zip does not exist', initSDKHome(async ({ run }) => { - const result = await run([ - 'sdk', - 'install', - pathToFileURL(join(fixturesDir, 'does_not_exist')).toString(), - '--no-progress-bars' - ]); - const { exitCode, stderr } = result; - assert.match(stderr, /Specified file does not exist/); - assert.strictEqual(exitCode, 1); - })); - - it('should error if local zip is not a .zip', initSDKHome(async ({ run }) => { - const { exitCode, stderr } = await run(['sdk', 'install', join(fixturesDir, 'not_a_zip'), '--no-progress-bars']); - assert.match(stderr, /Specified file is not a zip file/); - assert.strictEqual(exitCode, 1); - })); - - it.skip('should install an SDK from a URL', initSDKHome(async ({ _run }) => { - // const { exitCode, stderr } = await run(['sdk', 'install', 'https://titaniumsdk.com/', '--no-progress-bars']); - // assert.match(stderr, /Specified file does not exist/); - // assert.strictEqual(exitCode, 1); - })); - - it('should install an SDK from a branch', initSDKHome(async ({ _run }) => { - // TODO - })); - - it('should error if SDK release not found', initSDKHome(async ({ run }) => { - const { exitCode, stderr } = await run(['sdk', 'install', 'foo', '--no-progress-bars']); - assert.match(stderr, /Unable to find any Titanium SDK releases or CI builds that match "foo"/); - assert.strictEqual(exitCode, 1); - })); + }); + assert.strictEqual(exitCode, 0); + }), + 120000 + ); + + it( + 'should error if local zip does not exist', + initSDKHome(async ({ run }) => { + const result = await run([ + 'sdk', + 'install', + pathToFileURL(join(fixturesDir, 'does_not_exist')).toString(), + '--no-progress-bars', + ]); + const { exitCode, stderr } = result; + assert.match(stderr, /Specified file does not exist/); + assert.strictEqual(exitCode, 1); + }) + ); + + it( + 'should error if local zip is not a .zip', + initSDKHome(async ({ run }) => { + const { exitCode, stderr } = await run([ + 'sdk', + 'install', + join(fixturesDir, 'not_a_zip'), + '--no-progress-bars', + ]); + assert.match(stderr, /Specified file is not a zip file/); + assert.strictEqual(exitCode, 1); + }) + ); + + it.skip( + 'should install an SDK from a URL', + initSDKHome(async ({ _run }) => { + // const { exitCode, stderr } = await run(['sdk', 'install', 'https://titaniumsdk.com/', '--no-progress-bars']); + // assert.match(stderr, /Specified file does not exist/); + // assert.strictEqual(exitCode, 1); + }) + ); + + it( + 'should install an SDK from a branch', + initSDKHome(async ({ _run }) => { + // TODO + }) + ); + + it( + 'should error if SDK release not found', + initSDKHome(async ({ run }) => { + const { exitCode, stderr } = await run(['sdk', 'install', 'foo', '--no-progress-bars']); + assert.match( + stderr, + /Unable to find any Titanium SDK releases or CI builds that match "foo"/ + ); + assert.strictEqual(exitCode, 1); + }) + ); }); describe('list', () => { - it('should list releases, branches, and builds', initSDKHome(async ({ run, tmpSDKDir }) => { - // list branches - let { exitCode, stdout } = await run(['sdk', 'list', '-b']); - let output = stripColor(stdout); - assert.match(output, /Titanium Command-Line Interface/); - assert.match(output, new RegExp(`SDK Install Locations:\n[\\s\\S]*${tmpSDKDir.replace(/\\/g, '\\\\')}`)); - assert.match(output, /Branches:\n\s*(main|master)/); - - // list stable releases - ({ exitCode, stdout } = await run(['sdk', 'list', '-r'])); - output = stripColor(stdout); - assert.match(output, /Titanium Command-Line Interface/); - assert.match(output, new RegExp(`SDK Install Locations:\n[\\s\\S]*${tmpSDKDir.replace(/\\/g, '\\\\')}`)); - assert.match(output, /Releases:/); - assert.match(output, /12\.2\.0\.GA\s+9\/15\/23/); - - // list stable and unstable releases - ({ exitCode, stdout } = await run(['sdk', 'list', '-u'])); - output = stripColor(stdout); - assert.match(output, /Titanium Command-Line Interface/); - assert.match(output, new RegExp(`SDK Install Locations:\n[\\s\\S]*${tmpSDKDir.replace(/\\/g, '\\\\')}`)); - assert.match(output, /Releases:/); - assert.match(output, /12\.2\.0\.GA\s+9\/15\/23/); - assert.match(output, /12\.2\.0\.RC\s+8\/11\/23/); - - // list branch builds - ({ exitCode, stdout } = await run(['sdk', 'list', '--branch', 'main'])); - output = stripColor(stdout); - assert.match(output, /Titanium Command-Line Interface/); - assert.match(output, new RegExp(`SDK Install Locations:\n[\\s\\S]*${tmpSDKDir.replace(/\\/g, '\\\\')}`)); - assert.match(output, /'main' Branch Builds:/); - // assert.match(output, /\d+\.\d+\.\d+\.v\d+\s+\d+\/\d+\/\d+\s+\d+(\.\d+)? .B \[unstable\]/); - - // list branches, stable, and unstable releases as JSON - ({ exitCode, stdout } = await run(['sdk', 'ls', '-bu', '--json'])); - const json = JSON.parse(stdout); - assert(json.branches.branches.includes('main') || json.branches.branches.includes('master')); - assert(json.branches.branches.includes('12_6_X')); - assert(json.releases[sdkName]); - - assert.strictEqual(exitCode, 0); - }), 60000); - - it('should not find any SDKs in empty SDK home directory', initSDKHome(async ({ run, tmpSDKDir }) => { - const { exitCode, stdout } = await run(['sdk', 'list']); - const output = stripColor(stdout); - assert.match(output, new RegExp(`SDK Install Locations:\n[\\s\\S]*${tmpSDKDir.replace(/\\/g, '\\\\')}`)); - assert.match(output, /No Titanium SDKs found/); - assert.strictEqual(exitCode, 0); - }), 60000); - - it('should list SDKs in SDK home directory', initMockSDKHome(async ({ run, tmpSDKDir }) => { - const { exitCode, stdout } = await run(['sdk', 'list']); - const output = stripColor(stdout); - assert.match(output, new RegExp(`SDK Install Locations:\n[\\s\\S]*${tmpSDKDir.replace(/\\/g, '\\\\')}`)); - assert.match(output, /Installed SDKs:/); - assert.match(output, new RegExp(`0.0.0.GA\\s+0.0.0\\s+${join(tmpSDKDir, 'mobilesdk', os, '0.0.0.GA').replace(/\\/g, '\\\\')}`)); - assert.strictEqual(exitCode, 0); - }), 60000); + it( + 'should list releases, branches, and builds', + initSDKHome(async ({ run, tmpSDKDir }) => { + // list branches + let { exitCode, stdout } = await run(['sdk', 'list', '-b']); + let output = stripColor(stdout); + assert.match(output, /Titanium Command-Line Interface/); + assert.match( + output, + new RegExp(`SDK Install Locations:\n[\\s\\S]*${tmpSDKDir.replace(/\\/g, '\\\\')}`) + ); + assert.match(output, /Branches:\n\s*(main|master)/); + + // list stable releases + ({ exitCode, stdout } = await run(['sdk', 'list', '-r'])); + output = stripColor(stdout); + assert.match(output, /Titanium Command-Line Interface/); + assert.match( + output, + new RegExp(`SDK Install Locations:\n[\\s\\S]*${tmpSDKDir.replace(/\\/g, '\\\\')}`) + ); + assert.match(output, /Releases:/); + assert.match(output, /12\.2\.0\.GA\s+9\/15\/23/); + + // list stable and unstable releases + ({ exitCode, stdout } = await run(['sdk', 'list', '-u'])); + output = stripColor(stdout); + assert.match(output, /Titanium Command-Line Interface/); + assert.match( + output, + new RegExp(`SDK Install Locations:\n[\\s\\S]*${tmpSDKDir.replace(/\\/g, '\\\\')}`) + ); + assert.match(output, /Releases:/); + assert.match(output, /12\.2\.0\.GA\s+9\/15\/23/); + assert.match(output, /12\.2\.0\.RC\s+8\/11\/23/); + + // list branch builds + ({ exitCode, stdout } = await run(['sdk', 'list', '--branch', 'main'])); + output = stripColor(stdout); + assert.match(output, /Titanium Command-Line Interface/); + assert.match( + output, + new RegExp(`SDK Install Locations:\n[\\s\\S]*${tmpSDKDir.replace(/\\/g, '\\\\')}`) + ); + assert.match(output, /'main' Branch Builds:/); + // assert.match(output, /\d+\.\d+\.\d+\.v\d+\s+\d+\/\d+\/\d+\s+\d+(\.\d+)? .B \[unstable\]/); + + // list branches, stable, and unstable releases as JSON + ({ exitCode, stdout } = await run(['sdk', 'ls', '-bu', '--json'])); + const json = JSON.parse(stdout); + assert( + json.branches.branches.includes('main') || json.branches.branches.includes('master') + ); + assert(json.branches.branches.includes('12_6_X')); + assert(json.releases[sdkName]); + + assert.strictEqual(exitCode, 0); + }), + 60000 + ); + + it( + 'should not find any SDKs in empty SDK home directory', + initSDKHome(async ({ run, tmpSDKDir }) => { + const { exitCode, stdout } = await run(['sdk', 'list']); + const output = stripColor(stdout); + assert.match( + output, + new RegExp(`SDK Install Locations:\n[\\s\\S]*${tmpSDKDir.replace(/\\/g, '\\\\')}`) + ); + assert.match(output, /No Titanium SDKs found/); + assert.strictEqual(exitCode, 0); + }), + 60000 + ); + + it( + 'should list SDKs in SDK home directory', + initMockSDKHome(async ({ run, tmpSDKDir }) => { + const { exitCode, stdout } = await run(['sdk', 'list']); + const output = stripColor(stdout); + assert.match( + output, + new RegExp(`SDK Install Locations:\n[\\s\\S]*${tmpSDKDir.replace(/\\/g, '\\\\')}`) + ); + assert.match(output, /Installed SDKs:/); + assert.match( + output, + new RegExp( + `0.0.0.GA\\s+0.0.0\\s+${join(tmpSDKDir, 'mobilesdk', os, '0.0.0.GA').replace(/\\/g, '\\\\')}` + ) + ); + assert.strictEqual(exitCode, 0); + }), + 60000 + ); }); describe('select', () => { - it('should show message for select command', initCLI(async ({ run }) => { - const { exitCode, stdout } = await run(['sdk', 'select']); - - const output = stripColor(stdout); - assert.match(output, /Titanium Command-Line Interface/); - assert.match(output, /The "select" subcommand is no longer required./); - - assert.strictEqual(exitCode, 0); - })); + it( + 'should show message for select command', + initCLI(async ({ run }) => { + const { exitCode, stdout } = await run(['sdk', 'select']); + + const output = stripColor(stdout); + assert.match(output, /Titanium Command-Line Interface/); + assert.match(output, /The "select" subcommand is no longer required./); + + assert.strictEqual(exitCode, 0); + }) + ); }); }); diff --git a/test/commands/ti-setup.test.js b/test/commands/ti-setup.test.js index a70db7b50..e22206496 100644 --- a/test/commands/ti-setup.test.js +++ b/test/commands/ti-setup.test.js @@ -1,19 +1,22 @@ -import { describe, it } from 'node:test'; -import assert from 'node:assert'; import { initCLI } from '../helpers/init-cli.js'; import { stripColor } from '../helpers/strip-color.js'; +import assert from 'node:assert'; +import { describe, it } from 'node:test'; describe('ti setup', () => { - it('should show help', initCLI(async ({ run }) => { - const { exitCode, stdout } = await run(['setup', '-h']); + it( + 'should show help', + initCLI(async ({ run }) => { + const { exitCode, stdout } = await run(['setup', '-h']); - const output = stripColor(stdout); - assert.match(output, /Titanium Command-Line Interface/); - assert.match(output, /Usage: titanium setup \[options\] \[screen\]/); - assert.match(output, /Setup Arguments:/); - assert.match(output, /Setup Options:/); - assert.match(output, /Global Options:/); + const output = stripColor(stdout); + assert.match(output, /Titanium Command-Line Interface/); + assert.match(output, /Usage: titanium setup \[options\] \[screen\]/); + assert.match(output, /Setup Arguments:/); + assert.match(output, /Setup Options:/); + assert.match(output, /Global Options:/); - assert.strictEqual(exitCode, 0); - })); + assert.strictEqual(exitCode, 0); + }) + ); }); diff --git a/test/commands/ti.test.js b/test/commands/ti.test.js index b60640343..2d0c0fcc6 100644 --- a/test/commands/ti.test.js +++ b/test/commands/ti.test.js @@ -1,37 +1,46 @@ -import { describe, it } from 'node:test'; +import { initCLI } from '../helpers/init-cli.js'; +import { stripColor } from '../helpers/strip-color.js'; +import fs from 'fs-extra'; import assert from 'node:assert'; import { dirname, join } from 'node:path'; +import { describe, it } from 'node:test'; import { fileURLToPath } from 'node:url'; -import fs from 'fs-extra'; -import { initCLI } from '../helpers/init-cli.js'; -import { stripColor } from '../helpers/strip-color.js'; const __dirname = dirname(fileURLToPath(import.meta.url)); const pkgJson = fs.readJsonSync(join(__dirname, '../../package.json')); describe('ti', () => { - it('should display the version using short flag', initCLI(async ({ run }) => { - const { exitCode, stdout } = await run(['-v']); - assert.strictEqual(stdout, pkgJson.version); - assert.strictEqual(exitCode, 0); - })); + it( + 'should display the version using short flag', + initCLI(async ({ run }) => { + const { exitCode, stdout } = await run(['-v']); + assert.strictEqual(stdout, pkgJson.version); + assert.strictEqual(exitCode, 0); + }) + ); - it('should display the version using long flag', initCLI(async ({ run }) => { - const { exitCode, stdout } = await run(['--version']); - assert.strictEqual(stdout, pkgJson.version); - assert.strictEqual(exitCode, 0); - })); + it( + 'should display the version using long flag', + initCLI(async ({ run }) => { + const { exitCode, stdout } = await run(['--version']); + assert.strictEqual(stdout, pkgJson.version); + assert.strictEqual(exitCode, 0); + }) + ); - it('should display the help', initCLI(async ({ run }) => { - const { exitCode, stdout } = await run(); + it( + 'should display the help', + initCLI(async ({ run }) => { + const { exitCode, stdout } = await run(); - const output = stripColor(stdout); - assert.match(output, /Titanium Command-Line Interface/); - assert.match(output, /Usage: titanium/); - assert.match(output, /Commands:/); - assert.match(output, /Global Options:/); - assert.match(output, /-h, --help/); + const output = stripColor(stdout); + assert.match(output, /Titanium Command-Line Interface/); + assert.match(output, /Usage: titanium/); + assert.match(output, /Commands:/); + assert.match(output, /Global Options:/); + assert.match(output, /-h, --help/); - assert.strictEqual(exitCode, 0); - })); + assert.strictEqual(exitCode, 0); + }) + ); }); diff --git a/test/helpers/init-cli.js b/test/helpers/init-cli.js index b2452f4a8..f641de32c 100644 --- a/test/helpers/init-cli.js +++ b/test/helpers/init-cli.js @@ -1,8 +1,8 @@ +import { initHome } from './init-home.js'; import { execaNode } from 'execa'; +import fs from 'fs-extra'; import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; -import { initHome } from './init-home.js'; -import fs from 'fs-extra'; const __dirname = dirname(fileURLToPath(import.meta.url)); const ti = join(__dirname, '../../src/main.js'); @@ -28,14 +28,14 @@ export function initCLI(fixture, fn, sharedOpts = {}) { ...process.env, ...sharedOpts.env, ...opts.env, - HOME: tmpHome - } + HOME: tmpHome, + }, }); } catch (e) { return e; } }, - tmpHome + tmpHome, }); } finally { await fs.remove(tmpHome); diff --git a/test/helpers/init-sdk-home.js b/test/helpers/init-sdk-home.js index d525f5120..54cb5bc43 100644 --- a/test/helpers/init-sdk-home.js +++ b/test/helpers/init-sdk-home.js @@ -1,8 +1,8 @@ import { initCLI } from './init-cli.js'; import { tmpDirName } from './tmp-dir-name.js'; import fs from 'fs-extra'; -import { fileURLToPath } from 'node:url'; import { join } from 'node:path'; +import { fileURLToPath } from 'node:url'; export function initSDKHome(fn, mock) { const tmpSDKDir = tmpDirName(); @@ -22,7 +22,7 @@ export function initSDKHome(fn, mock) { await run(['config', 'sdk.defaultInstallLocation', tmpSDKDir]); await fn({ ...opts, - tmpSDKDir + tmpSDKDir, }); } finally { await fs.remove(tmpSDKDir); diff --git a/test/mock-sdk/android/cli/commands/_build.js b/test/mock-sdk/android/cli/commands/_build.js index d0b9b3bae..bbe01cba3 100644 --- a/test/mock-sdk/android/cli/commands/_build.js +++ b/test/mock-sdk/android/cli/commands/_build.js @@ -25,13 +25,17 @@ function AndroidBuilder() { this.validABIs = this.packageJson.architectures; this.compileSdkVersion = this.packageJson.compileSDKVersion; // this should always be >= maxSupportedApiLevel this.minSupportedApiLevel = parseInt(this.packageJson.minSDKVersion); - this.minTargetApiLevel = parseInt(version.parseMin(this.packageJson.vendorDependencies['android sdk'])); - this.maxSupportedApiLevel = parseInt(version.parseMax(this.packageJson.vendorDependencies['android sdk'])); + this.minTargetApiLevel = parseInt( + version.parseMin(this.packageJson.vendorDependencies['android sdk']) + ); + this.maxSupportedApiLevel = parseInt( + version.parseMax(this.packageJson.vendorDependencies['android sdk']) + ); this.deployTypes = { emulator: 'development', device: 'test', - 'dist-playstore': 'production' + 'dist-playstore': 'production', }; this.targets = ['emulator', 'device', 'dist-playstore']; @@ -46,9 +50,14 @@ AndroidBuilder.prototype.config = function config(logger, config, cli) { function assertIssue(logger, issues, name) { for (let i = 0; i < issues.length; i++) { - if ((typeof name === 'string' && issues[i].id === name) || (typeof name === 'object' && name.test(issues[i].id))) { + if ( + (typeof name === 'string' && issues[i].id === name) || + (typeof name === 'object' && name.test(issues[i].id)) + ) { issues[i].message.split('\n').forEach(function (line) { - logger[issues[i].type === 'error' ? 'error' : 'warn'](line.replace(/(__(.+?)__)/g, '$2'.bold)); + logger[issues[i].type === 'error' ? 'error' : 'warn']( + line.replace(/(__(.+?)__)/g, '$2'.bold) + ); }); logger.log(); if (issues[i].type === 'error') { @@ -74,7 +83,7 @@ AndroidBuilder.prototype.config = function config(logger, config, cli) { _t.jdkInfo = {}; // mock? // detect Android environment - androidDetect(config, { packageJson: _t.packageJson }, androidInfo => { + androidDetect(config, { packageJson: _t.packageJson }, (androidInfo) => { _t.androidInfo = androidInfo; assertIssue(logger, androidInfo.issues, 'ANDROID_JDK_NOT_FOUND'); assertIssue(logger, androidInfo.issues, 'ANDROID_JDK_PATH_CONTAINS_AMPERSANDS'); @@ -99,81 +108,91 @@ AndroidBuilder.prototype.config = function config(logger, config, cli) { } if (target === 'device') { - new ADB(config).devices(function (err, devices) { - if (err) { - callback(err); - } else { - this.devices = devices.filter(function (d) { - return !d.emulator && d.state === 'device'; - }); - if (this.devices.length > 1) { - // we have more than 1 device, so we should show 'all' - this.devices.push({ - id: 'all', - model: 'All Devices' + new ADB(config).devices( + function (err, devices) { + if (err) { + callback(err); + } else { + this.devices = devices.filter(function (d) { + return !d.emulator && d.state === 'device'; }); + if (this.devices.length > 1) { + // we have more than 1 device, so we should show 'all' + this.devices.push({ + id: 'all', + model: 'All Devices', + }); + } + callback( + null, + (targetDeviceCache[target] = this.devices.map(function (d) { + return { + name: d.model || d.manufacturer, + id: d.id, + version: d.release, + abi: Array.isArray(d.abi) ? d.abi.join(',') : d.abi, + type: 'device', + }; + })) + ); } - callback(null, targetDeviceCache[target] = this.devices.map(function (d) { - return { - name: d.model || d.manufacturer, - id: d.id, - version: d.release, - abi: Array.isArray(d.abi) ? d.abi.join(',') : d.abi, - type: 'device' - }; - })); - } - }.bind(this)); + }.bind(this) + ); } else if (target === 'emulator') { - new EmulatorManager(config).detect(function (err, emus) { - if (err) { - callback(err); - } else { - this.devices = emus; - callback(null, targetDeviceCache[target] = emus.map(function (emu) { - // normalize the emulator info - if (emu.type === 'avd') { - return { - name: emu.name, - id: emu.id, - api: emu['api-level'], - version: emu['sdk-version'], - abi: emu.abi, - type: emu.type, - googleApis: emu.googleApis, - sdcard: emu.sdcard - }; - } else if (emu.type === 'genymotion') { - return { - name: emu.name, - id: emu.name, - api: emu['api-level'], - version: emu['sdk-version'], - abi: emu.abi, - type: emu.type, - googleApis: emu.googleApis, - sdcard: true - }; - } - return emu; // not good - })); - } - }.bind(this)); + new EmulatorManager(config).detect( + function (err, emus) { + if (err) { + callback(err); + } else { + this.devices = emus; + callback( + null, + (targetDeviceCache[target] = emus.map(function (emu) { + // normalize the emulator info + if (emu.type === 'avd') { + return { + name: emu.name, + id: emu.id, + api: emu['api-level'], + version: emu['sdk-version'], + abi: emu.abi, + type: emu.type, + googleApis: emu.googleApis, + sdcard: emu.sdcard, + }; + } else if (emu.type === 'genymotion') { + return { + name: emu.name, + id: emu.name, + api: emu['api-level'], + version: emu['sdk-version'], + abi: emu.abi, + type: emu.type, + googleApis: emu.googleApis, + sdcard: true, + }; + } + return emu; // not good + })) + ); + } + }.bind(this) + ); } else { callback(); } }.bind(this); return function (finished) { - cli.createHook('build.android.config', this, callback => { + cli.createHook('build.android.config', this, (callback) => { const conf = { flags: { launch: { desc: 'disable launching the app after installing', default: true, hideDefault: true, - negate: true - } + negate: true, + }, }, options: { alias: { @@ -182,35 +201,42 @@ AndroidBuilder.prototype.config = function config(logger, config, cli) { hint: 'alias', order: 155, prompt(callback) { - callback(fields.select({ - title: 'What is the name of the keystore\'s certificate alias?', - promptLabel: 'Select a certificate alias by number or name', - margin: '', - optionLabel: 'name', - optionValue: 'name', - numbered: true, - relistOnError: true, - complete: true, - suggest: false, - options: _t.keystoreAliases, - validate: conf.options.alias.validate - })); + callback( + fields.select({ + title: "What is the name of the keystore's certificate alias?", + promptLabel: 'Select a certificate alias by number or name', + margin: '', + optionLabel: 'name', + optionValue: 'name', + numbered: true, + relistOnError: true, + complete: true, + suggest: false, + options: _t.keystoreAliases, + validate: conf.options.alias.validate, + }) + ); }, validate(value, callback) { // if there's a value, then they entered something, otherwise let the CLI prompt if (value) { const selectedAlias = value.toLowerCase(), - alias = _t.keystoreAlias = _t.keystoreAliases.filter(function (a) { return a.name && a.name.toLowerCase() === selectedAlias; }).shift(); + alias = (_t.keystoreAlias = _t.keystoreAliases + .filter(function (a) { + return a.name && a.name.toLowerCase() === selectedAlias; + }) + .shift()); if (!alias) { return callback(new Error(`Invalid "--alias" value "${value}"`)); } } callback(null, value); - } + }, }, 'android-sdk': { abbr: 'A', - default: config.android && config.android.sdkPath && path.resolve(config.android.sdkPath), + default: + config.android && config.android.sdkPath && path.resolve(config.android.sdkPath), desc: 'the path to the Android SDK', hint: 'path', order: 100, @@ -226,22 +252,26 @@ AndroidBuilder.prototype.config = function config(logger, config, cli) { } } - callback(fields.file({ - promptLabel: 'Where is the Android SDK?', - default: androidSdkPath, - complete: true, - showHidden: true, - ignoreDirs: _t.ignoreDirs, - ignoreFiles: _t.ignoreFiles, - validate: _t.conf.options['android-sdk'].validate.bind(_t) - })); + callback( + fields.file({ + promptLabel: 'Where is the Android SDK?', + default: androidSdkPath, + complete: true, + showHidden: true, + ignoreDirs: _t.ignoreDirs, + ignoreFiles: _t.ignoreFiles, + validate: _t.conf.options['android-sdk'].validate.bind(_t), + }) + ); }, required: true, validate: function (value, callback) { if (!value) { callback(new Error('Invalid Android SDK path')); } else if (process.platform === 'win32' && value.indexOf('&') !== -1) { - callback(new Error('The Android SDK path cannot contain ampersands (&) on Windows')); + callback( + new Error('The Android SDK path cannot contain ampersands (&) on Windows') + ); } else if (_t.androidInfo.sdk && _t.androidInfo.sdk.path === path.resolve(value)) { callback(null, value); } else { @@ -249,7 +279,6 @@ AndroidBuilder.prototype.config = function config(logger, config, cli) { const pkginfo = {}; // appc.pkginfo.package(module) android.findSDK(value, config, pkginfo, () => { - // NOTE: ignore errors when finding SDK, let gradle validate the SDK function next() { @@ -257,16 +286,19 @@ AndroidBuilder.prototype.config = function config(logger, config, cli) { config.set('android.sdkPath', value); // path looks good, do a full scan again - androidDetect(config, { packageJson: _t.packageJson, bypassCache: true }, androidInfo => { + androidDetect( + config, + { packageJson: _t.packageJson, bypassCache: true }, + (androidInfo) => { + // assume SDK is valid, let gradle validate the SDK + if (!androidInfo.sdk) { + androidInfo.sdk = { path: value }; + } - // assume SDK is valid, let gradle validate the SDK - if (!androidInfo.sdk) { - androidInfo.sdk = { path: value }; + _t.androidInfo = androidInfo; + callback(null, value); } - - _t.androidInfo = androidInfo; - callback(null, value); - }); + ); } // new Android SDK path looks good @@ -278,35 +310,35 @@ AndroidBuilder.prototype.config = function config(logger, config, cli) { } }); } - } + }, }, 'avd-abi': { abbr: 'B', desc: 'the ABI for the Android emulator; deprecated, use --device-id', - hint: 'abi' + hint: 'abi', }, 'avd-id': { abbr: 'I', desc: 'the id for the Android emulator; deprecated, use --device-id', - hint: 'id' + hint: 'id', }, 'avd-skin': { abbr: 'S', desc: 'the skin for the Android emulator; deprecated, use --device-id', - hint: 'skin' + hint: 'skin', }, 'build-type': { - hidden: true + hidden: true, }, 'debug-host': { - hidden: true + hidden: true, }, 'deploy-type': { abbr: 'D', desc: `the type of deployment; only used when target is ${'emulator'.cyan} or ${'device'.cyan}`, hint: 'type', order: 110, - values: ['test', 'development'] + values: ['test', 'development'], }, 'device-id': { abbr: 'C', @@ -340,7 +372,10 @@ AndroidBuilder.prototype.config = function config(logger, config, cli) { if (emus.length) { opts['Genymotion Emulators'] = emus; - logger.log('NOTE: Genymotion emulator must be running to detect Google API support'.magenta + '\n'); + logger.log( + 'NOTE: Genymotion emulator must be running to detect Google API support' + .magenta + '\n' + ); } title = 'Which emulator do you want to launch your app in?'; @@ -350,38 +385,54 @@ AndroidBuilder.prototype.config = function config(logger, config, cli) { // if there are no devices/emulators, error if (!Object.keys(opts).length) { if (cli.argv.target === 'device') { - logger.warn('Unable to find any devices, possibly due to missing dependencies.\n'); - logger.log('Continuing with build... (will attempt to install missing dependencies)\n'); + logger.warn( + 'Unable to find any devices, possibly due to missing dependencies.\n' + ); + logger.log( + 'Continuing with build... (will attempt to install missing dependencies)\n' + ); } else { - logger.warn('Unable to find any emulators, possibly due to missing dependencies.\n'); - logger.log('Continuing with build... (will attempt to install missing dependencies)\n'); + logger.warn( + 'Unable to find any emulators, possibly due to missing dependencies.\n' + ); + logger.log( + 'Continuing with build... (will attempt to install missing dependencies)\n' + ); } _t.buildOnly = true; return callback(); } - callback(fields.select({ - title: title, - promptLabel: promptLabel, - formatters: { - option: function (opt, idx, num) { - return ' ' + num + opt.name.cyan + (opt.version ? ' (' + opt.version + ')' : '') + (opt.googleApis - ? ' (Google APIs supported)'.grey - : opt.googleApis === null - ? ' (Google APIs support unknown)'.grey - : ''); - } - }, - autoSelectOne: true, - margin: '', - optionLabel: 'name', - optionValue: 'id', - numbered: true, - relistOnError: true, - complete: true, - suggest: true, - options: opts - })); + callback( + fields.select({ + title: title, + promptLabel: promptLabel, + formatters: { + option: function (opt, idx, num) { + return ( + ' ' + + num + + opt.name.cyan + + (opt.version ? ' (' + opt.version + ')' : '') + + (opt.googleApis + ? ' (Google APIs supported)'.grey + : opt.googleApis === null + ? ' (Google APIs support unknown)'.grey + : '') + ); + }, + }, + autoSelectOne: true, + margin: '', + optionLabel: 'name', + optionValue: 'id', + numbered: true, + relistOnError: true, + complete: true, + suggest: true, + options: opts, + }) + ); }); }, required: true, @@ -397,7 +448,13 @@ AndroidBuilder.prototype.config = function config(logger, config, cli) { return callback(null, devices[i].id); } } - callback(new Error(cli.argv.target ? `Invalid Android device "${device}"` : `Invalid Android emulator "${device}"`)); + callback( + new Error( + cli.argv.target + ? `Invalid Android device "${device}"` + : `Invalid Android emulator "${device}"` + ) + ); }); }, verifyIfRequired(callback) { @@ -407,14 +464,20 @@ AndroidBuilder.prototype.config = function config(logger, config, cli) { } findTargetDevices(cli.argv.target, function (err, results) { - if (cli.argv.target === 'emulator' && cli.argv['device-id'] === undefined && cli.argv['avd-id']) { + if ( + cli.argv.target === 'emulator' && + cli.argv['device-id'] === undefined && + cli.argv['avd-id'] + ) { // if --device-id was not specified, but --avd-id was, then we need to // try to resolve a device based on the legacy --avd-* options - let avds = results.filter(function (a) { - return a.type === 'avd'; - }).map(function (a) { - return a.name; - }), + let avds = results + .filter(function (a) { + return a.type === 'avd'; + }) + .map(function (a) { + return a.name; + }), name = 'titanium_' + cli.argv['avd-id'] + '_'; if (avds.length) { @@ -430,7 +493,9 @@ AndroidBuilder.prototype.config = function config(logger, config, cli) { if (!cli.argv['avd-skin']) { // we have more than one match logger.error(`Found ${avds.length} AVD with id "${cli.argv['avd-id']}"`); - logger.error('Specify --avd-skin and --avd-abi to select a specific emulator\n'); + logger.error( + 'Specify --avd-skin and --avd-abi to select a specific emulator\n' + ); } else { name += cli.argv['avd-skin']; // try exact match @@ -446,13 +511,17 @@ AndroidBuilder.prototype.config = function config(logger, config, cli) { }); } if (avds.length === 0) { - logger.error(`No emulators found with id "${cli.argv['avd-id']}" and skin "${cli.argv['avd-skin']}"\n`); + logger.error( + `No emulators found with id "${cli.argv['avd-id']}" and skin "${cli.argv['avd-skin']}"\n` + ); } else if (avds.length === 1) { cli.argv['device-id'] = avds[0]; return callback(); } else if (!cli.argv['avd-abi']) { // we have more than one matching AVD, but no ABI to filter by so we have to error - logger.error(`Found ${avds.length} AVD with id "${cli.argv['avd-id']}" and skin "${cli.argv['avd-skin']}"`); + logger.error( + `Found ${avds.length} AVD with id "${cli.argv['avd-id']}" and skin "${cli.argv['avd-skin']}"` + ); logger.error('Specify --avd-abi to select a specific emulator\n'); } else { name += '_' + cli.argv['avd-abi']; @@ -468,7 +537,9 @@ AndroidBuilder.prototype.config = function config(logger, config, cli) { }); } if (avds.length === 0) { - logger.error(`No emulators found with id "${cli.argv['avd-id']}", skin "${cli.argv['avd-skin']}", and ABI "${cli.argv['avd-abi']}"\n`); + logger.error( + `No emulators found with id "${cli.argv['avd-id']}", skin "${cli.argv['avd-skin']}", and ABI "${cli.argv['avd-abi']}"\n` + ); } else { // there is one or more AVDs, but we'll just return the first one cli.argv['device-id'] = avds[0]; @@ -478,7 +549,9 @@ AndroidBuilder.prototype.config = function config(logger, config, cli) { } } - logger.warn(`${'--avd-*'.cyan} options have been ${'deprecated'.red}, please use ${'--device-id'.cyan}\n`); + logger.warn( + `${'--avd-*'.cyan} options have been ${'deprecated'.red}, please use ${'--device-id'.cyan}\n` + ); // print list of available AVDs if (results.length && !cli.argv.prompt) { @@ -489,8 +562,12 @@ AndroidBuilder.prototype.config = function config(logger, config, cli) { logger.log(); } } - - } else if (cli.argv['device-id'] === undefined && results && results.length && config.get('android.autoSelectDevice', true)) { + } else if ( + cli.argv['device-id'] === undefined && + results && + results.length && + config.get('android.autoSelectDevice', true) + ) { // we set the device-id to an array of devices so that later in validate() // after the tiapp.xml has been parsed, we can auto select the best device _t.devicesToAutoSelectFrom = results.sort((a, b) => { @@ -517,37 +594,48 @@ AndroidBuilder.prototype.config = function config(logger, config, cli) { } // Failed to find devices, fallback to buildOnly. - logger.warn('Unable to find any emulators or devices, possibly due to missing dependencies.'); - logger.warn('Continuing with build... (will attempt to install missing dependencies)'); + logger.warn( + 'Unable to find any emulators or devices, possibly due to missing dependencies.' + ); + logger.warn( + 'Continuing with build... (will attempt to install missing dependencies)' + ); _t.buildOnly = true; return callback(); }); - } + }, }, 'key-password': { desc: 'the password for the keystore private key (defaults to the store-password)', hint: 'keypass', order: 160, prompt: function (callback) { - callback(fields.text({ - promptLabel: 'What is the keystore\'s __key password__? ' + '(leave blank to use the store password)'.grey, - password: true, - validate: _t.conf.options['key-password'].validate.bind(_t) - })); + callback( + fields.text({ + promptLabel: + "What is the keystore's __key password__? " + + '(leave blank to use the store password)'.grey, + password: true, + validate: _t.conf.options['key-password'].validate.bind(_t), + }) + ); }, secret: true, validate: function (keyPassword, callback) { // sanity check the keystore and store password - _t.conf.options['store-password'].validate(cli.argv['store-password'], function (err, _storePassword) { - if (err) { - // we have a bad --keystore or --store-password arg - cli.argv.keystore = cli.argv['store-password'] = undefined; - return callback(err); - } + _t.conf.options['store-password'].validate( + cli.argv['store-password'], + function (err, _storePassword) { + if (err) { + // we have a bad --keystore or --store-password arg + cli.argv.keystore = cli.argv['store-password'] = undefined; + return callback(err); + } - callback(null, keyPassword); - }); - } + callback(null, keyPassword); + } + ); + }, }, keystore: { abbr: 'K', @@ -560,14 +648,16 @@ AndroidBuilder.prototype.config = function config(logger, config, cli) { order: 140, prompt(callback) { _t.conf.options['key-password'].required = true; - callback(fields.file({ - promptLabel: 'Where is the __keystore file__ used to sign the app?', - complete: true, - showHidden: true, - ignoreDirs: _t.ignoreDirs, - ignoreFiles: _t.ignoreFiles, - validate: _t.conf.options.keystore.validate.bind(_t) - })); + callback( + fields.file({ + promptLabel: 'Where is the __keystore file__ used to sign the app?', + complete: true, + showHidden: true, + ignoreDirs: _t.ignoreDirs, + ignoreFiles: _t.ignoreFiles, + validate: _t.conf.options.keystore.validate.bind(_t), + }) + ); }, validate(keystoreFile, callback) { if (!keystoreFile) { @@ -580,7 +670,7 @@ AndroidBuilder.prototype.config = function config(logger, config, cli) { callback(null, keystoreFile); } } - } + }, }, 'output-dir': { abbr: 'O', @@ -588,22 +678,30 @@ AndroidBuilder.prototype.config = function config(logger, config, cli) { hint: 'dir', order: 180, prompt: function (callback) { - callback(fields.file({ - promptLabel: 'Where would you like the output APK file saved?', - default: cli.argv['project-dir'] && path.resolvePath(cli.argv['project-dir'], 'dist'), - complete: true, - showHidden: true, - ignoreDirs: _t.ignoreDirs, - ignoreFiles: /.*/, - validate: _t.conf.options['output-dir'].validate.bind(_t) - })); + callback( + fields.file({ + promptLabel: 'Where would you like the output APK file saved?', + default: + cli.argv['project-dir'] && path.resolvePath(cli.argv['project-dir'], 'dist'), + complete: true, + showHidden: true, + ignoreDirs: _t.ignoreDirs, + ignoreFiles: /.*/, + validate: _t.conf.options['output-dir'].validate.bind(_t), + }) + ); }, validate: function (outputDir, callback) { - callback(outputDir || !_t.conf.options['output-dir'].required ? null : new Error('Invalid output directory'), outputDir); - } + callback( + outputDir || !_t.conf.options['output-dir'].required + ? null + : new Error('Invalid output directory'), + outputDir + ); + }, }, 'profiler-host': { - hidden: true + hidden: true, }, 'store-password': { abbr: 'P', @@ -611,17 +709,19 @@ AndroidBuilder.prototype.config = function config(logger, config, cli) { hint: 'password', order: 150, prompt(callback) { - callback(fields.text({ - next: function (err) { - return err && err.next || null; - }, - promptLabel: 'What is the keystore\'s __password__?', - password: true, - // if the password fails due to bad keystore file, - // we need to prompt for the keystore file again - repromptOnError: false, - validate: _t.conf.options['store-password'].validate.bind(_t) - })); + callback( + fields.text({ + next: function (err) { + return (err && err.next) || null; + }, + promptLabel: "What is the keystore's __password__?", + password: true, + // if the password fails due to bad keystore file, + // we need to prompt for the keystore file again + repromptOnError: false, + validate: _t.conf.options['store-password'].validate.bind(_t), + }) + ); }, secret: true, validate(storePassword, callback) { @@ -639,7 +739,7 @@ AndroidBuilder.prototype.config = function config(logger, config, cli) { callback(null, storePassword); }); - } + }, }, target: { abbr: 'T', @@ -658,18 +758,18 @@ AndroidBuilder.prototype.config = function config(logger, config, cli) { desc: 'the target to build for', order: 120, required: true, - values: _t.targets + values: _t.targets, }, sigalg: { desc: 'the type of a digital signature algorithm. only used when overriding keystore signing algorithm', hint: 'signing', order: 170, - values: ['MD5withRSA', 'SHA1withRSA', 'SHA256withRSA'] - } - } + values: ['MD5withRSA', 'SHA1withRSA', 'SHA256withRSA'], + }, + }, }; - callback(null, _t.conf = conf); + callback(null, (_t.conf = conf)); })((_err, result) => finished(result)); }; }; @@ -688,16 +788,16 @@ AndroidBuilder.prototype.run = async function run(_logger, _config, cli, finishe try { Builder.prototype.run.apply(this, arguments); - await new Promise(resolve => cli.emit('build.pre.construct', this, resolve)); + await new Promise((resolve) => cli.emit('build.pre.construct', this, resolve)); await new Promise((resolve, reject) => { - cli.emit('build.pre.compile', this, e => (e ? reject(e) : resolve())); + cli.emit('build.pre.compile', this, (e) => (e ? reject(e) : resolve())); }); - await new Promise(resolve => cli.emit('build.pre.build', this, resolve)); - await new Promise(resolve => cli.emit('build.post.build', this, resolve)); - await new Promise(resolve => cli.emit('build.post.compile', this, resolve)); - await new Promise(resolve => cli.emit('build.finalize', this, resolve)); + await new Promise((resolve) => cli.emit('build.pre.build', this, resolve)); + await new Promise((resolve) => cli.emit('build.post.build', this, resolve)); + await new Promise((resolve) => cli.emit('build.post.compile', this, resolve)); + await new Promise((resolve) => cli.emit('build.finalize', this, resolve)); } catch { process.exit(1); } @@ -709,7 +809,7 @@ AndroidBuilder.prototype.run = async function run(_logger, _config, cli, finishe // create the builder instance and expose the public API (function (androidBuilder) { - exports.config = androidBuilder.config.bind(androidBuilder); + exports.config = androidBuilder.config.bind(androidBuilder); exports.validate = androidBuilder.validate.bind(androidBuilder); - exports.run = androidBuilder.run.bind(androidBuilder); -}(new AndroidBuilder(module))); + exports.run = androidBuilder.run.bind(androidBuilder); +})(new AndroidBuilder(module)); diff --git a/test/mock-sdk/android/cli/commands/_buildModule.js b/test/mock-sdk/android/cli/commands/_buildModule.js index 16162e7f0..9332309fc 100644 --- a/test/mock-sdk/android/cli/commands/_buildModule.js +++ b/test/mock-sdk/android/cli/commands/_buildModule.js @@ -12,8 +12,12 @@ function AndroidModuleBuilder() { this.requiredArchitectures = this.packageJson.architectures; this.compileSdkVersion = this.packageJson.compileSDKVersion; // this should always be >= maxSupportedApiLevel this.minSupportedApiLevel = parseInt(this.packageJson.minSDKVersion); - this.minTargetApiLevel = parseInt(version.parseMin(this.packageJson.vendorDependencies['android sdk'])); - this.maxSupportedApiLevel = parseInt(version.parseMax(this.packageJson.vendorDependencies['android sdk'])); + this.minTargetApiLevel = parseInt( + version.parseMin(this.packageJson.vendorDependencies['android sdk']) + ); + this.maxSupportedApiLevel = parseInt( + version.parseMax(this.packageJson.vendorDependencies['android sdk']) + ); } util.inherits(AndroidModuleBuilder, Builder); @@ -22,7 +26,7 @@ AndroidModuleBuilder.prototype.validate = function validate(logger, config, cli) Builder.prototype.config.apply(this, arguments); Builder.prototype.validate.apply(this, arguments); - return finished => { + return (finished) => { this.projectDir = cli.argv['project-dir']; this.buildOnly = cli.argv['build-only']; this.target = cli.argv['target']; @@ -35,15 +39,14 @@ AndroidModuleBuilder.prototype.validate = function validate(logger, config, cli) this.manifest = this.cli.manifest; // detect Android environment - androidDetect(config, { packageJson: this.packageJson }, androidInfo => { + androidDetect(config, { packageJson: this.packageJson }, (androidInfo) => { this.androidInfo = androidInfo; const targetSDKMap = { - // placeholder for gradle to use [this.compileSdkVersion]: { - sdk: this.compileSdkVersion - } + sdk: this.compileSdkVersion, + }, }; Object.keys(this.androidInfo.targets).forEach(function (id) { var t = this.androidInfo.targets[id]; @@ -63,7 +66,7 @@ AndroidModuleBuilder.prototype.validate = function validate(logger, config, cli) if (!this.androidTargetSDK) { this.androidTargetSDK = { - sdk: this.targetSDK + sdk: this.targetSDK, }; } @@ -73,25 +76,37 @@ AndroidModuleBuilder.prototype.validate = function validate(logger, config, cli) } if (this.maxSDK && this.maxSDK < this.targetSDK) { - logger.error(`Maximum Android SDK version must be greater than or equal to the target SDK ${this.targetSDK}, but is currently set to ${this.maxSDK}\n`); + logger.error( + `Maximum Android SDK version must be greater than or equal to the target SDK ${this.targetSDK}, but is currently set to ${this.maxSDK}\n` + ); process.exit(1); } if (this.maxSupportedApiLevel && this.targetSDK > this.maxSupportedApiLevel) { // print warning that version this.targetSDK is not tested - logger.warn(`Building with Android SDK ${('' + this.targetSDK).cyan} which hasn't been tested against Titanium SDK ${this.titaniumSdkVersion}`); + logger.warn( + `Building with Android SDK ${('' + this.targetSDK).cyan} which hasn't been tested against Titanium SDK ${this.titaniumSdkVersion}` + ); } // get javac params this.javacMaxMemory = config.get('android.javac.maxMemory', '3072M'); // TODO remove in the next SDK - if (cli.timodule.properties['android.javac.maxmemory'] && cli.timodule.properties['android.javac.maxmemory'].value) { - logger.error('android.javac.maxmemory is deprecated and will be removed in the next version. Please use android.javac.maxMemory\n'); + if ( + cli.timodule.properties['android.javac.maxmemory'] && + cli.timodule.properties['android.javac.maxmemory'].value + ) { + logger.error( + 'android.javac.maxmemory is deprecated and will be removed in the next version. Please use android.javac.maxMemory\n' + ); this.javacMaxMemory = cli.timodule.properties['android.javac.maxmemory'].value; } - if (cli.timodule.properties['android.javac.maxMemory'] && cli.timodule.properties['android.javac.maxMemory'].value) { + if ( + cli.timodule.properties['android.javac.maxMemory'] && + cli.timodule.properties['android.javac.maxMemory'].value + ) { this.javacMaxMemory = cli.timodule.properties['android.javac.maxMemory'].value; } @@ -111,7 +126,7 @@ AndroidModuleBuilder.prototype.run = function run(_logger, _config, _cli, finish // create the builder instance and expose the public API (function (androidModuleBuilder) { - exports.config = androidModuleBuilder.config.bind(androidModuleBuilder); + exports.config = androidModuleBuilder.config.bind(androidModuleBuilder); exports.validate = androidModuleBuilder.validate.bind(androidModuleBuilder); - exports.run = androidModuleBuilder.run.bind(androidModuleBuilder); -}(new AndroidModuleBuilder(module))); + exports.run = androidModuleBuilder.run.bind(androidModuleBuilder); +})(new AndroidModuleBuilder(module)); diff --git a/test/mock-sdk/android/cli/hooks/package.js b/test/mock-sdk/android/cli/hooks/package.js index 35f754ae0..aba3a8822 100644 --- a/test/mock-sdk/android/cli/hooks/package.js +++ b/test/mock-sdk/android/cli/hooks/package.js @@ -7,6 +7,6 @@ exports.init = function (_logger, _config, cli) { priority: 10000, post(_builder, finished) { finished(); - } + }, }); }; diff --git a/test/mock-sdk/android/cli/hooks/run.js b/test/mock-sdk/android/cli/hooks/run.js index cbf0f618b..0539e2f76 100644 --- a/test/mock-sdk/android/cli/hooks/run.js +++ b/test/mock-sdk/android/cli/hooks/run.js @@ -16,16 +16,19 @@ exports.init = function (logger, config, cli) { if (builder.target === 'emulator') { cli.createHook('build.android.startEmulator', function (_deviceId, _opts, cb) { setTimeout(() => cb(), 100); - })(builder.deviceId, { - logger: logger - }, finished); - + })( + builder.deviceId, + { + logger: logger, + }, + finished + ); } else if (builder.target === 'device') { setTimeout(() => finished(), 100); } else { finished(); } - } + }, }); cli.on('build.post.compile', { @@ -40,7 +43,6 @@ exports.init = function (logger, config, cli) { } cli.emit('build.post.install', builder, finished); - } + }, }); - }; diff --git a/test/mock-sdk/android/cli/lib/detect.js b/test/mock-sdk/android/cli/lib/detect.js index b4ef3d25c..44d62e0d9 100644 --- a/test/mock-sdk/android/cli/lib/detect.js +++ b/test/mock-sdk/android/cli/lib/detect.js @@ -61,11 +61,20 @@ exports.detectDevices = function detectDevices(config, finished) { return finished(err); } - finished(null, devices.filter(function (d) { - return !d.emulator; - }).map(function (d) { - d.name = d.model || d.manufacturer || d.name || (d.release ? __('Android %s Device', d.release) : __('Android Device')); - return d; - })); + finished( + null, + devices + .filter(function (d) { + return !d.emulator; + }) + .map(function (d) { + d.name = + d.model || + d.manufacturer || + d.name || + (d.release ? __('Android %s Device', d.release) : __('Android Device')); + return d; + }) + ); }); }; diff --git a/test/mock-sdk/android/cli/lib/info.js b/test/mock-sdk/android/cli/lib/info.js index 90709dbed..17840170d 100644 --- a/test/mock-sdk/android/cli/lib/info.js +++ b/test/mock-sdk/android/cli/lib/info.js @@ -8,19 +8,21 @@ exports.name = 'android'; exports.title = 'Android'; exports.detect = function (_types, config, next) { - const tisdk = path.basename((function scan(dir) { - const file = path.join(dir, 'manifest.json'); - if (fs.existsSync(file)) { - return dir; - } - dir = path.dirname(dir); - return dir !== '/' && scan(dir); - }(__dirname))); + const tisdk = path.basename( + (function scan(dir) { + const file = path.join(dir, 'manifest.json'); + if (fs.existsSync(file)) { + return dir; + } + dir = path.dirname(dir); + return dir !== '/' && scan(dir); + })(__dirname) + ); const mod = require('./detect'); // detect Android environment - mod.detect(config, null, result => { + mod.detect(config, null, (result) => { // detect devices mod.detectDevices(config, (_err, devices) => { // detect emulators @@ -47,15 +49,39 @@ exports.render = function (logger, _config, rpad, styleHeading, styleValue, styl return; } - logger.log(styleHeading('Android SDK') + '\n' - + ' ' + rpad('Android Executable') + ' = ' + styleValue(data.sdk && data.sdk.executables.android || 'not found') + '\n' - + ' ' + rpad('ADB Executable') + ' = ' + styleValue(data.sdk && data.sdk.executables.adb || 'not found') + '\n' - + ' ' + rpad('SDK Path') + ' = ' + styleValue(data.sdk && data.sdk.path || 'not found') + '\n' + logger.log( + styleHeading('Android SDK') + + '\n' + + ' ' + + rpad('Android Executable') + + ' = ' + + styleValue((data.sdk && data.sdk.executables.android) || 'not found') + + '\n' + + ' ' + + rpad('ADB Executable') + + ' = ' + + styleValue((data.sdk && data.sdk.executables.adb) || 'not found') + + '\n' + + ' ' + + rpad('SDK Path') + + ' = ' + + styleValue((data.sdk && data.sdk.path) || 'not found') + + '\n' ); - logger.log(styleHeading('Android NDK') + '\n' - + ' ' + rpad('NDK Path') + ' = ' + styleValue(data.ndk && data.ndk.path || 'not found') + '\n' - + ' ' + rpad('NDK Version') + ' = ' + styleValue(data.ndk && data.ndk.version || 'not found') + '\n' + logger.log( + styleHeading('Android NDK') + + '\n' + + ' ' + + rpad('NDK Path') + + ' = ' + + styleValue((data.ndk && data.ndk.path) || 'not found') + + '\n' + + ' ' + + rpad('NDK Version') + + ' = ' + + styleValue((data.ndk && data.ndk.version) || 'not found') + + '\n' ); let androidPlatforms = ''; @@ -65,50 +91,135 @@ exports.render = function (logger, _config, rpad, styleHeading, styleValue, styl if (data.targets && Object.keys(data.targets).length) { Object.keys(data.targets).forEach(function (targetId) { var target = data.targets[targetId], - supported = (target.supported === 'maybe' - ? ` (not supported by Titanium SDK ${data.tisdk}, but may work)`.yellow - : target.supported - ? '' - : styleBad(` **Not supported by Titanium SDK ${data.tisdk}**`)); + supported = + target.supported === 'maybe' + ? ` (not supported by Titanium SDK ${data.tisdk}, but may work)`.yellow + : target.supported + ? '' + : styleBad(` **Not supported by Titanium SDK ${data.tisdk}**`); if (target.type === 'platform') { const m = target.name.match(/Android\s+(\d(?:\.\d(?:\.\d)?)?)/); if (m) { apiLevelMap[m[1]] = target['api-level']; } - androidPlatforms += ' ' + `${targetId}) ${target.id}`.cyan + '\n' - + ' ' + rpad(' Name') + ' = ' + styleValue(target.name) + supported + '\n' - + ' ' + rpad(' API Level') + ' = ' + styleValue(target['api-level']) + '\n' - + ' ' + rpad(' Revision') + ' = ' + styleValue(target.revision) + '\n' - + ' ' + rpad(' Skins') + ' = ' + styleValue(target.skins.join(', ')) + '\n' - + ' ' + rpad(' ABIs') + ' = ' + styleValue(target.abis.join(', ')) + '\n' - + ' ' + rpad(' Path') + ' = ' + styleValue(target.path) + '\n'; + androidPlatforms += + ' ' + + `${targetId}) ${target.id}`.cyan + + '\n' + + ' ' + + rpad(' Name') + + ' = ' + + styleValue(target.name) + + supported + + '\n' + + ' ' + + rpad(' API Level') + + ' = ' + + styleValue(target['api-level']) + + '\n' + + ' ' + + rpad(' Revision') + + ' = ' + + styleValue(target.revision) + + '\n' + + ' ' + + rpad(' Skins') + + ' = ' + + styleValue(target.skins.join(', ')) + + '\n' + + ' ' + + rpad(' ABIs') + + ' = ' + + styleValue(target.abis.join(', ')) + + '\n' + + ' ' + + rpad(' Path') + + ' = ' + + styleValue(target.path) + + '\n'; } else if (target.type === 'add-on') { - androidAddons += ' ' + (targetId + ') ' + target.id).cyan + '\n' - + ' ' + rpad(' Name') + ' = ' + styleValue(target.name - + ' (' + (target['based-on'] ? `Android ${target['based-on']['android-version']} (API level ${target['based-on']['api-level']})` : 'unknown') + ')') + supported + '\n' - + ' ' + rpad(' Vendor') + ' = ' + styleValue(target.vendor || 'n/a') + '\n' - + ' ' + rpad(' Revision') + ' = ' + styleValue(target.revision) + '\n' - + ' ' + rpad(' Description') + ' = ' + styleValue(target.description || 'n/a') + '\n' - + ' ' + rpad(' Skins') + ' = ' + styleValue(target.skins && target.skins.length ? target.skins.join(', ') : 'none') + '\n' - + ' ' + rpad(' ABIs') + ' = ' + styleValue(target.abis && target.abis.length ? target.abis.join(', ') : 'none') + '\n' - + ' ' + rpad(' Path') + ' = ' + styleValue(target.path) + '\n'; + androidAddons += + ' ' + + (targetId + ') ' + target.id).cyan + + '\n' + + ' ' + + rpad(' Name') + + ' = ' + + styleValue( + target.name + + ' (' + + (target['based-on'] + ? `Android ${target['based-on']['android-version']} (API level ${target['based-on']['api-level']})` + : 'unknown') + + ')' + ) + + supported + + '\n' + + ' ' + + rpad(' Vendor') + + ' = ' + + styleValue(target.vendor || 'n/a') + + '\n' + + ' ' + + rpad(' Revision') + + ' = ' + + styleValue(target.revision) + + '\n' + + ' ' + + rpad(' Description') + + ' = ' + + styleValue(target.description || 'n/a') + + '\n' + + ' ' + + rpad(' Skins') + + ' = ' + + styleValue(target.skins && target.skins.length ? target.skins.join(', ') : 'none') + + '\n' + + ' ' + + rpad(' ABIs') + + ' = ' + + styleValue(target.abis && target.abis.length ? target.abis.join(', ') : 'none') + + '\n' + + ' ' + + rpad(' Path') + + ' = ' + + styleValue(target.path) + + '\n'; if (target.libraries && Object.keys(target.libraries).length) { Object.keys(target.libraries).forEach(function (lib, i) { - androidAddons += ' ' + (i === 0 ? rpad(' Libraries') + ' = ' : rpad('') + ' ') - + styleValue(lib + ': ' + target.libraries[lib].description + ' (' + target.libraries[lib].jar + ')') + '\n'; + androidAddons += + ' ' + + (i === 0 ? rpad(' Libraries') + ' = ' : rpad('') + ' ') + + styleValue( + lib + + ': ' + + target.libraries[lib].description + + ' (' + + target.libraries[lib].jar + + ')' + ) + + '\n'; }); androidAddons += '\n'; } else { - androidAddons += ' ' + rpad(' ' + 'Libraries') + ' = ' + styleValue('none') + '\n'; + androidAddons += ' ' + rpad(' ' + 'Libraries') + ' = ' + styleValue('none') + '\n'; } } }); } - logger.log(styleHeading('Android Platforms') + '\n' + (androidPlatforms ? androidPlatforms : ' ' + 'none'.grey + '\n')); - logger.log(styleHeading('Android Add-Ons') + '\n' + (androidAddons ? androidAddons : ' ' + 'none'.grey + '\n')); + logger.log( + styleHeading('Android Platforms') + + '\n' + + (androidPlatforms ? androidPlatforms : ' ' + 'none'.grey + '\n') + ); + logger.log( + styleHeading('Android Add-Ons') + + '\n' + + (androidAddons ? androidAddons : ' ' + 'none'.grey + '\n') + ); logger.log(styleHeading('Android Emulators')); if (data.emulators) { @@ -116,20 +227,60 @@ exports.render = function (logger, _config, rpad, styleHeading, styleValue, styl return e.type === 'avd'; }); if (emus.length) { - logger.log(emus.map(function (emu) { - return ' ' + emu.name.cyan + '\n' - + ' ' + rpad(' ID') + ' = ' + styleValue(emu.id) + '\n' - + ' ' + rpad(' SDK Version') + ' = ' + styleValue(emu.target || 'not installed') + '\n' - + ' ' + rpad(' ABI') + ' = ' + styleValue(emu.abi) + '\n' - + ' ' + rpad(' Skin') + ' = ' + styleValue(emu.skin) + '\n' - + ' ' + rpad(' Path') + ' = ' + styleValue(emu.path) + '\n' - + ' ' + rpad(' SD Card') + ' = ' + styleValue(emu.sdcard || 'no sd card') + '\n' - + (emu['based-on'] - ? ' ' + rpad(' ' + 'Based On') + ' = ' + styleValue(`Android ${emu['based-on']['android-version']} (API level ${emu['based-on']['api-level']})`) + '\n' - : '' - ) - + ' ' + rpad(' ' + 'Google APIs') + ' = ' + styleValue(emu.googleApis ? 'yes' : 'no'); - }).join('\n') + '\n'); + logger.log( + emus + .map(function (emu) { + return ( + ' ' + + emu.name.cyan + + '\n' + + ' ' + + rpad(' ID') + + ' = ' + + styleValue(emu.id) + + '\n' + + ' ' + + rpad(' SDK Version') + + ' = ' + + styleValue(emu.target || 'not installed') + + '\n' + + ' ' + + rpad(' ABI') + + ' = ' + + styleValue(emu.abi) + + '\n' + + ' ' + + rpad(' Skin') + + ' = ' + + styleValue(emu.skin) + + '\n' + + ' ' + + rpad(' Path') + + ' = ' + + styleValue(emu.path) + + '\n' + + ' ' + + rpad(' SD Card') + + ' = ' + + styleValue(emu.sdcard || 'no sd card') + + '\n' + + (emu['based-on'] + ? ' ' + + rpad(' ' + 'Based On') + + ' = ' + + styleValue( + `Android ${emu['based-on']['android-version']} (API level ${emu['based-on']['api-level']})` + ) + + '\n' + : '') + + ' ' + + rpad(' ' + 'Google APIs') + + ' = ' + + styleValue(emu.googleApis ? 'yes' : 'no') + ); + }) + .join('\n') + '\n' + ); } else { logger.log(' ' + 'none'.grey + '\n'); } @@ -143,17 +294,65 @@ exports.render = function (logger, _config, rpad, styleHeading, styleValue, styl return e.type === 'genymotion'; }); if (emus.length) { - logger.log(emus.map(function (emu) { - return ' ' + emu.name.cyan + '\n' - + ' ' + rpad(' ID') + ' = ' + styleValue(emu.id) + '\n' - + ' ' + rpad(' SDK Version') + ' = ' + styleValue(emu.target + (apiLevelMap[emu.target] ? ' (android-' + apiLevelMap[emu.target] + ')' : '')) + '\n' - + ' ' + rpad(' ABI') + ' = ' + styleValue(emu.abi || 'unknown') + '\n' - + ' ' + rpad(' Genymotion Version') + ' = ' + styleValue(emu.genymotion || 'unknown') + '\n' - + ' ' + rpad(' Display') + ' = ' + styleValue(emu.display || 'unknown') + '\n' - + ' ' + rpad(' DPI') + ' = ' + styleValue(emu.dpi || 'unknown') + '\n' - + ' ' + rpad(' OpenGL Acceleration') + ' = ' + styleValue(emu.hardwareOpenGL ? 'yes' : 'no') + '\n' - + ' ' + rpad(' Google APIs') + ' = ' + styleValue(emu.googleApis === null ? 'unknown, emulator not running' : emu.googleApis ? 'yes' : 'no'); - }).join('\n') + '\n'); + logger.log( + emus + .map(function (emu) { + return ( + ' ' + + emu.name.cyan + + '\n' + + ' ' + + rpad(' ID') + + ' = ' + + styleValue(emu.id) + + '\n' + + ' ' + + rpad(' SDK Version') + + ' = ' + + styleValue( + emu.target + + (apiLevelMap[emu.target] ? ' (android-' + apiLevelMap[emu.target] + ')' : '') + ) + + '\n' + + ' ' + + rpad(' ABI') + + ' = ' + + styleValue(emu.abi || 'unknown') + + '\n' + + ' ' + + rpad(' Genymotion Version') + + ' = ' + + styleValue(emu.genymotion || 'unknown') + + '\n' + + ' ' + + rpad(' Display') + + ' = ' + + styleValue(emu.display || 'unknown') + + '\n' + + ' ' + + rpad(' DPI') + + ' = ' + + styleValue(emu.dpi || 'unknown') + + '\n' + + ' ' + + rpad(' OpenGL Acceleration') + + ' = ' + + styleValue(emu.hardwareOpenGL ? 'yes' : 'no') + + '\n' + + ' ' + + rpad(' Google APIs') + + ' = ' + + styleValue( + emu.googleApis === null + ? 'unknown, emulator not running' + : emu.googleApis + ? 'yes' + : 'no' + ) + ); + }) + .join('\n') + '\n' + ); } else { logger.log(' ' + 'none'.grey + '\n'); } @@ -163,45 +362,82 @@ exports.render = function (logger, _config, rpad, styleHeading, styleValue, styl logger.log(styleHeading('Connected Android Devices')); if (data.devices && data.devices.length) { - logger.log(data.devices.map(function (device) { - var name = device.name, - result = [ - ' ' + rpad('ID') + ' = ' + styleValue(device.id), - ' ' + rpad('State') + ' = ' + styleValue(device.state) - ]; - - if (device.release) { - result.push(' ' + rpad('SDK Version') + ' = ' + styleValue(device.release + ' (android-' + device.sdk + ')')); - } + logger.log( + data.devices + .map(function (device) { + var name = device.name, + result = [ + ' ' + rpad('ID') + ' = ' + styleValue(device.id), + ' ' + rpad('State') + ' = ' + styleValue(device.state), + ]; - if (Array.isArray(device.abi)) { - result.push(' ' + rpad('ABIs') + ' = ' + styleValue(device.abi.join(', '))); - } + if (device.release) { + result.push( + ' ' + + rpad('SDK Version') + + ' = ' + + styleValue(device.release + ' (android-' + device.sdk + ')') + ); + } - if (device.emulator) { - switch (device.emulator.type) { - case 'avd': - name = 'Android Emulator: ' + device.emulator.name; - result.push(' ' + rpad('Skin') + ' = ' + styleValue(device.emulator.skin || 'unknown')); - result.push(' ' + rpad('SD Card') + ' = ' + styleValue(device.emulator.sdcard || 'unknown')); - result.push(' ' + rpad('Google APIs') + ' = ' + styleValue(device.emulator.googleApis ? 'yes' : 'no')); - break; - - case 'genymotion': - name = 'Genymotion Emulator: ' + device.emulator.name; - result.push(' ' + rpad('Genymotion Version') + ' = ' + styleValue(device.emulator.genymotion || 'unknown')); - result.push(' ' + rpad('Display') + ' = ' + styleValue(device.emulator.display || 'unknown')); - result.push(' ' + rpad('DPI') + ' = ' + styleValue(device.emulator.dpi || 'unknown')); - result.push(' ' + rpad('OpenGL Acceleration') + ' = ' + styleValue(device.emulator.hardwareOpenGL ? 'yes' : 'no')); - result.push(' ' + rpad('Google APIs') + ' = ' + styleValue(device.emulator.googleApis ? 'yes' : 'no')); - break; - } + if (Array.isArray(device.abi)) { + result.push(' ' + rpad('ABIs') + ' = ' + styleValue(device.abi.join(', '))); + } - return name.cyan + '\n' + result.join('\n'); - } else { - return name.cyan + '\n' + result.join('\n'); - } - }).join('\n') + '\n'); + if (device.emulator) { + switch (device.emulator.type) { + case 'avd': + name = 'Android Emulator: ' + device.emulator.name; + result.push( + ' ' + rpad('Skin') + ' = ' + styleValue(device.emulator.skin || 'unknown') + ); + result.push( + ' ' + rpad('SD Card') + ' = ' + styleValue(device.emulator.sdcard || 'unknown') + ); + result.push( + ' ' + + rpad('Google APIs') + + ' = ' + + styleValue(device.emulator.googleApis ? 'yes' : 'no') + ); + break; + + case 'genymotion': + name = 'Genymotion Emulator: ' + device.emulator.name; + result.push( + ' ' + + rpad('Genymotion Version') + + ' = ' + + styleValue(device.emulator.genymotion || 'unknown') + ); + result.push( + ' ' + rpad('Display') + ' = ' + styleValue(device.emulator.display || 'unknown') + ); + result.push( + ' ' + rpad('DPI') + ' = ' + styleValue(device.emulator.dpi || 'unknown') + ); + result.push( + ' ' + + rpad('OpenGL Acceleration') + + ' = ' + + styleValue(device.emulator.hardwareOpenGL ? 'yes' : 'no') + ); + result.push( + ' ' + + rpad('Google APIs') + + ' = ' + + styleValue(device.emulator.googleApis ? 'yes' : 'no') + ); + break; + } + + return name.cyan + '\n' + result.join('\n'); + } else { + return name.cyan + '\n' + result.join('\n'); + } + }) + .join('\n') + '\n' + ); } else { logger.log(' ' + 'none'.grey + '\n'); } diff --git a/test/mock-sdk/android/package.json b/test/mock-sdk/android/package.json index a0a438169..7989e9c3d 100644 --- a/test/mock-sdk/android/package.json +++ b/test/mock-sdk/android/package.json @@ -1,25 +1,30 @@ { - "name": "titanium-mobile-android", - "title": "Android", - "description": "TiDev Titanium Mobile Android", - "version": "12.2.0", - "architectures": ["arm64-v8a", "armeabi-v7a", "x86", "x86_64"], - "v8": { - "version": "8.8.278.17", - "mode": "release", - "integrity": "sha512-A0tV+fYtkpKfIF5roRTCFPtdULMFygmfWlEuuHOBjC3q4rz/mKnAsJTYBlqayC/4oYEWehj867Oh1o6vy26XHQ==" - }, - "minSDKVersion": "21", - "compileSDKVersion": "33", - "vendorDependencies": { - "android sdk": ">=23.x <=33.x", - "android build tools": ">=30.0.2 <=33.x", - "android platform tools": "33.x", - "android tools": "<=26.x", - "android ndk": ">=r21 <=r22b", - "java": ">=11.x" - }, - "engines": { - "node": ">=12.13.0" - } + "name": "titanium-mobile-android", + "version": "12.2.0", + "description": "TiDev Titanium Mobile Android", + "engines": { + "node": ">=12.13.0" + }, + "architectures": [ + "arm64-v8a", + "armeabi-v7a", + "x86", + "x86_64" + ], + "compileSDKVersion": "33", + "minSDKVersion": "21", + "title": "Android", + "v8": { + "version": "8.8.278.17", + "mode": "release", + "integrity": "sha512-A0tV+fYtkpKfIF5roRTCFPtdULMFygmfWlEuuHOBjC3q4rz/mKnAsJTYBlqayC/4oYEWehj867Oh1o6vy26XHQ==" + }, + "vendorDependencies": { + "android sdk": ">=23.x <=33.x", + "android build tools": ">=30.0.2 <=33.x", + "android platform tools": "33.x", + "android tools": "<=26.x", + "android ndk": ">=r21 <=r22b", + "java": ">=11.x" + } } diff --git a/test/mock-sdk/cli/commands/build.js b/test/mock-sdk/cli/commands/build.js index 325be8808..bbc6ff71e 100644 --- a/test/mock-sdk/cli/commands/build.js +++ b/test/mock-sdk/cli/commands/build.js @@ -12,12 +12,15 @@ fields.setup({ return `[ERROR] ${err.message}`.red + '\n'; } err = '' + err; - return '\n' + (/^(\[ERROR\])/i.test(err) ? err : '[ERROR] ' + err.replace(/^Error:/i, '').trim()).red; - } + return ( + '\n' + + (/^(\[ERROR\])/i.test(err) ? err : '[ERROR] ' + err.replace(/^Error:/i, '').trim()).red + ); + }, }, style: { - accelerator: 'cyan' - } + accelerator: 'cyan', + }, }); exports.cliVersion = '>=3.2.1'; @@ -31,188 +34,202 @@ exports.config = function config(logger, config, cli) { // start patching the logger here patchLogger(logger, cli); - return finished => { - cli.createHook('build.config', callback => { + return (finished) => { + cli.createHook('build.config', (callback) => { // note: it's currently impossible for the module build to declare any // config options/flags. - ti.platformOptions(logger, config, cli, 'build', platformConf => { + ti.platformOptions(logger, config, cli, 'build', (platformConf) => { var conf = { flags: { 'build-only': { abbr: 'b', - desc: 'only perform the build; if true, does not install or run the app' + desc: 'only perform the build; if true, does not install or run the app', }, force: { abbr: 'f', - desc: 'force a full rebuild' + desc: 'force a full rebuild', }, legacy: { - desc: 'build using the old Python-based builder.py; deprecated' + desc: 'build using the old Python-based builder.py; deprecated', }, 'skip-js-minify': { default: false, - desc: `bypasses JavaScript minification; ${'simulator'.cyan} builds are never minified; only supported for ${'Android'.cyan} and ${'iOS'.cyan}` + desc: `bypasses JavaScript minification; ${'simulator'.cyan} builds are never minified; only supported for ${'Android'.cyan} and ${'iOS'.cyan}`, }, 'source-maps': { - desc: 'generate inline source maps for transpiled JS files' + desc: 'generate inline source maps for transpiled JS files', }, }, - options: Object.assign({ - platform: { - abbr: 'p', - callback(platform) { - if (!cli.argv.$originalPlatform) { - cli.argv.$originalPlatform = platform; - } - platform = cli.argv.platform = ti.resolvePlatform(platform); - - const p = platformConf[platform]; - p && p.options && Object.keys(p.options).forEach(name => { - if (p.options[name].default && cli.argv[name] === undefined) { - cli.argv[name] = p.options[name].default; + options: Object.assign( + { + platform: { + abbr: 'p', + callback(platform) { + if (!cli.argv.$originalPlatform) { + cli.argv.$originalPlatform = platform; } - }); - - return platform; + platform = cli.argv.platform = ti.resolvePlatform(platform); + + const p = platformConf[platform]; + p && + p.options && + Object.keys(p.options).forEach((name) => { + if (p.options[name].default && cli.argv[name] === undefined) { + cli.argv[name] = p.options[name].default; + } + }); + + return platform; + }, + desc: 'the target build platform', + hint: 'platform', + order: 2, + prompt: { + label: 'Target platform', + error: 'Invalid platform', + validator(platform) { + if (!platform) { + throw new Error('Invalid platform'); + } else if (ti.availablePlatforms.indexOf(platform) === -1) { + throw new Error(`Invalid platform: ${platform}`); + } + return true; + }, + }, + required: true, + skipValueCheck: true, + values: ti.targetPlatforms, }, - desc: 'the target build platform', - hint: 'platform', - order: 2, - prompt: { - label: 'Target platform', - error: 'Invalid platform', - validator(platform) { - if (!platform) { - throw new Error('Invalid platform'); - } else if (ti.availablePlatforms.indexOf(platform) === -1) { - throw new Error(`Invalid platform: ${platform}`); - } - return true; - } - }, - required: true, - skipValueCheck: true, - values: ti.targetPlatforms - }, - 'project-dir': { - abbr: 'd', - callback(projectDir) { - if (projectDir === '') { - // no option value was specified - // set project dir to current directory - projectDir = conf.options['project-dir'].default; - } - - projectDir = path.resolve(projectDir); - - // load the tiapp.xml/timodule.xml - if (fs.existsSync(path.join(projectDir, 'tiapp.xml'))) { - let tiapp; - try { - tiapp = cli.tiapp = {}; - } catch (ex) { - logger.error(ex); - logger.log(); - process.exit(1); + 'project-dir': { + abbr: 'd', + callback(projectDir) { + if (projectDir === '') { + // no option value was specified + // set project dir to current directory + projectDir = conf.options['project-dir'].default; } - tiapp.properties || (tiapp.properties = {}); + projectDir = path.resolve(projectDir); + + // load the tiapp.xml/timodule.xml + if (fs.existsSync(path.join(projectDir, 'tiapp.xml'))) { + let tiapp; + try { + tiapp = cli.tiapp = {}; + } catch (ex) { + logger.error(ex); + logger.log(); + process.exit(1); + } - cli.argv.type = 'app'; + tiapp.properties || (tiapp.properties = {}); + + cli.argv.type = 'app'; + } else if (fs.existsSync(path.join(projectDir, 'timodule.xml'))) { + let timodule; + try { + timodule = cli.tiapp = cli.timodule = {}; + } catch (ex) { + logger.error(ex); + logger.log(); + process.exit(1); + } - } else if (fs.existsSync(path.join(projectDir, 'timodule.xml'))) { - let timodule; - try { - timodule = cli.tiapp = cli.timodule = {}; - } catch (ex) { - logger.error(ex); - logger.log(); - process.exit(1); - } + const manifest = (cli.manifest = ti.loadModuleManifest( + logger, + path.join(projectDir, 'manifest') + )); - const manifest = cli.manifest = ti.loadModuleManifest(logger, path.join(projectDir, 'manifest')); + // if they didn't explicitly set --platform and we have a platform in the manifest, + // then just use that and skip the platform prompting + if (!cli.argv.platform && manifest.platform) { + cli.argv.platform = ti.resolvePlatform(manifest.platform); + conf.options.platform.required = false; + } + + timodule.properties || (timodule.properties = {}); - // if they didn't explicitly set --platform and we have a platform in the manifest, - // then just use that and skip the platform prompting - if (!cli.argv.platform && manifest.platform) { - cli.argv.platform = ti.resolvePlatform(manifest.platform); - conf.options.platform.required = false; + cli.argv.type = 'module'; + } else { + // neither app nor module + return; } - timodule.properties || (timodule.properties = {}); + cli.scanHooks(path.join(projectDir, 'hooks')); + + return projectDir; + }, + desc: 'the directory containing the project', + default: process.env.SOURCE_ROOT + ? path.join(process.env.SOURCE_ROOT, '..', '..') + : '.', + order: 1, + prompt(callback) { + callback( + fields.file({ + promptLabel: 'Where is the __project directory__?', + complete: true, + showHidden: true, + ignoreDirs: new RegExp(config.get('cli.ignoreDirs')), + ignoreFiles: /.*/, + validate: conf.options['project-dir'].validate, + }) + ); + }, + required: true, + validate(projectDir, callback) { + const isDefault = projectDir == conf.options['project-dir'].default; + let dir = path.resolve(projectDir); + + if (!fs.existsSync(dir)) { + return callback(new Error('Project directory does not exist')); + } - cli.argv.type = 'module'; + const root = path.resolve('/'); + let isFound, + projDir = dir; - } else { - // neither app nor module - return; - } + ['tiapp.xml', 'timodule.xml'].some((tiXml) => { + let tiFile = path.join(dir, tiXml); - cli.scanHooks(path.join(projectDir, 'hooks')); + while (!fs.existsSync(tiFile)) { + dir = path.dirname(dir); + if (dir == root) { + isFound = false; + break; + } + tiFile = path.join(dir, tiXml); + } - return projectDir; - }, - desc: 'the directory containing the project', - default: process.env.SOURCE_ROOT ? path.join(process.env.SOURCE_ROOT, '..', '..') : '.', - order: 1, - prompt(callback) { - callback(fields.file({ - promptLabel: 'Where is the __project directory__?', - complete: true, - showHidden: true, - ignoreDirs: new RegExp(config.get('cli.ignoreDirs')), - ignoreFiles: /.*/, - validate: conf.options['project-dir'].validate - })); - }, - required: true, - validate(projectDir, callback) { - const isDefault = (projectDir == conf.options['project-dir'].default); - let dir = path.resolve(projectDir); - - if (!fs.existsSync(dir)) { - return callback(new Error('Project directory does not exist')); - } - - const root = path.resolve('/'); - let isFound, - projDir = dir; - - ['tiapp.xml', 'timodule.xml'].some(tiXml => { - let tiFile = path.join(dir, tiXml); - - while (!fs.existsSync(tiFile)) { - dir = path.dirname(dir); - if (dir == root) { - isFound = false; - break; + // Found the XML file, break the loop + if (fs.existsSync(tiFile)) { + isFound = true; + return true; } - tiFile = path.join(dir, tiXml); - } - // Found the XML file, break the loop - if (fs.existsSync(tiFile)) { - isFound = true; - return true; + dir = projDir; + }); + + if (!isFound && dir == root && isDefault) { + callback(true); + return; } - dir = projDir; - }); - - if (!isFound && dir == root && isDefault) { - callback(true); - return; - } - - if (!isFound) { - callback(new Error(`Invalid project directory "${projectDir}" because tiapp.xml or timodule.xml not found`)); - return; - } - callback(null, dir); - } - } - }, ti.commonOptions(logger, config)), - platforms: platformConf + if (!isFound) { + callback( + new Error( + `Invalid project directory "${projectDir}" because tiapp.xml or timodule.xml not found` + ) + ); + return; + } + callback(null, dir); + }, + }, + }, + ti.commonOptions(logger, config) + ), + platforms: platformConf, }; callback(null, conf); }); @@ -221,14 +238,12 @@ exports.config = function config(logger, config, cli) { }; exports.validate = function validate(logger, config, cli) { - // Determine if the project is an app or a module, run appropriate build command if (cli.argv.type === 'module') { - // make sure the module manifest is sane ti.validateModuleManifest(logger, cli, cli.manifest); - return finished => { + return (finished) => { logger.log.init(() => { const result = ti.validatePlatformOptions(logger, config, cli, 'buildModule'); if (result && typeof result === 'function') { @@ -238,9 +253,7 @@ exports.validate = function validate(logger, config, cli) { } }); }; - } else { - ti.validatePlatform(logger, cli, 'platform'); // since we need validate() to be async, we return a function in which the cli @@ -277,7 +290,9 @@ exports.run = function run(logger, config, cli, finished) { if (!fs.existsSync(buildModule)) { logger.error('Unable to find platform specific build command\n'); - logger.log(`Your SDK installation may be corrupt. You can reinstall it by running '${(cli.argv.$ + ' sdk install --force --default').cyan}'.\n`); + logger.log( + `Your SDK installation may be corrupt. You can reinstall it by running '${(cli.argv.$ + ' sdk install --force --default').cyan}'.\n` + ); process.exit(1); } @@ -290,9 +305,12 @@ exports.run = function run(logger, config, cli, finished) { if (err instanceof Error) { err.dump(logger.error); } else if (err !== true) { - (err.message || err.toString()).trim().split('\n').forEach(function (msg) { - logger.error(msg); - }); + (err.message || err.toString()) + .trim() + .split('\n') + .forEach(function (msg) { + logger.error(msg); + }); } logger.log(); process.exit(1); @@ -367,32 +385,37 @@ function patchLogger(logger, cli) { } cli.env.getOSInfo(function (osInfo) { - logger.log([ - new Date().toLocaleString(), - '', - styleHeading('Operating System'), - ' ' + rpad('Name') + ' = ' + styleValue(osInfo.os), - ' ' + rpad('Version') + ' = ' + styleValue(osInfo.osver), - ' ' + rpad('Architecture') + ' = ' + styleValue(osInfo.ostype), - ' ' + rpad('# CPUs') + ' = ' + styleValue(osInfo.oscpu), - ' ' + rpad('Memory') + ' = ' + styleValue(osInfo.memory), - '', - styleHeading('Node.js'), - ' ' + rpad('Node.js Version') + ' = ' + styleValue(osInfo.node), - ' ' + rpad('npm Version') + ' = ' + styleValue(osInfo.npm), - '', - styleHeading('Titanium CLI'), - ' ' + rpad('CLI Version') + ' = ' + styleValue(cli.version), - '', - styleHeading('Titanium SDK'), - ' ' + rpad('SDK Version') + ' = ' + styleValue(cli.argv.sdk), - ' ' + rpad('SDK Path') + ' = ' + styleValue(cli.sdk.path), - ' ' + rpad('Target Platform') + ' = ' + styleValue(ti.resolvePlatform(cli.argv.platform)), - '', - styleHeading('Command'), - ' ' + styleValue(process.argv.join(' ')), - '' - ].join('\n')); + logger.log( + [ + new Date().toLocaleString(), + '', + styleHeading('Operating System'), + ' ' + rpad('Name') + ' = ' + styleValue(osInfo.os), + ' ' + rpad('Version') + ' = ' + styleValue(osInfo.osver), + ' ' + rpad('Architecture') + ' = ' + styleValue(osInfo.ostype), + ' ' + rpad('# CPUs') + ' = ' + styleValue(osInfo.oscpu), + ' ' + rpad('Memory') + ' = ' + styleValue(osInfo.memory), + '', + styleHeading('Node.js'), + ' ' + rpad('Node.js Version') + ' = ' + styleValue(osInfo.node), + ' ' + rpad('npm Version') + ' = ' + styleValue(osInfo.npm), + '', + styleHeading('Titanium CLI'), + ' ' + rpad('CLI Version') + ' = ' + styleValue(cli.version), + '', + styleHeading('Titanium SDK'), + ' ' + rpad('SDK Version') + ' = ' + styleValue(cli.argv.sdk), + ' ' + rpad('SDK Path') + ' = ' + styleValue(cli.sdk.path), + ' ' + + rpad('Target Platform') + + ' = ' + + styleValue(ti.resolvePlatform(cli.argv.platform)), + '', + styleHeading('Command'), + ' ' + styleValue(process.argv.join(' ')), + '', + ].join('\n') + ); callback(); }); }; diff --git a/test/mock-sdk/cli/commands/clean.js b/test/mock-sdk/cli/commands/clean.js index 6ea8f3c75..c8979ca36 100644 --- a/test/mock-sdk/cli/commands/clean.js +++ b/test/mock-sdk/cli/commands/clean.js @@ -13,119 +13,126 @@ exports.config = function (logger, config, cli) { patchLogger(logger, cli); return (finished) => { - cli.createHook('clean.config', callback => { + cli.createHook('clean.config', (callback) => { var conf = { - options: Object.assign({ - platform: { - // this is for backwards compatibility and eventually should be dropped - hidden: true - }, - platforms: { - // note: --platforms is not required for the clean command - abbr: 'p', - desc: 'one or more platforms to clean', - values: ['android'], - skipValueCheck: true // we do our own validation - }, - 'project-dir': { - abbr: 'd', - callback: function (projectDir) { - if (projectDir === '') { - // no option value was specified - // set project dir to current directory - projectDir = conf.options['project-dir'].default; - } - - projectDir = path.resolve(projectDir); - - // load the tiapp.xml/timodule.xml - if (fs.existsSync(path.join(projectDir, 'tiapp.xml'))) { - cli.tiapp = {}; - cli.tiapp.properties ||= {}; - cli.argv.type = 'app'; - - } else if (fs.existsSync(path.join(projectDir, 'timodule.xml'))) { - cli.tiapp = cli.timodule = {}; - cli.manifest = { - platform: 'android' - }; - - if (!cli.argv.platform) { - cli.argv.platform = cli.manifest.platform; - conf.options.platform.required = false; + options: Object.assign( + { + platform: { + // this is for backwards compatibility and eventually should be dropped + hidden: true, + }, + platforms: { + // note: --platforms is not required for the clean command + abbr: 'p', + desc: 'one or more platforms to clean', + values: ['android'], + skipValueCheck: true, // we do our own validation + }, + 'project-dir': { + abbr: 'd', + callback: function (projectDir) { + if (projectDir === '') { + // no option value was specified + // set project dir to current directory + projectDir = conf.options['project-dir'].default; } - cli.timodule.properties ||= {}; - cli.argv.type = 'module'; + projectDir = path.resolve(projectDir); + + // load the tiapp.xml/timodule.xml + if (fs.existsSync(path.join(projectDir, 'tiapp.xml'))) { + cli.tiapp = {}; + cli.tiapp.properties ||= {}; + cli.argv.type = 'app'; + } else if (fs.existsSync(path.join(projectDir, 'timodule.xml'))) { + cli.tiapp = cli.timodule = {}; + cli.manifest = { + platform: 'android', + }; + + if (!cli.argv.platform) { + cli.argv.platform = cli.manifest.platform; + conf.options.platform.required = false; + } - } else { - // neither app nor module - return; - } + cli.timodule.properties ||= {}; + cli.argv.type = 'module'; + } else { + // neither app nor module + return; + } - cli.scanHooks(path.join(projectDir, 'hooks')); + cli.scanHooks(path.join(projectDir, 'hooks')); + + return projectDir; + }, + desc: 'the directory containing the project, otherwise the current working directory', + default: '.', + order: 1, + prompt(callback) { + callback( + fields.file({ + promptLabel: 'Where is the __project directory__?', + complete: true, + showHidden: true, + ignoreDirs: new RegExp(config.get('cli.ignoreDirs')), + ignoreFiles: /.*/, + validate: conf.options['project-dir'].validate, + }) + ); + }, + validate: function (projectDir, callback) { + const isDefault = projectDir == conf.options['project-dir'].default; + let dir = path.resolve(projectDir); + + if (!fs.existsSync(dir)) { + return callback(new Error('Project directory does not exist')); + } - return projectDir; - }, - desc: 'the directory containing the project, otherwise the current working directory', - default: '.', - order: 1, - prompt(callback) { - callback(fields.file({ - promptLabel: 'Where is the __project directory__?', - complete: true, - showHidden: true, - ignoreDirs: new RegExp(config.get('cli.ignoreDirs')), - ignoreFiles: /.*/, - validate: conf.options['project-dir'].validate - })); - }, - validate: function (projectDir, callback) { - const isDefault = (projectDir == conf.options['project-dir'].default); - let dir = path.resolve(projectDir); - - if (!fs.existsSync(dir)) { - return callback(new Error('Project directory does not exist')); - } - - const root = path.resolve('/'); - let isFound; - let projDir = dir; - - ['tiapp.xml', 'timodule.xml'].some(tiXml => { - let tiFile = path.join(dir, tiXml); - - while (!fs.existsSync(tiFile)) { - dir = path.dirname(dir); - if (dir == root) { - isFound = false; - break; + const root = path.resolve('/'); + let isFound; + let projDir = dir; + + ['tiapp.xml', 'timodule.xml'].some((tiXml) => { + let tiFile = path.join(dir, tiXml); + + while (!fs.existsSync(tiFile)) { + dir = path.dirname(dir); + if (dir == root) { + isFound = false; + break; + } + tiFile = path.join(dir, tiXml); } - tiFile = path.join(dir, tiXml); - } - // Found the XML file, break the loop - if (fs.existsSync(tiFile)) { - isFound = true; - return true; - } + // Found the XML file, break the loop + if (fs.existsSync(tiFile)) { + isFound = true; + return true; + } - dir = projDir; - }); + dir = projDir; + }); + + if (!isFound && dir == root && isDefault) { + callback(true); + return; + } - if (!isFound && dir == root && isDefault) { - callback(true); - return; - } - - if (!isFound) { - callback(new Error(`Invalid project directory "${projectDir}" because tiapp.xml or timodule.xml not found`)); - return; - } - callback(null, dir); - } - } - }, ti.commonOptions(logger, config)) + if (!isFound) { + callback( + new Error( + `Invalid project directory "${projectDir}" because tiapp.xml or timodule.xml not found` + ) + ); + return; + } + callback(null, dir); + }, + }, + }, + ti.commonOptions(logger, config) + ), }; callback(null, conf); })((_err, result) => finished(result)); @@ -135,11 +142,10 @@ exports.config = function (logger, config, cli) { exports.validate = function (logger, config, cli) { // Determine if the project is an app or a module, run appropriate clean command if (cli.argv.type === 'module') { - // make sure the module manifest is sane ti.validateModuleManifest(logger, cli, cli.manifest); - return finished => { + return (finished) => { logger.log.init(() => { const result = ti.validatePlatformOptions(logger, config, cli, 'cleanModule'); if (result && typeof result === 'function') { @@ -149,14 +155,15 @@ exports.validate = function (logger, config, cli) { } }); }; - } else { let platforms = cli.argv.platforms || cli.argv.platform; if (platforms) { platforms = ti.scrubPlatforms(platforms); if (platforms.bad.length) { - logger.error(`Invalid platform${platforms.bad.length === 1 ? '' : 's'}: ${platforms.bad.join(', ')}\n`); + logger.error( + `Invalid platform${platforms.bad.length === 1 ? '' : 's'}: ${platforms.bad.join(', ')}\n` + ); logger.log(`Available platforms for SDK version ${ti.manifest.sdkVersion}:\n`); ti.targetPlatforms.forEach(function (p) { logger.log(' ' + p.cyan); @@ -172,7 +179,7 @@ exports.validate = function (logger, config, cli) { ti.validateProjectDir(logger, cli, cli.argv, 'project-dir'); - return finished =>{ + return (finished) => { ti.loadPlugins(logger, config, cli, cli.argv['project-dir'], () => finished()); }; } @@ -181,7 +188,15 @@ exports.validate = function (logger, config, cli) { exports.run = function (logger, config, cli) { if (cli.argv.type === 'module') { const platform = ti.resolvePlatform(cli.argv.platform); - const cleanModule = path.join(__dirname, '..', '..', platform, 'cli', 'commands', '_cleanModule.js'); + const cleanModule = path.join( + __dirname, + '..', + '..', + platform, + 'cli', + 'commands', + '_cleanModule.js' + ); if (!fs.existsSync(cleanModule)) { process.exit(1); } @@ -200,38 +215,46 @@ exports.run = function (logger, config, cli) { if (cli.argv.platforms) { cli.argv.platforms.reduce((prom, platform) => { - return prom.then(new Promise(resolve => { - // scan platform SDK specific clean hooks - cli.scanHooks(path.join(__dirname, '..', '..', platform, 'cli', 'hooks')); - cli.fireHook('clean.pre', function () { - cli.fireHook(`clean.${platform}.pre`, function () { - cli.fireHook(`clean.${platform}.post`, function () { - cli.fireHook('clean.post', () => resolve()); + return prom.then( + new Promise((resolve) => { + // scan platform SDK specific clean hooks + cli.scanHooks(path.join(__dirname, '..', '..', platform, 'cli', 'hooks')); + cli.fireHook('clean.pre', function () { + cli.fireHook(`clean.${platform}.pre`, function () { + cli.fireHook(`clean.${platform}.post`, function () { + cli.fireHook('clean.post', () => resolve()); + }); }); }); - }); - })); + }) + ); }, Promise.resolve()); } else if (fs.existsSync(buildDir)) { logger.debug('Deleting all platform build directories'); // scan platform SDK specific clean hooks if (ti.targetPlatforms) { - ti.targetPlatforms.forEach(platform => { + ti.targetPlatforms.forEach((platform) => { cli.scanHooks(path.join(__dirname, '..', '..', platform, 'cli', 'hooks')); }); } cli.fireHook('clean.pre', function () { - fs.readdirSync(buildDir).reduce((prom, dir) => { - return prom.then(new Promise(resolve => { - cli.fireHook(`clean.${dir}.pre`, () => { - cli.fireHook(`clean.${dir}.post`, () => resolve()); - }); - })); - }, Promise.resolve()).then(new Promise(resolve => { - cli.fireHook('clean.post', () => resolve()); - })); + fs.readdirSync(buildDir) + .reduce((prom, dir) => { + return prom.then( + new Promise((resolve) => { + cli.fireHook(`clean.${dir}.pre`, () => { + cli.fireHook(`clean.${dir}.post`, () => resolve()); + }); + }) + ); + }, Promise.resolve()) + .then( + new Promise((resolve) => { + cli.fireHook('clean.post', () => resolve()); + }) + ); }); } } @@ -295,43 +318,46 @@ function patchLogger(logger, cli) { } cli.env.getOSInfo(function (osInfo) { - logger.log([ - new Date().toLocaleString(), - '', - styleHeading('Operating System'), - ' ' + rpad('Name') + ' = ' + styleValue(osInfo.os), - ' ' + rpad('Version') + ' = ' + styleValue(osInfo.osver), - ' ' + rpad('Architecture') + ' = ' + styleValue(osInfo.ostype), - ' ' + rpad('# CPUs') + ' = ' + styleValue(osInfo.oscpu), - ' ' + rpad('Memory') + ' = ' + styleValue(osInfo.memory), - '', - styleHeading('Node.js'), - ' ' + rpad('Node.js Version') + ' = ' + styleValue(osInfo.node), - ' ' + rpad('npm Version') + ' = ' + styleValue(osInfo.npm), - '', - styleHeading('Titanium CLI'), - ' ' + rpad('CLI Version') + ' = ' + styleValue(cli.version), - '', - styleHeading('Titanium SDK'), - ' ' + rpad('SDK Version') + ' = ' + styleValue(cli.argv.sdk), - ' ' + rpad('SDK Path') + ' = ' + styleValue(cli.sdk.path), - ' ' + rpad('Target Platform') + ' = ' + styleValue(ti.resolvePlatform(cli.argv.platform)), - '', - styleHeading('Command'), - ' ' + styleValue(process.argv.join(' ')), - '' - ].join('\n')); + logger.log( + [ + new Date().toLocaleString(), + '', + styleHeading('Operating System'), + ' ' + rpad('Name') + ' = ' + styleValue(osInfo.os), + ' ' + rpad('Version') + ' = ' + styleValue(osInfo.osver), + ' ' + rpad('Architecture') + ' = ' + styleValue(osInfo.ostype), + ' ' + rpad('# CPUs') + ' = ' + styleValue(osInfo.oscpu), + ' ' + rpad('Memory') + ' = ' + styleValue(osInfo.memory), + '', + styleHeading('Node.js'), + ' ' + rpad('Node.js Version') + ' = ' + styleValue(osInfo.node), + ' ' + rpad('npm Version') + ' = ' + styleValue(osInfo.npm), + '', + styleHeading('Titanium CLI'), + ' ' + rpad('CLI Version') + ' = ' + styleValue(cli.version), + '', + styleHeading('Titanium SDK'), + ' ' + rpad('SDK Version') + ' = ' + styleValue(cli.argv.sdk), + ' ' + rpad('SDK Path') + ' = ' + styleValue(cli.sdk.path), + ' ' + + rpad('Target Platform') + + ' = ' + + styleValue(ti.resolvePlatform(cli.argv.platform)), + '', + styleHeading('Command'), + ' ' + styleValue(process.argv.join(' ')), + '', + ].join('\n') + ); logger.log.flush(); callback(); }); }; - logger.log.flush = function () { - }; + logger.log.flush = function () {}; - logger.log.end = function () { - }; + logger.log.end = function () {}; logger.log.buffer = ''; } diff --git a/test/mock-sdk/cli/commands/create.js b/test/mock-sdk/cli/commands/create.js index 8ba1e0842..20e44609d 100644 --- a/test/mock-sdk/cli/commands/create.js +++ b/test/mock-sdk/cli/commands/create.js @@ -8,9 +8,10 @@ const ti = require('../lib/node-titanium-sdk/ti'); exports.cliVersion = '>=3.2.1'; exports.title = 'Create'; exports.desc = 'creates a new project'; -exports.extendedDesc = 'Creates a new Titanium application, native module, or Apple Watch™ app.\n\n' - + 'Apple, iPhone, and iPad are registered trademarks of Apple Inc. Apple Watch is a trademark of Apple Inc.\n\n' - + 'Android is a trademark of Google Inc.'; +exports.extendedDesc = + 'Creates a new Titanium application, native module, or Apple Watch™ app.\n\n' + + 'Apple, iPhone, and iPad are registered trademarks of Apple Inc. Apple Watch is a trademark of Apple Inc.\n\n' + + 'Android is a trademark of Google Inc.'; function CreateCommand() { this.creators = {}; @@ -23,90 +24,99 @@ CreateCommand.prototype.config = function config(logger, config, cli) { fields.setup({ colors: cli.argv.colors }); - return finished => { + return (finished) => { // find and load the creators const creatorDir = path.join(__dirname, '..', 'lib', 'creators'); const jsRegExp = /\.js$/; const typeConf = {}; - fs.readdirSync(creatorDir).reduce((promise, filename) => { - return promise.then(() => new Promise(resolve => { - if (!jsRegExp.test(filename)) { - return resolve(); - } + fs.readdirSync(creatorDir) + .reduce((promise, filename) => { + return promise.then( + () => + new Promise((resolve) => { + if (!jsRegExp.test(filename)) { + return resolve(); + } - const CreatorConstructor = require(path.join(creatorDir, filename)); - const creator = new CreatorConstructor(logger, config, cli); - this.creators[creator.type] = creator; + const CreatorConstructor = require(path.join(creatorDir, filename)); + const creator = new CreatorConstructor(logger, config, cli); + this.creators[creator.type] = creator; - try { - if (typeof creator.init === 'function') { - if (creator.init.length > 1) { - typeConf[creator.type] = creator.init(function (conf) { - typeConf[creator.type] = conf; + try { + if (typeof creator.init === 'function') { + if (creator.init.length > 1) { + typeConf[creator.type] = creator.init(function (conf) { + typeConf[creator.type] = conf; + resolve(); + }); + return; + } + typeConf[creator.type] = creator.init(); + } + } catch (ex) { + // squeltch + delete this.creators[creator.type]; + } finally { resolve(); - }); - return; - } - typeConf[creator.type] = creator.init(); - } - } catch (ex) { - // squeltch - delete this.creators[creator.type]; - } finally { - resolve(); - } - })); - }, Promise.resolve()) + } + }) + ); + }, Promise.resolve()) .then(() => { - cli.createHook('create.config', this, callback => { + cli.createHook('create.config', this, (callback) => { const conf = { flags: { force: { abbr: 'f', - desc: 'force project creation even if path already exists' - } + desc: 'force project creation even if path already exists', + }, }, - options: Object.assign({ - type: { - abbr: 't', - default: cli.argv.prompt ? undefined : 'app', - desc: 'the type of project to create', - order: 100, - prompt: callback => { - callback(fields.select({ - title: 'What type of project would you like to create?', - promptLabel: 'Select a type by number or name', - default: 'app', - margin: '', - numbered: true, - relistOnError: true, - complete: true, - suggest: false, - options: Object.keys(this.creators) - .map(function (type) { - return { - label: this.creators[type].title || type, - value: type, - order: this.creators[type].titleOrder - }; - }, this) - .sort(function (a, b) { - return a.order < b.order ? -1 : a.order > b.order ? 1 : 0; + options: Object.assign( + { + type: { + abbr: 't', + default: cli.argv.prompt ? undefined : 'app', + desc: 'the type of project to create', + order: 100, + prompt: (callback) => { + callback( + fields.select({ + title: 'What type of project would you like to create?', + promptLabel: 'Select a type by number or name', + default: 'app', + margin: '', + numbered: true, + relistOnError: true, + complete: true, + suggest: false, + options: Object.keys(this.creators) + .map(function (type) { + return { + label: this.creators[type].title || type, + value: type, + order: this.creators[type].titleOrder, + }; + }, this) + .sort(function (a, b) { + return a.order < b.order ? -1 : a.order > b.order ? 1 : 0; + }), }) - })); + ); + }, + required: true, + values: Object.keys(this.creators), }, - required: true, - values: Object.keys(this.creators) - } - }, ti.commonOptions(logger, config)), - type: typeConf + }, + ti.commonOptions(logger, config) + ), + type: typeConf, }; callback(null, conf); })((_err, result) => finished(result)); }) - .catch(err => { + .catch((err) => { console.log(err); }); }; @@ -118,6 +128,6 @@ CreateCommand.prototype.run = function run(_logger, _config, _cli, finished) { // create the builder instance and expose the public API (function (createCommand) { - exports.config = createCommand.config.bind(createCommand); - exports.run = createCommand.run.bind(createCommand); -}(new CreateCommand())); + exports.config = createCommand.config.bind(createCommand); + exports.run = createCommand.run.bind(createCommand); +})(new CreateCommand()); diff --git a/test/mock-sdk/cli/commands/project.js b/test/mock-sdk/cli/commands/project.js index b5533a29e..e4f0c92e1 100644 --- a/test/mock-sdk/cli/commands/project.js +++ b/test/mock-sdk/cli/commands/project.js @@ -9,42 +9,45 @@ exports.extendedDesc = [ `Run ${'titanium project --project-dir /path/to/project'.cyan} to see all available entries that can be changed.`, [ `When setting the ${'deployment-targets'.cyan} entry, it will non-destructively copy each specified `, - 'platform\'s default resources into your project\'s Resources folder. For ', + "platform's default resources into your project's Resources folder. For ", `example, if your app currently supports ${'iphone'.cyan} and you wish to add Android `, `support, you must specify ${'iphone,android'.cyan}, otherwise only specifying ${'android'.cyan} will remove `, - 'support for iPhone.' - ].join('') + 'support for iPhone.', + ].join(''), ].join('\n\n'); exports.config = function (logger, config) { return { skipBanner: true, - options: Object.assign({ - output: { - abbr: 'o', - default: 'report', - desc: 'output format', - values: ['report', 'json', 'text'] - }, - 'project-dir': { - desc: 'the directory of the project to analyze', - default: '.' + options: Object.assign( + { + output: { + abbr: 'o', + default: 'report', + desc: 'output format', + values: ['report', 'json', 'text'], + }, + 'project-dir': { + desc: 'the directory of the project to analyze', + default: '.', + }, + template: { + desc: 'the name of the project template to use', + default: 'default', + }, }, - template: { - desc: 'the name of the project template to use', - default: 'default' - } - }, ti.commonOptions(logger, config)), + ti.commonOptions(logger, config) + ), args: [ { name: 'key', - desc: 'the key to get or set' + desc: 'the key to get or set', }, { name: 'value', - desc: 'the value to set the specified key' - } - ] + desc: 'the value to set the specified key', + }, + ], }; }; @@ -61,7 +64,15 @@ exports.validate = function (logger, config, cli) { } return function (finished) { - ti.loadPlugins(null, config, cli, cli.argv['project-dir'], finished, cli.argv.output !== 'report' || cli.argv._.length, false); + ti.loadPlugins( + null, + config, + cli, + cli.argv['project-dir'], + finished, + cli.argv.output !== 'report' || cli.argv._.length, + false + ); }; }; diff --git a/test/mock-sdk/cli/hooks/hyperloop-fix.js b/test/mock-sdk/cli/hooks/hyperloop-fix.js index 525890706..2d6d11d9d 100644 --- a/test/mock-sdk/cli/hooks/hyperloop-fix.js +++ b/test/mock-sdk/cli/hooks/hyperloop-fix.js @@ -3,7 +3,7 @@ exports.id = 'com.appcelerator.hyperloop-fix'; exports.init = function init(_logger, _config, cli, _appc) { - cli.env.os.sdkPaths.forEach(_sdkPath => { + cli.env.os.sdkPaths.forEach((_sdkPath) => { // noop }); }; diff --git a/test/mock-sdk/cli/lib/creator.js b/test/mock-sdk/cli/lib/creator.js index d8dfc2afa..8e4596236 100644 --- a/test/mock-sdk/cli/lib/creator.js +++ b/test/mock-sdk/cli/lib/creator.js @@ -44,7 +44,9 @@ Creator.prototype.configOptionId = function configOptionId(order) { logger.error('The App ID must consist of letters, numbers, dashes, and underscores.'); logger.error('Note: Android does not allow dashes and iOS does not allow underscores.'); logger.error('The first character must be a letter or underscore.'); - logger.error('Usually the App ID is your company\'s reversed Internet domain name. (e.g. com.example.myapp)\n'); + logger.error( + "Usually the App ID is your company's reversed Internet domain name. (e.g. com.example.myapp)\n" + ); return callback(true); } @@ -57,13 +59,17 @@ Creator.prototype.configOptionId = function configOptionId(order) { if (!/^([a-zA-Z_]{1}[a-zA-Z0-9_]*(\.[a-zA-Z_]{1}[a-zA-Z0-9_]*)*)$/.test(value)) { logger.error(`Invalid App ID "${value}"`); - logger.error(`Numbers are not allowed directly after periods when targeting ${'Android'.cyan}.\n`); + logger.error( + `Numbers are not allowed directly after periods when targeting ${'Android'.cyan}.\n` + ); return callback(true); } if (!ti.validAppId(value)) { logger.error(`Invalid App ID "${value}"`); - logger.error(`The app must not contain Java reserved words when targeting ${'Android'.cyan}.\n`); + logger.error( + `The app must not contain Java reserved words when targeting ${'Android'.cyan}.\n` + ); return callback(true); } } else { @@ -88,18 +94,28 @@ Creator.prototype.configOptionId = function configOptionId(order) { counter++; } - counter && logger.warn('If you wish to add Android support, you will need to fix the in the tiapp.xml.\n'); + counter && + logger.warn( + 'If you wish to add Android support, you will need to fix the in the tiapp.xml.\n' + ); } if (value.indexOf('_') !== -1) { - if (cli.argv.type !== 'app' && (cli.argv.platforms.indexOf('ios') !== -1 || cli.argv.platforms.indexOf('iphone') !== -1 || cli.argv.platforms.indexOf('ipad') !== -1)) { + if ( + cli.argv.type !== 'app' && + (cli.argv.platforms.indexOf('ios') !== -1 || + cli.argv.platforms.indexOf('iphone') !== -1 || + cli.argv.platforms.indexOf('ipad') !== -1) + ) { logger.error(`Invalid App ID "${value}"`); logger.error(`Underscores are not allowed in the App ID when targeting ${'iOS'.cyan}.\n`); return callback(true); } else { logger.warn('The specified App ID is not compatible with the iOS platform.'); logger.warn('iOS does not allow underscores in the App ID.'); - logger.warn('If you wish to add iOS support, you will need to fix the in the tiapp.xml.\n'); + logger.warn( + 'If you wish to add iOS support, you will need to fix the in the tiapp.xml.\n' + ); } } @@ -107,23 +123,32 @@ Creator.prototype.configOptionId = function configOptionId(order) { } return { - desc: 'the App ID in the format \'com.companyname.appname\'', + desc: "the App ID in the format 'com.companyname.appname'", order: order, prompt(callback) { let defaultValue; const name = cli.argv.name.replace(/[^a-zA-Z0-9]/g, ''); if (idPrefix) { - defaultValue = idPrefix.replace(/\.$/, '') + '.' + (/^[a-zA-Z]/.test(name) || (cli.argv.type === 'app' && cli.argv.platforms.indexOf('android') === -1) ? '' : 'my') + name; + defaultValue = + idPrefix.replace(/\.$/, '') + + '.' + + (/^[a-zA-Z]/.test(name) || + (cli.argv.type === 'app' && cli.argv.platforms.indexOf('android') === -1) + ? '' + : 'my') + + name; } - callback(fields.text({ - default: defaultValue, - promptLabel: 'App ID', - validate: validate - })); + callback( + fields.text({ + default: defaultValue, + promptLabel: 'App ID', + validate: validate, + }) + ); }, required: true, - validate: validate + validate: validate, }; }; @@ -148,7 +173,7 @@ Creator.prototype.configOptionCodeBase = function configCodeBase(order) { required: false, validate: validate, values: validTypes, - hidden: true + hidden: true, }; }; @@ -163,17 +188,38 @@ Creator.prototype.configOptionName = function configOptionName(order) { return callback(true); } - if ((cli.argv.type !== 'app' || cli.argv.platforms.indexOf('android') !== -1) && value.indexOf('&') !== -1) { + if ( + (cli.argv.type !== 'app' || cli.argv.platforms.indexOf('android') !== -1) && + value.indexOf('&') !== -1 + ) { if (config.get('android.allowAppNameAmpersands', false)) { - logger.warn('The project name contains an ampersand (&) which will most likely cause problems.'); - logger.warn('It is recommended that you change the app name in the tiapp.xml or define the app name using i18n strings.'); - logger.warn('Refer to %s for more information.', 'https://titaniumsdk.com/guide/Titanium_SDK/Titanium_SDK_How-tos/Cross-Platform_Mobile_Development_In_Titanium/Internationalization.html'.cyan); + logger.warn( + 'The project name contains an ampersand (&) which will most likely cause problems.' + ); + logger.warn( + 'It is recommended that you change the app name in the tiapp.xml or define the app name using i18n strings.' + ); + logger.warn( + 'Refer to %s for more information.', + 'https://titaniumsdk.com/guide/Titanium_SDK/Titanium_SDK_How-tos/Cross-Platform_Mobile_Development_In_Titanium/Internationalization.html' + .cyan + ); } else { - logger.error('The project name contains an ampersand (&) which will most likely cause problems.'); - logger.error('It is recommended that you change the app name in the tiapp.xml or define the app name using i18n strings.'); - logger.error('Refer to %s for more information.', 'https://titaniumsdk.com/guide/Titanium_SDK/Titanium_SDK_How-tos/Cross-Platform_Mobile_Development_In_Titanium/Internationalization.html'); + logger.error( + 'The project name contains an ampersand (&) which will most likely cause problems.' + ); + logger.error( + 'It is recommended that you change the app name in the tiapp.xml or define the app name using i18n strings.' + ); + logger.error( + 'Refer to %s for more information.', + 'https://titaniumsdk.com/guide/Titanium_SDK/Titanium_SDK_How-tos/Cross-Platform_Mobile_Development_In_Titanium/Internationalization.html' + ); logger.error('To allow ampersands in the app name, run:'); - logger.error(' %sti config android.allowAppNameAmpersands true\n', process.env.APPC_ENV ? 'appc ' : ''); + logger.error( + ' %sti config android.allowAppNameAmpersands true\n', + process.env.APPC_ENV ? 'appc ' : '' + ); return callback(true); } } @@ -186,13 +232,15 @@ Creator.prototype.configOptionName = function configOptionName(order) { desc: 'the name of the project', order: order, prompt(callback) { - callback(fields.text({ - promptLabel: 'Project name', - validate: validate - })); + callback( + fields.text({ + promptLabel: 'Project name', + validate: validate, + }) + ); }, required: true, - validate: validate + validate: validate, }; }; @@ -211,15 +259,19 @@ Creator.prototype.configOptionPlatforms = function configOptionPlatforms(order) let goodValues = {}; const badValues = {}; - value.trim().toLowerCase().split(',').forEach(function (s) { - if (s = s.trim()) { - if (validPlatforms[s]) { - goodValues[s] = 1; - } else { - badValues[s] = 1; + value + .trim() + .toLowerCase() + .split(',') + .forEach(function (s) { + if ((s = s.trim())) { + if (validPlatforms[s]) { + goodValues[s] = 1; + } else { + badValues[s] = 1; + } } - } - }, this); + }, this); const badLen = Object.keys(badValues).length; if (badLen) { @@ -251,16 +303,18 @@ Creator.prototype.configOptionPlatforms = function configOptionPlatforms(order) desc: 'one or more target platforms.', order: order, prompt(callback) { - callback(fields.text({ - promptLabel: `Target platform (${availablePlatforms.join('|')})`, - default: 'all', - validate: validate - })); + callback( + fields.text({ + promptLabel: `Target platform (${availablePlatforms.join('|')})`, + default: 'all', + validate: validate, + }) + ); }, required: true, skipValueCheck: true, validate: validate, - values: availablePlatforms + values: availablePlatforms, }; }; @@ -269,7 +323,7 @@ Creator.prototype.configOptionTemplate = function configOptionTemplate(order, de desc: 'the name of the project template, path to template dir, path to zip file, or URL to zip file', default: defaultValue || 'default', order: order, - required: true + required: true, }; }; @@ -306,7 +360,9 @@ Creator.prototype.configOptionWorkspaceDir = function configOptionWorkspaceDir(o const projectDir = path.join(dir, cli.argv.name); if (fs.existsSync(projectDir)) { logger.error(`Project already exists: ${projectDir}`); - logger.error('Either change the project name, workspace directory, or re-run this command with the --force flag.\n'); + logger.error( + 'Either change the project name, workspace directory, or re-run this command with the --force flag.\n' + ); process.exit(1); } } @@ -316,21 +372,23 @@ Creator.prototype.configOptionWorkspaceDir = function configOptionWorkspaceDir(o return { abbr: 'd', - default: !cli.argv.prompt && workspaceDir || undefined, + default: (!cli.argv.prompt && workspaceDir) || undefined, desc: 'the directory to place the project in', order: order, prompt(callback) { - callback(fields.file({ - complete: true, - default: workspaceDir || '.', - ignoreDirs: new RegExp(config.get('cli.ignoreDirs')), - ignoreFiles: new RegExp(config.get('cli.ignoreFiles')), - promptLabel: 'Directory to place project', - showHidden: true, - validate: validate - })); + callback( + fields.file({ + complete: true, + default: workspaceDir || '.', + ignoreDirs: new RegExp(config.get('cli.ignoreDirs')), + ignoreFiles: new RegExp(config.get('cli.ignoreFiles')), + promptLabel: 'Directory to place project', + showHidden: true, + validate: validate, + }) + ); }, required: true, - validate: validate + validate: validate, }; }; diff --git a/test/mock-sdk/cli/lib/creators/app.js b/test/mock-sdk/cli/lib/creators/app.js index c3681a458..7853830a6 100644 --- a/test/mock-sdk/cli/lib/creators/app.js +++ b/test/mock-sdk/cli/lib/creators/app.js @@ -17,7 +17,7 @@ function AppCreator(_logger, _config, _cli) { const availablePlatforms = {}; const validPlatforms = {}; - ti.platforms.forEach(platform => { + ti.platforms.forEach((platform) => { if (/^iphone|ios|ipad$/.test(platform)) { validPlatforms['iphone'] = availablePlatforms['iphone'] = 1; validPlatforms['ipad'] = availablePlatforms['ipad'] = 1; @@ -39,12 +39,12 @@ util.inherits(AppCreator, Creator); AppCreator.prototype.init = function init() { return { options: { - id: this.configOptionId(150), - name: this.configOptionName(140), - platforms: this.configOptionPlatforms(120), - template: this.configOptionTemplate(110), - 'workspace-dir': this.configOptionWorkspaceDir(170) - } + id: this.configOptionId(150), + name: this.configOptionName(140), + platforms: this.configOptionPlatforms(120), + template: this.configOptionTemplate(110), + 'workspace-dir': this.configOptionWorkspaceDir(170), + }, }; }; diff --git a/test/mock-sdk/cli/lib/creators/module.js b/test/mock-sdk/cli/lib/creators/module.js index 521e7dbf9..8e3181373 100644 --- a/test/mock-sdk/cli/lib/creators/module.js +++ b/test/mock-sdk/cli/lib/creators/module.js @@ -18,7 +18,7 @@ function ModuleCreator(_logger, _config, _cli) { const availablePlatforms = {}; const validPlatforms = {}; - ti.platforms.forEach(platform => { + ti.platforms.forEach((platform) => { if (/^iphone|ios|ipad$/.test(platform)) { validPlatforms['iphone'] = 1; validPlatforms['ipad'] = 1; @@ -40,16 +40,15 @@ util.inherits(ModuleCreator, Creator); ModuleCreator.prototype.init = function init() { return { options: { - id: this.configOptionId(150), - name: this.configOptionName(140), - platforms: this.configOptionPlatforms(120), - template: this.configOptionTemplate(110), + id: this.configOptionId(150), + name: this.configOptionName(140), + platforms: this.configOptionPlatforms(120), + template: this.configOptionTemplate(110), 'workspace-dir': this.configOptionWorkspaceDir(170), - 'code-base': this.configOptionCodeBase(150), + 'code-base': this.configOptionCodeBase(150), 'android-code-base': this.configOptionAndroidCodeBase(150), - 'ios-code-base': this.configOptionIosCodeBase(140) - - } + 'ios-code-base': this.configOptionIosCodeBase(140), + }, }; }; @@ -71,11 +70,13 @@ ModuleCreator.prototype.configOptionAndroidCodeBase = function configAndroidCode order: order, default: !cli.argv.prompt ? 'java' : undefined, prompt(callback) { - callback(fields.text({ - promptLabel: `Android code base (${validTypes.join('|')})`, - default: 'java', - validate: validate - })); + callback( + fields.text({ + promptLabel: `Android code base (${validTypes.join('|')})`, + default: 'java', + validate: validate, + }) + ); }, required: true, validate: validate, @@ -85,7 +86,7 @@ ModuleCreator.prototype.configOptionAndroidCodeBase = function configAndroidCode return callback(true); } return callback(); - } + }, }; }; @@ -107,21 +108,27 @@ ModuleCreator.prototype.configOptionIosCodeBase = function configIosCodeBase(ord order: order, default: !cli.argv.prompt ? 'objc' : undefined, // if we're prompting, then force the platforms to be prompted for, otherwise force 'all' prompt(callback) { - callback(fields.text({ - promptLabel: `iOS code base (${validTypes.join('|')})`, - default: 'objc', - validate: validate - })); + callback( + fields.text({ + promptLabel: `iOS code base (${validTypes.join('|')})`, + default: 'objc', + validate: validate, + }) + ); }, required: true, validate: validate, values: validTypes, verifyIfRequired(callback) { - if (cli.argv.platforms.includes('ios') || cli.argv.platforms.includes('iphone') || cli.argv.platforms.includes('ipad')) { + if ( + cli.argv.platforms.includes('ios') || + cli.argv.platforms.includes('iphone') || + cli.argv.platforms.includes('ipad') + ) { return callback(true); } return callback(); - } + }, }; }; diff --git a/test/mock-sdk/cli/lib/info.js b/test/mock-sdk/cli/lib/info.js index 88885e4d6..bc5b41f96 100644 --- a/test/mock-sdk/cli/lib/info.js +++ b/test/mock-sdk/cli/lib/info.js @@ -18,36 +18,46 @@ exports.name = 'miscinfo'; exports.title = 'Misc Info'; exports.detect = function (types, config, callback) { - const results = this.data = {}, - tisdk = path.basename((function scan(dir) { - const file = path.join(dir, 'manifest.json'); - if (fs.existsSync(file)) { - return dir; - } - dir = path.dirname(dir); - return dir !== '/' && scan(dir); - }(__dirname))); - - appc.async.parallel(this, [ - function (next) { - genymotion.detect(config, null, function (err, genymotionInfo) { - if (err) { - return next(err); + const results = (this.data = {}), + tisdk = path.basename( + (function scan(dir) { + const file = path.join(dir, 'manifest.json'); + if (fs.existsSync(file)) { + return dir; } + dir = path.dirname(dir); + return dir !== '/' && scan(dir); + })(__dirname) + ); - genymotionInfo.tisdk = tisdk; - results.genymotion = genymotionInfo; + appc.async.parallel( + this, + [ + function (next) { + genymotion.detect( + config, + null, + function (err, genymotionInfo) { + if (err) { + return next(err); + } - if (genymotionInfo.issues.length) { - this.issues = this.issues.concat(genymotionInfo.issues); - } + genymotionInfo.tisdk = tisdk; + results.genymotion = genymotionInfo; - next(); - }.bind(this)); + if (genymotionInfo.issues.length) { + this.issues = this.issues.concat(genymotionInfo.issues); + } + + next(); + }.bind(this) + ); + }, + ], + function (err) { + callback(err, results); } - ], function (err) { - callback(err, results); - }); + ); }; exports.render = function (logger, config, rpad, styleHeading, styleValue) { @@ -56,15 +66,49 @@ exports.render = function (logger, config, rpad, styleHeading, styleValue) { return; } - logger.log(styleHeading(__('Genymotion')) + '\n' - + ' ' + rpad(__('Path')) + ' = ' + styleValue(data.genymotion.path || __('not found')) + '\n' - + ' ' + rpad(__('Genymotion Executable')) + ' = ' + styleValue(data.genymotion.executables && data.genymotion.executables.genymotion || __('not found')) + '\n' - + ' ' + rpad(__('Genymotion Player')) + ' = ' + styleValue(data.genymotion.executables && data.genymotion.executables.player || __('not found')) + '\n' - + ' ' + rpad(__('Home')) + ' = ' + styleValue(data.genymotion.home || __('not found')) + '\n' + logger.log( + styleHeading(__('Genymotion')) + + '\n' + + ' ' + + rpad(__('Path')) + + ' = ' + + styleValue(data.genymotion.path || __('not found')) + + '\n' + + ' ' + + rpad(__('Genymotion Executable')) + + ' = ' + + styleValue( + (data.genymotion.executables && data.genymotion.executables.genymotion) || __('not found') + ) + + '\n' + + ' ' + + rpad(__('Genymotion Player')) + + ' = ' + + styleValue( + (data.genymotion.executables && data.genymotion.executables.player) || __('not found') + ) + + '\n' + + ' ' + + rpad(__('Home')) + + ' = ' + + styleValue(data.genymotion.home || __('not found')) + + '\n' ); - logger.log(styleHeading(__('VirtualBox')) + '\n' - + ' ' + rpad(__('Executable')) + ' = ' + styleValue(data.genymotion.executables && data.genymotion.executables.vboxmanage || __('not found')) + '\n' - + ' ' + rpad(__('Version')) + ' = ' + styleValue(data.genymotion.virtualbox || __('unknown')) + '\n' + logger.log( + styleHeading(__('VirtualBox')) + + '\n' + + ' ' + + rpad(__('Executable')) + + ' = ' + + styleValue( + (data.genymotion.executables && data.genymotion.executables.vboxmanage) || __('not found') + ) + + '\n' + + ' ' + + rpad(__('Version')) + + ' = ' + + styleValue(data.genymotion.virtualbox || __('unknown')) + + '\n' ); }; diff --git a/test/mock-sdk/cli/lib/node-titanium-sdk/builder.js b/test/mock-sdk/cli/lib/node-titanium-sdk/builder.js index 714cb7dad..49f3a337d 100644 --- a/test/mock-sdk/cli/lib/node-titanium-sdk/builder.js +++ b/test/mock-sdk/cli/lib/node-titanium-sdk/builder.js @@ -14,7 +14,7 @@ function Builder(buildModule) { } dir = path.dirname(dir); return dir !== '/' && scan(dir); - }(__dirname)); + })(__dirname); this.titaniumSdkName = path.basename(this.titaniumSdkPath); @@ -27,7 +27,7 @@ function Builder(buildModule) { } dir = path.dirname(dir); return dir !== '/' && scan(dir); - }(path.dirname(buildModule.filename))); + })(path.dirname(buildModule.filename)); this.platformName = path.basename(this.platformPath); @@ -60,13 +60,16 @@ Builder.prototype.validate = function validate(logger, config, cli) { this.defaultIcons = [ path.join(this.projectDir, 'DefaultIcon-' + this.platformName + '.png'), - path.join(this.projectDir, 'DefaultIcon.png') + path.join(this.projectDir, 'DefaultIcon.png'), ]; }; -Builder.prototype.run = function run(_logger, _config, _cli, _finished) { -}; +Builder.prototype.run = function run(_logger, _config, _cli, _finished) {}; -Builder.prototype.validateTiModules = function validateTiModules(_platformName, _deployType, callback) { +Builder.prototype.validateTiModules = function validateTiModules( + _platformName, + _deployType, + callback +) { callback(null, []); }; diff --git a/test/mock-sdk/cli/lib/node-titanium-sdk/ti.js b/test/mock-sdk/cli/lib/node-titanium-sdk/ti.js index 701042137..3bc784c53 100644 --- a/test/mock-sdk/cli/lib/node-titanium-sdk/ti.js +++ b/test/mock-sdk/cli/lib/node-titanium-sdk/ti.js @@ -3,12 +3,12 @@ const path = require('path'); const manifest = { platforms: ['android'], - sdkVersion: '0.0.0.GA' + sdkVersion: '0.0.0.GA', }; const platformAliases = { // add additional aliases here for new platforms ipad: 'iphone', - ios: 'iphone' + ios: 'iphone', }; exports.manifest = manifest; @@ -16,7 +16,16 @@ exports.platforms = manifest.platforms; exports.targetPlatforms = ['android']; exports.availablePlatforms = ['android']; exports.availablePlatformsNames = ['Android']; -exports.allPlatformNames = ['android', 'ios', 'iphone', 'ipad', 'mobileweb', 'blackberry', 'windows', 'tizen']; +exports.allPlatformNames = [ + 'android', + 'ios', + 'iphone', + 'ipad', + 'mobileweb', + 'blackberry', + 'windows', + 'tizen', +]; function commonOptions(logger, config) { return { @@ -28,8 +37,8 @@ function commonOptions(logger, config) { desc: 'minimum logging level', default: config.cli.logLevel || 'trace', hint: 'level', - values: logger.getLevels() - } + values: logger.getLevels(), + }, }; } @@ -37,24 +46,35 @@ function loadPlugins(_logger, config, cli, projectDir, finished, silent, compact let searchPaths = { project: [path.join(projectDir, 'plugins')], config: [], - global: [] + global: [], }; let confPaths = config.get('paths.plugins'); let defaultInstallLocation = cli.env.installPath; - let sdkLocations = cli.env.os.sdkPaths.map(function (p) { return path.resolve(p); }); + let sdkLocations = cli.env.os.sdkPaths.map(function (p) { + return path.resolve(p); + }); // set our paths from the config file - Array.isArray(confPaths) || (confPaths = [ confPaths ]); + Array.isArray(confPaths) || (confPaths = [confPaths]); confPaths.forEach(function (p) { - p && fs.existsSync(p = path.resolve(p)) && searchPaths.project.indexOf(p) === -1 && searchPaths.config.indexOf(p) === -1 && (searchPaths.config.push(p)); + p && + fs.existsSync((p = path.resolve(p))) && + searchPaths.project.indexOf(p) === -1 && + searchPaths.config.indexOf(p) === -1 && + searchPaths.config.push(p); }); // add any plugins from various SDK locations sdkLocations.indexOf(defaultInstallLocation) === -1 && sdkLocations.push(defaultInstallLocation); cli.sdk && sdkLocations.push(path.resolve(cli.sdk.path, '..', '..', '..')); - sdkLocations.forEach(p => { + sdkLocations.forEach((p) => { p = path.resolve(p, 'plugins'); - if (fs.existsSync(p) && searchPaths.project.indexOf(p) === -1 && searchPaths.config.indexOf(p) === -1 && searchPaths.global.indexOf(p) === -1) { + if ( + fs.existsSync(p) && + searchPaths.project.indexOf(p) === -1 && + searchPaths.config.indexOf(p) === -1 && + searchPaths.global.indexOf(p) === -1 + ) { searchPaths.global.push(p); } }); @@ -77,12 +97,13 @@ function platformOptions(logger, config, cli, commandName, finished) { function set(obj, title, platform) { // add the platform and title to the options and flags - ['options', 'flags'].forEach(type => { + ['options', 'flags'].forEach((type) => { if (obj && obj[type]) { - result[platform] || (result[platform] = { - platform: platform, - title: title || platform - }); + result[platform] || + (result[platform] = { + platform: platform, + title: title || platform, + }); result[platform][type] = obj[type]; } }); @@ -92,49 +113,58 @@ function platformOptions(logger, config, cli, commandName, finished) { targetPlatform = platformAliases[targetPlatform] || targetPlatform; // for each platform, fetch their specific flags/options - manifest.platforms.reduce((promise, platform) => { - return promise.then(() => new Promise(resolve => { - // only configure target platform - if (targetPlatform && platform !== targetPlatform) { - return resolve(); - } - - let platformDir = path.join(path.dirname(module.filename), '..', '..', '..', platform); - let platformCommand = path.join(platformDir, 'cli', 'commands', '_' + commandName + '.js'); - let command; - let conf; - let title; - - if (!fs.existsSync(platformCommand)) { - return resolve(); - } - - command = require(platformCommand); - if (!command || !command.config) { - return resolve(); - } - - // try to get the platform specific configuration - conf = command.config(logger, config, cli); - - try { - // try to read a title from the platform's package.json - title = JSON.parse(fs.readFileSync(path.join(platformDir, 'package.json'))).title; - } catch (e) {} - - if (typeof conf === 'function') { - // async callback - conf(function (obj) { - set(obj, title, platform); - resolve(); - }); - return; - } - - set(conf, title, platform); - resolve(); - })); - }, Promise.resolve()) + manifest.platforms + .reduce((promise, platform) => { + return promise.then( + () => + new Promise((resolve) => { + // only configure target platform + if (targetPlatform && platform !== targetPlatform) { + return resolve(); + } + + let platformDir = path.join(path.dirname(module.filename), '..', '..', '..', platform); + let platformCommand = path.join( + platformDir, + 'cli', + 'commands', + '_' + commandName + '.js' + ); + let command; + let conf; + let title; + + if (!fs.existsSync(platformCommand)) { + return resolve(); + } + + command = require(platformCommand); + if (!command || !command.config) { + return resolve(); + } + + // try to get the platform specific configuration + conf = command.config(logger, config, cli); + + try { + // try to read a title from the platform's package.json + title = JSON.parse(fs.readFileSync(path.join(platformDir, 'package.json'))).title; + } catch (e) {} + + if (typeof conf === 'function') { + // async callback + conf(function (obj) { + set(obj, title, platform); + resolve(); + }); + return; + } + + set(conf, title, platform); + resolve(); + }) + ); + }, Promise.resolve()) .then(() => finished(result)) .catch(() => finished(result)); } @@ -148,23 +178,26 @@ function scrubPlatforms(platforms) { const original = {}; const bad = {}; - platforms.toLowerCase().split(',').forEach(platform => { - const name = platformAliases[platform] || platform; - // if name is falsey, then it's invalid anyways - if (name) { - if (manifest.platforms.indexOf(name) === -1) { - bad[platform] = 1; - } else { - scrubbed[name] = 1; - original[platform] = 1; + platforms + .toLowerCase() + .split(',') + .forEach((platform) => { + const name = platformAliases[platform] || platform; + // if name is falsey, then it's invalid anyways + if (name) { + if (manifest.platforms.indexOf(name) === -1) { + bad[platform] = 1; + } else { + scrubbed[name] = 1; + original[platform] = 1; + } } - } - }); + }); return { scrubbed: Object.keys(scrubbed).sort(), // distinct list of un-aliased platforms original: Object.keys(original).sort(), - bad: Object.keys(bad).sort() + bad: Object.keys(bad).sort(), }; } @@ -222,7 +255,7 @@ function validAppId(id) { try: 1, void: 1, volatile: 1, - while: 1 + while: 1, }; const parts = id.split('.'); const l = parts.length; @@ -247,7 +280,7 @@ function validateModuleManifest(logger, cli, manifest) { 'copyright', 'platform', 'minsdk', - 'architectures' + 'architectures', ]; // check if all the required module keys are in the list @@ -268,16 +301,27 @@ function validateModuleManifest(logger, cli, manifest) { function validatePlatformOptions(logger, config, cli, commandName) { const platform = exports.resolvePlatform(cli.argv.platform), - platformCommand = path.join(path.dirname(module.filename), '..', '..', '..', manifest.platforms[manifest.platforms.indexOf(platform)], 'cli', 'commands', '_' + commandName + '.js'); + platformCommand = path.join( + path.dirname(module.filename), + '..', + '..', + '..', + manifest.platforms[manifest.platforms.indexOf(platform)], + 'cli', + 'commands', + '_' + commandName + '.js' + ); if (fs.existsSync(platformCommand)) { const command = require(platformCommand); - return command && typeof command.validate === 'function' ? command.validate(logger, config, cli) : null; + return command && typeof command.validate === 'function' + ? command.validate(logger, config, cli) + : null; } } function validateProjectDir(logger, cli, argv, name) { const dir = argv[name] || '.'; - let projectDir = argv[name] = path.resolve(dir); + let projectDir = (argv[name] = path.resolve(dir)); if (!fs.existsSync(projectDir)) { logger.banner(); @@ -294,7 +338,8 @@ function validateProjectDir(logger, cli, argv, name) { if (tiapp.split(path.sep).length === 2) { logger.banner(); logger.error(`Invalid project directory "${dir}"\n`); - dir === '.' && logger.log(`Use the ${'--project-dir'.cyan} property to specify the project's directory\n`); + dir === '.' && + logger.log(`Use the ${'--project-dir'.cyan} property to specify the project's directory\n`); process.exit(1); } diff --git a/test/mock-sdk/manifest.json b/test/mock-sdk/manifest.json index 901c8ef89..2df2eea22 100644 --- a/test/mock-sdk/manifest.json +++ b/test/mock-sdk/manifest.json @@ -1 +1,8 @@ -{"name":"0.0.0.GA","version":"0.0.0","moduleAPIVersion":{"iphone":"2","android":"4"},"timestamp":"1/1/2023 00:00","githash":"1234567890","platforms":["android"]} +{ + "name": "0.0.0.GA", + "version": "0.0.0", + "moduleAPIVersion": { "iphone": "2", "android": "4" }, + "timestamp": "1/1/2023 00:00", + "githash": "1234567890", + "platforms": ["android"] +} diff --git a/test/mock-sdk/package.json b/test/mock-sdk/package.json index 1f8335a37..383a09cc2 100644 --- a/test/mock-sdk/package.json +++ b/test/mock-sdk/package.json @@ -1,15 +1,15 @@ { "name": "titanium-mobile", - "description": "TiDev Titanium Mobile", "version": "0.0.0", + "description": "TiDev Titanium Mobile", + "engines": { + "node": ">=12.13.0" + }, "moduleApiVersion": { "iphone": "2", "android": "4" }, "vendorDependencies": { "node": "12.x || 14.x || 16.x" - }, - "engines": { - "node": ">=12.13.0" } } diff --git a/test/util/arrayify.test.js b/test/util/arrayify.test.js index cb9994382..75926a3c4 100644 --- a/test/util/arrayify.test.js +++ b/test/util/arrayify.test.js @@ -1,6 +1,6 @@ -import { describe, it } from 'node:test'; -import assert from 'node:assert'; import { arrayify } from '../../src/util/arrayify.js'; +import assert from 'node:assert'; +import { describe, it } from 'node:test'; describe('arrayify', () => { it('should init undefined array', () => { @@ -15,30 +15,15 @@ describe('arrayify', () => { }); it('should arrayify a set', () => { - assert.deepStrictEqual( - arrayify(new Set([1, 'a', true])), - [1, 'a', true] - ); + assert.deepStrictEqual(arrayify(new Set([1, 'a', true])), [1, 'a', true]); }); it('should remove falsey values', () => { - assert.deepStrictEqual( - arrayify([ - 0, - 1, - null, - undefined, - '', - 'a', - true, - false - ], true), - [ - 0, - 1, - 'a', - true - ] - ); + assert.deepStrictEqual(arrayify([0, 1, null, undefined, '', 'a', true, false], true), [ + 0, + 1, + 'a', + true, + ]); }); }); diff --git a/test/util/busyindicator.test.js b/test/util/busyindicator.test.js index cd80a6488..de626c95a 100644 --- a/test/util/busyindicator.test.js +++ b/test/util/busyindicator.test.js @@ -1,8 +1,8 @@ -import { describe, it } from 'node:test'; -import assert from 'node:assert'; import { BusyIndicator } from '../../src/util/busyindicator.js'; -import { setTimeout } from 'node:timers/promises'; import { MockStream } from '../helpers/mock-stream.js'; +import assert from 'node:assert'; +import { describe, it } from 'node:test'; +import { setTimeout } from 'node:timers/promises'; describe('BusyIndicator', () => { it('should render a busy indicator', async () => { diff --git a/test/util/capitalize.test.js b/test/util/capitalize.test.js index 8b3eab07c..35e85de40 100644 --- a/test/util/capitalize.test.js +++ b/test/util/capitalize.test.js @@ -1,6 +1,6 @@ -import { describe, it } from 'node:test'; -import assert from 'node:assert'; import { capitalize } from '../../src/util/capitalize.js'; +import assert from 'node:assert'; +import { describe, it } from 'node:test'; describe('capitalize', () => { it('should capitalize a string', () => { diff --git a/test/util/columns.test.js b/test/util/columns.test.js index 170244f3f..94552fb90 100644 --- a/test/util/columns.test.js +++ b/test/util/columns.test.js @@ -1,6 +1,6 @@ -import { describe, it } from 'node:test'; -import assert from 'node:assert'; import { columns } from '../../src/util/columns.js'; +import assert from 'node:assert'; +import { describe, it } from 'node:test'; const data = [ 'aaaa', @@ -28,51 +28,54 @@ const data = [ 'wwww', 'xxxx', 'yyyy', - 'zzzz' + 'zzzz', ]; describe('columns', () => { it('should render columns with no wrapping', () => { - assert.strictEqual(columns([ - 'foo', - 'bar' - ]), 'foo bar'); + assert.strictEqual(columns(['foo', 'bar']), 'foo bar'); }); it('should render columns with margin', () => { - assert.strictEqual(columns([ - 'foo', - 'bar' - ], ' '), ' foo bar'); + assert.strictEqual(columns(['foo', 'bar'], ' '), ' foo bar'); }); it('should render lots of data in columns with wrapping', () => { - assert.strictEqual(columns(data, ' ', 50), [ - ' aaaa hhhh oooo vvvv', - ' bbbb iiii pppp wwww', - ' cccc jjjj qqqq xxxx', - ' dddd kkkk rrrr yyyy', - ' eeee llll ssss zzzz', - ' ffff mmmm tttt', - ' gggg nnnn uuuu' - ].join('\n')); + assert.strictEqual( + columns(data, ' ', 50), + [ + ' aaaa hhhh oooo vvvv', + ' bbbb iiii pppp wwww', + ' cccc jjjj qqqq xxxx', + ' dddd kkkk rrrr yyyy', + ' eeee llll ssss zzzz', + ' ffff mmmm tttt', + ' gggg nnnn uuuu', + ].join('\n') + ); }); it('should render lots of data with huge width', () => { - assert.strictEqual(columns(data, ' ', 1000), [ - ' aaaa eeee iiii mmmm qqqq uuuu yyyy', - ' bbbb ffff jjjj nnnn rrrr vvvv zzzz', - ' cccc gggg kkkk oooo ssss wwww', - ' dddd hhhh llll pppp tttt xxxx' - ].join('\n')); + assert.strictEqual( + columns(data, ' ', 1000), + [ + ' aaaa eeee iiii mmmm qqqq uuuu yyyy', + ' bbbb ffff jjjj nnnn rrrr vvvv zzzz', + ' cccc gggg kkkk oooo ssss wwww', + ' dddd hhhh llll pppp tttt xxxx', + ].join('\n') + ); }); it('should render lots of data with zero width', () => { - assert.strictEqual(columns(data, ' ', 0), [ - ' aaaa eeee iiii mmmm qqqq uuuu yyyy', - ' bbbb ffff jjjj nnnn rrrr vvvv zzzz', - ' cccc gggg kkkk oooo ssss wwww', - ' dddd hhhh llll pppp tttt xxxx' - ].join('\n')); + assert.strictEqual( + columns(data, ' ', 0), + [ + ' aaaa eeee iiii mmmm qqqq uuuu yyyy', + ' bbbb ffff jjjj nnnn rrrr vvvv zzzz', + ' cccc gggg kkkk oooo ssss wwww', + ' dddd hhhh llll pppp tttt xxxx', + ].join('\n') + ); }); }); diff --git a/test/util/detect.test.js b/test/util/detect.test.js index c5daa76ec..640ea0a47 100644 --- a/test/util/detect.test.js +++ b/test/util/detect.test.js @@ -1,8 +1,8 @@ -import { describe, it } from 'node:test'; -import assert from 'node:assert'; import { detect } from '../../src/util/detect.js'; import { TiConfig } from '../../src/util/ticonfig.js'; +import assert from 'node:assert'; import { join } from 'node:path'; +import { describe, it } from 'node:test'; import { fileURLToPath } from 'node:url'; const goodConfig = join(fileURLToPath(import.meta.url), '../fixtures/ticonfig/good.json'); @@ -10,7 +10,7 @@ const goodConfig = join(fileURLToPath(import.meta.url), '../fixtures/ticonfig/go describe('detect', () => { it('should detect all development environment', async () => { const mockCLI = { - version: '1.2.3' + version: '1.2.3', }; const results = await detect(null, new TiConfig(goodConfig), mockCLI); assert(Object.hasOwn(results, 'data')); @@ -21,7 +21,7 @@ describe('detect', () => { it('should detect just Titanium development environment', async () => { const mockCLI = { - version: '1.2.3' + version: '1.2.3', }; const results = await detect(null, new TiConfig(goodConfig), mockCLI, { titanium: true }); assert(Object.hasOwn(results, 'data')); diff --git a/test/util/expand.test.js b/test/util/expand.test.js index e2ede75bb..f75f7a1e1 100644 --- a/test/util/expand.test.js +++ b/test/util/expand.test.js @@ -1,14 +1,14 @@ -import { afterEach, beforeEach, describe, it } from 'node:test'; -import assert from 'node:assert'; import { expand } from '../../src/util/expand.js'; +import assert from 'node:assert'; +import { afterEach, beforeEach, describe, it } from 'node:test'; const backup = {}; describe('expand', () => { beforeEach(() => { - backup.HOME = process.env.HOME; + backup.HOME = process.env.HOME; backup.USERPROFILE = process.env.USERPROFILE; - backup.SystemRoot = process.env.SystemRoot; + backup.SystemRoot = process.env.SystemRoot; }); afterEach(() => { diff --git a/test/util/fixtures/ticonfig/good.json b/test/util/fixtures/ticonfig/good.json index 8572c6bd3..8199f8e74 100644 --- a/test/util/fixtures/ticonfig/good.json +++ b/test/util/fixtures/ticonfig/good.json @@ -1,8 +1,8 @@ { - "cli": { - "width": "" - }, - "user": { - "name": "Titanium" - } + "cli": { + "width": "" + }, + "user": { + "name": "Titanium" + } } diff --git a/test/util/logger.test.js b/test/util/logger.test.js index 008c213d2..362a893f6 100644 --- a/test/util/logger.test.js +++ b/test/util/logger.test.js @@ -1,8 +1,8 @@ -import { describe, it } from 'node:test'; -import assert from 'node:assert'; import { Logger } from '../../src/util/logger.js'; -import { WritableStream } from 'memory-streams'; import { stripColor } from '../helpers/strip-color.js'; +import { WritableStream } from 'memory-streams'; +import assert from 'node:assert'; +import { describe, it } from 'node:test'; describe('Logger', () => { it('should log using all log levels', () => { @@ -10,7 +10,7 @@ describe('Logger', () => { const stdout = new WritableStream(); const logger = new Logger('trace', { stdout, - stderr + stderr, }); logger.log('log test'); @@ -24,13 +24,7 @@ describe('Logger', () => { assert.strictEqual( stripColor(stdout.toString()), - [ - 'log test', - 'log test', - 'log test', - '[INFO] info test', - '' - ].join('\n') + ['log test', 'log test', 'log test', '[INFO] info test', ''].join('\n') ); assert.strictEqual( @@ -40,7 +34,7 @@ describe('Logger', () => { '[DEBUG] debug test', '[ERROR] error test', '[WARN] warn test', - '' + '', ].join('\n') ); }); @@ -50,7 +44,7 @@ describe('Logger', () => { const stdout = new WritableStream(); const logger = new Logger('warn', { stdout, - stderr + stderr, }); logger.log('log test'); @@ -64,21 +58,12 @@ describe('Logger', () => { assert.strictEqual( stripColor(stdout.toString()), - [ - 'log test', - 'log test', - 'log test', - '' - ].join('\n') + ['log test', 'log test', 'log test', ''].join('\n') ); assert.strictEqual( stripColor(stderr.toString()), - [ - '[ERROR] error test', - '[WARN] warn test', - '' - ].join('\n') + ['[ERROR] error test', '[WARN] warn test', ''].join('\n') ); }); @@ -87,49 +72,27 @@ describe('Logger', () => { const stdout = new WritableStream(); const logger = new Logger('info', { stdout, - stderr + stderr, }); logger.debug('debug test'); logger.info('info test'); logger.warn('warn test'); - assert.strictEqual( - stripColor(stdout.toString()), - [ - '[INFO] info test', - '' - ].join('\n') - ); + assert.strictEqual(stripColor(stdout.toString()), ['[INFO] info test', ''].join('\n')); - assert.strictEqual( - stripColor(stderr.toString()), - [ - '[WARN] warn test', - '' - ].join('\n') - ); + assert.strictEqual(stripColor(stderr.toString()), ['[WARN] warn test', ''].join('\n')); logger.setLevel('warn'); logger.debug('debug test'); logger.info('info test'); logger.warn('warn test'); - assert.strictEqual( - stripColor(stdout.toString()), - [ - '[INFO] info test', - '' - ].join('\n') - ); + assert.strictEqual(stripColor(stdout.toString()), ['[INFO] info test', ''].join('\n')); assert.strictEqual( stripColor(stderr.toString()), - [ - '[WARN] warn test', - '[WARN] warn test', - '' - ].join('\n') + ['[WARN] warn test', '[WARN] warn test', ''].join('\n') ); logger.setLevel(2); // debug @@ -139,11 +102,7 @@ describe('Logger', () => { assert.strictEqual( stripColor(stdout.toString()), - [ - '[INFO] info test', - '[INFO] info test', - '' - ].join('\n') + ['[INFO] info test', '[INFO] info test', ''].join('\n') ); assert.strictEqual( @@ -153,7 +112,7 @@ describe('Logger', () => { '[WARN] warn test', '[DEBUG] debug test', '[WARN] warn test', - '' + '', ].join('\n') ); }); @@ -163,7 +122,7 @@ describe('Logger', () => { const stdout = new WritableStream(); const logger = new Logger('info', { stdout, - stderr + stderr, }); logger.silence(); @@ -185,19 +144,17 @@ describe('Logger', () => { const stdout = new WritableStream(); const logger = new Logger('info', { stdout, - stderr + stderr, }); let emittedCount = 0; logger.on('cli:logger-banner', () => { emittedCount++; }); - const expected = new RegExp([ - 'foo v1.2.3 SDK v4.5.6', - 'bar', - '', - 'Please star us on GitHub!' - ].join('\n'), 's'); + const expected = new RegExp( + ['foo v1.2.3 SDK v4.5.6', 'bar', '', 'Please star us on GitHub!'].join('\n'), + 's' + ); logger.setBanner({ name: 'foo', copyright: 'bar', version: '1.2.3', sdkVersion: '4.5.6' }); assert.match(stripColor(logger.getBanner()), expected); @@ -224,7 +181,7 @@ describe('Logger', () => { const stdout = new WritableStream(); const logger = new Logger('info', { stdout, - stderr + stderr, }); let emittedCount = 0; logger.on('cli:logger-banner', () => { @@ -250,7 +207,7 @@ describe('Logger', () => { const stdout = new WritableStream(); const logger = new Logger('info', { stdout, - stderr + stderr, }); let emittedCount = 0; logger.on('cli:logger-banner', () => { @@ -274,7 +231,7 @@ describe('Logger', () => { const stdout = new WritableStream(); const logger = new Logger('info', { stdout, - stderr + stderr, }); assert.strictEqual(logger.timestampEnabled(), false); diff --git a/test/util/progress.test.js b/test/util/progress.test.js index be1ae8b0d..7913481b5 100644 --- a/test/util/progress.test.js +++ b/test/util/progress.test.js @@ -1,23 +1,20 @@ -import { describe, it } from 'node:test'; -import assert from 'node:assert'; import { ProgressBar } from '../../src/util/progress.js'; import { MockStream } from '../helpers/mock-stream.js'; +import assert from 'node:assert'; +import { describe, it } from 'node:test'; describe('progress', () => { it('should render a progress bar', async () => { const stream = new MockStream(); - const bar = new ProgressBar( - ':paddedPercent [:bar] :current of :total (:percent)', - { - complete: '=', - incomplete: '.', - width: 40, - total: 10, - stream - } - ); + const bar = new ProgressBar(':paddedPercent [:bar] :current of :total (:percent)', { + complete: '=', + incomplete: '.', + width: 40, + total: 10, + stream, + }); - await new Promise(resolve => { + await new Promise((resolve) => { let timer = setInterval(() => { bar.tick(); if (bar.complete) { @@ -29,33 +26,30 @@ describe('progress', () => { assert.strictEqual( stream.buffer, - ' 10% [====....................................] 1 of 10 (10%)\n' - + ' 20% [========................................] 2 of 10 (20%)\n' - + ' 30% [============............................] 3 of 10 (30%)\n' - + ' 40% [================........................] 4 of 10 (40%)\n' - + ' 50% [====================....................] 5 of 10 (50%)\n' - + ' 60% [========================................] 6 of 10 (60%)\n' - + ' 70% [============================............] 7 of 10 (70%)\n' - + ' 80% [================================........] 8 of 10 (80%)\n' - + ' 90% [====================================....] 9 of 10 (90%)\n' - + '100% [========================================] 10 of 10 (100%)\n' + ' 10% [====....................................] 1 of 10 (10%)\n' + + ' 20% [========................................] 2 of 10 (20%)\n' + + ' 30% [============............................] 3 of 10 (30%)\n' + + ' 40% [================........................] 4 of 10 (40%)\n' + + ' 50% [====================....................] 5 of 10 (50%)\n' + + ' 60% [========================................] 6 of 10 (60%)\n' + + ' 70% [============================............] 7 of 10 (70%)\n' + + ' 80% [================================........] 8 of 10 (80%)\n' + + ' 90% [====================================....] 9 of 10 (90%)\n' + + '100% [========================================] 10 of 10 (100%)\n' ); }); it('should render a progress bar with swapped tick() params', async () => { const stream = new MockStream(); - const bar = new ProgressBar( - ':paddedPercent [:bar] :current of :total (:percent)', - { - complete: '=', - incomplete: '.', - width: 40, - total: 10, - stream - } - ); + const bar = new ProgressBar(':paddedPercent [:bar] :current of :total (:percent)', { + complete: '=', + incomplete: '.', + width: 40, + total: 10, + stream, + }); - await new Promise(resolve => { + await new Promise((resolve) => { let timer = setInterval(() => { bar.tick({}, 1); if (bar.complete) { @@ -67,36 +61,33 @@ describe('progress', () => { assert.strictEqual( stream.buffer, - ' 10% [====....................................] 1 of 10 (10%)\n' - + ' 20% [========................................] 2 of 10 (20%)\n' - + ' 30% [============............................] 3 of 10 (30%)\n' - + ' 40% [================........................] 4 of 10 (40%)\n' - + ' 50% [====================....................] 5 of 10 (50%)\n' - + ' 60% [========================................] 6 of 10 (60%)\n' - + ' 70% [============================............] 7 of 10 (70%)\n' - + ' 80% [================================........] 8 of 10 (80%)\n' - + ' 90% [====================================....] 9 of 10 (90%)\n' - + '100% [========================================] 10 of 10 (100%)\n' + ' 10% [====....................................] 1 of 10 (10%)\n' + + ' 20% [========................................] 2 of 10 (20%)\n' + + ' 30% [============............................] 3 of 10 (30%)\n' + + ' 40% [================........................] 4 of 10 (40%)\n' + + ' 50% [====================....................] 5 of 10 (50%)\n' + + ' 60% [========================................] 6 of 10 (60%)\n' + + ' 70% [============================............] 7 of 10 (70%)\n' + + ' 80% [================================........] 8 of 10 (80%)\n' + + ' 90% [====================================....] 9 of 10 (90%)\n' + + '100% [========================================] 10 of 10 (100%)\n' ); }); it('should render custom tokens', async () => { const stream = new MockStream(); - const bar = new ProgressBar( - ':paddedPercent [:bar] :decimal', - { - complete: '=', - incomplete: '.', - width: 40, - total: 10, - stream - } - ); + const bar = new ProgressBar(':paddedPercent [:bar] :decimal', { + complete: '=', + incomplete: '.', + width: 40, + total: 10, + stream, + }); - await new Promise(resolve => { + await new Promise((resolve) => { let timer = setInterval(() => { bar.tick(1, { - decimal: ((bar.curr + 1) / bar.total).toFixed(1) + decimal: ((bar.curr + 1) / bar.total).toFixed(1), }); if (bar.complete) { clearInterval(timer); @@ -107,16 +98,16 @@ describe('progress', () => { assert.strictEqual( stream.buffer, - ' 10% [====....................................] 0.1\n' - + ' 20% [========................................] 0.2\n' - + ' 30% [============............................] 0.3\n' - + ' 40% [================........................] 0.4\n' - + ' 50% [====================....................] 0.5\n' - + ' 60% [========================................] 0.6\n' - + ' 70% [============================............] 0.7\n' - + ' 80% [================================........] 0.8\n' - + ' 90% [====================================....] 0.9\n' - + '100% [========================================] 1.0\n' + ' 10% [====....................................] 0.1\n' + + ' 20% [========................................] 0.2\n' + + ' 30% [============............................] 0.3\n' + + ' 40% [================........................] 0.4\n' + + ' 50% [====================....................] 0.5\n' + + ' 60% [========================................] 0.6\n' + + ' 70% [============================............] 0.7\n' + + ' 80% [================================........] 0.8\n' + + ' 90% [====================================....] 0.9\n' + + '100% [========================================] 1.0\n' ); }); }); diff --git a/test/util/proxy.test.js b/test/util/proxy.test.js index d03c404d4..dd701c939 100644 --- a/test/util/proxy.test.js +++ b/test/util/proxy.test.js @@ -1,6 +1,6 @@ -import { afterEach, describe, it } from 'node:test'; -import assert from 'node:assert'; import { detect } from '../../src/util/proxy.js'; +import assert from 'node:assert'; +import { afterEach, describe, it } from 'node:test'; describe('proxy', () => { afterEach(() => { diff --git a/test/util/suggest.test.js b/test/util/suggest.test.js index 6262a6fcf..9eb463a67 100644 --- a/test/util/suggest.test.js +++ b/test/util/suggest.test.js @@ -1,16 +1,13 @@ -import { describe, it } from 'node:test'; -import assert from 'node:assert'; import { suggest } from '../../src/util/suggest.js'; import { stripColor } from '../helpers/strip-color.js'; +import assert from 'node:assert'; +import { describe, it } from 'node:test'; const cmds = ['build', 'clean', 'config', 'create', 'info']; describe('suggest', () => { it('should suggest a value', () => { - assert.strictEqual( - stripColor(suggest('buid', cmds)), - 'Did you mean this?\n build\n\n' - ); + assert.strictEqual(stripColor(suggest('buid', cmds)), 'Did you mean this?\n build\n\n'); }); it('should suggest multiple values', () => { @@ -33,9 +30,6 @@ describe('suggest', () => { }); it('should not find any suggestions', () => { - assert.strictEqual( - stripColor(suggest('zzz', cmds)), - '' - ); + assert.strictEqual(stripColor(suggest('zzz', cmds)), ''); }); }); diff --git a/test/util/ticonfig.test.js b/test/util/ticonfig.test.js index eba3429cb..1ec05d7be 100644 --- a/test/util/ticonfig.test.js +++ b/test/util/ticonfig.test.js @@ -1,10 +1,10 @@ -import { describe, it } from 'node:test'; -import assert from 'node:assert'; import { TiConfig } from '../../src/util/ticonfig.js'; +import assert from 'node:assert'; +import { copyFile, mkdir, readFile, rmdir, unlink } from 'node:fs/promises'; +import { tmpdir } from 'node:os'; import { join } from 'node:path'; +import { describe, it } from 'node:test'; import { fileURLToPath } from 'node:url'; -import { tmpdir } from 'node:os'; -import { copyFile, mkdir, readFile, rmdir, unlink } from 'node:fs/promises'; const fixturesDir = join(fileURLToPath(import.meta.url), '../fixtures/ticonfig'); @@ -20,7 +20,7 @@ describe('TiConfig', () => { }, { name: 'Error', - message: /^Unable to parse config file/ + message: /^Unable to parse config file/, } ); }); @@ -32,7 +32,7 @@ describe('TiConfig', () => { }, { name: 'Error', - message: /^Unable to open config file/ + message: /^Unable to open config file/, } ); }); @@ -60,7 +60,7 @@ describe('TiConfig', () => { falsey: 'false', undef: undefined, nil: 'null', - empty: '' + empty: '', }); assert.strictEqual(cfg.get('truthy'), true); assert.strictEqual(cfg.get('falsey'), false); @@ -117,10 +117,7 @@ describe('TiConfig', () => { it('should save the config', async () => { const tmpFile = join(tmpdir(), 'ticonfig.json'); - await copyFile( - join(fixturesDir, 'good.json'), - tmpFile - ); + await copyFile(join(fixturesDir, 'good.json'), tmpFile); try { const cfg = new TiConfig(tmpFile); @@ -153,7 +150,7 @@ describe('TiConfig', () => { }, { name: 'Error', - message: /Unable to write config file/ + message: /Unable to write config file/, } ); } finally { @@ -170,7 +167,7 @@ describe('TiConfig', () => { }, { name: 'Error', - message: /Unable to write config file/ + message: /Unable to write config file/, } ); }); diff --git a/test/util/tierror.test.js b/test/util/tierror.test.js index 8c47d6a8c..4b55624e7 100644 --- a/test/util/tierror.test.js +++ b/test/util/tierror.test.js @@ -1,6 +1,6 @@ -import { describe, it } from 'node:test'; -import assert from 'node:assert'; import { TiError } from '../../src/util/tierror.js'; +import assert from 'node:assert'; +import { describe, it } from 'node:test'; describe('TiError', () => { it('should support no meta info', () => { diff --git a/test/util/timodule.test.js b/test/util/timodule.test.js index 7d6906072..ef36850ee 100644 --- a/test/util/timodule.test.js +++ b/test/util/timodule.test.js @@ -1,11 +1,11 @@ -import { describe, it } from 'node:test'; -import assert from 'node:assert'; +import { TiConfig } from '../../src/util/ticonfig.js'; import { detect } from '../../src/util/timodule.js'; +import { tmpDirName } from '../helpers/tmp-dir-name.js'; +import fs from 'fs-extra'; +import assert from 'node:assert'; import { join } from 'node:path'; +import { describe, it } from 'node:test'; import { fileURLToPath } from 'node:url'; -import { TiConfig } from '../../src/util/ticonfig.js'; -import fs from 'fs-extra'; -import { tmpDirName } from '../helpers/tmp-dir-name.js'; const fixturesDir = join(fileURLToPath(import.meta.url), '../fixtures/timodule'); const goodConfig = join(fileURLToPath(import.meta.url), '../fixtures/ticonfig/good.json'); @@ -21,22 +21,16 @@ describe('timodule', () => { results = await detect({}); assert.deepStrictEqual(results, {}); - results = await detect( - { global: null }, - new TiConfig(goodConfig) - ); + results = await detect({ global: null }, new TiConfig(goodConfig)); assert.deepStrictEqual(results, { - global: {} + global: {}, }); }); it('should find nothing if search path is empty', async () => { - const results = await detect( - { empty: join(fixturesDir, 'empty') }, - new TiConfig(goodConfig) - ); + const results = await detect({ empty: join(fixturesDir, 'empty') }, new TiConfig(goodConfig)); assert.deepStrictEqual(results, { - empty: {} + empty: {}, }); }); @@ -45,10 +39,7 @@ describe('timodule', () => { try { await fs.copy(join(fixturesDir, 'modules'), tmpModulesDir); - const results = await detect( - { global: tmpModulesDir }, - new TiConfig(goodConfig) - ); + const results = await detect({ global: tmpModulesDir }, new TiConfig(goodConfig)); assert(Object.hasOwn(results, 'global')); assert(Object.hasOwn(results.global, 'android')); @@ -75,7 +66,7 @@ describe('timodule', () => { moduleid: 'com.test.module', guid: 'dcaea77e-2860-42c1-a57b-319f81da10e0', platform: 'android', - minsdk: '7.2.0' + minsdk: '7.2.0', }); assert(Object.hasOwn(results.global.commonjs, 'com.test.module')); @@ -96,7 +87,7 @@ describe('timodule', () => { moduleid: 'com.test.module', guid: 'dcaea77e-2860-42c1-a57b-319f81da10e0', platform: 'commonjs', - minsdk: '7.2.0' + minsdk: '7.2.0', }); assert(Object.hasOwn(mod, '1.0')); @@ -115,7 +106,7 @@ describe('timodule', () => { moduleid: 'com.test.module', guid: 'dcaea77e-2860-42c1-a57b-319f81da10e0', platform: 'commonjs', - minsdk: '7.2.0' + minsdk: '7.2.0', }); assert(Object.hasOwn(results.global.ios, 'com.test.module')); @@ -138,7 +129,7 @@ describe('timodule', () => { moduleid: 'com.test.module', guid: 'dcaea77e-2860-42c1-a57b-319f81da10e0', platform: 'ios', - minsdk: '7.2.0' + minsdk: '7.2.0', }); } finally { await fs.remove(tmpModulesDir); diff --git a/test/util/unique.test.js b/test/util/unique.test.js index 51768f8e2..75e4f41dd 100644 --- a/test/util/unique.test.js +++ b/test/util/unique.test.js @@ -1,6 +1,6 @@ -import { describe, it } from 'node:test'; -import assert from 'node:assert'; import { unique } from '../../src/util/unique.js'; +import assert from 'node:assert'; +import { describe, it } from 'node:test'; describe('unique', () => { it('should return empty array if no elements', () => { @@ -10,26 +10,8 @@ describe('unique', () => { it('should remove duplicates, null, and undefined values', () => { assert.deepStrictEqual( - unique([ - 1, - '1', - 'a', - true, - null, - undefined, - 1, - '1', - 'a', - true, - null, - undefined - ]), - [ - 1, - '1', - 'a', - true - ] + unique([1, '1', 'a', true, null, undefined, 1, '1', 'a', true, null, undefined]), + [1, '1', 'a', true] ); }); }); diff --git a/test/util/version.test.js b/test/util/version.test.js deleted file mode 100644 index 02f9b5432..000000000 --- a/test/util/version.test.js +++ /dev/null @@ -1,257 +0,0 @@ -import { describe, it } from 'node:test'; -import assert from 'node:assert'; -import * as version from '../../src/util/version.js'; - -describe('version', () => { - describe('compare', () => { - it('should compare two versions', () => { - assert(version.compare('1.0.0', '2.0.0') < 0); - assert(version.compare('2.0.0', '1.0.0') > 0); - assert.strictEqual(version.compare('1.0.0', '1.0.0'), 0); - - assert(version.compare('1.1.0', '1.2.0') < 0); - assert(version.compare('1.2.0', '1.1.0') > 0); - assert.strictEqual(version.compare('1.1.0', '1.1.0'), 0); - - assert(version.compare('1.1.1', '1.1.2') < 0); - assert(version.compare('1.1.2', '1.1.1') > 0); - assert.strictEqual(version.compare('1.1.1', '1.1.1'), 0); - - assert(version.compare('1.0.0.v20220102', '1.0.0.v20230809') < 0); - assert(version.compare('1.0.0.v20230809', '1.0.0.v20220102') > 0); - assert.strictEqual(version.compare('1.0.0.v20220102', '1.0.0.v20220102'), 0); - - assert(version.compare('1.0.0', '1.0.0.v20220102') < 0); - assert(version.compare('1.0.0.v20220102', '1.0.0') > 0); - }); - }); - - describe('format', () => { - it('should format a version', () => { - assert.strictEqual(version.format(0), '0'); - assert.strictEqual(version.format('1'), '1'); - assert.strictEqual(version.format('1-tag'), '1-tag'); - - assert.strictEqual(version.format('1', 1), '1'); - assert.strictEqual(version.format('1.2', 1), '1.2'); - assert.strictEqual(version.format('1.2-tag', 1), '1.2-tag'); - assert.strictEqual(version.format('1.2-tag', 1, 3, true), '1.2'); - - assert.strictEqual(version.format('1', 2), '1.0'); - assert.strictEqual(version.format('1.2', 2), '1.2'); - assert.strictEqual(version.format('1.2-tag', 2), '1.2-tag'); - assert.strictEqual(version.format('1.2-tag', 2, 3, true), '1.2'); - - assert.strictEqual(version.format('1.2.3', 3), '1.2.3'); - assert.strictEqual(version.format('1.2.3-tag', 3, 3), '1.2.3-tag'); - assert.strictEqual(version.format('1.2.3-tag', 3, 3, true), '1.2.3'); - - assert.strictEqual(version.format('1.2', 1, 2), '1.2'); - assert.strictEqual(version.format('1.2-tag', 1, 2), '1.2-tag'); - assert.strictEqual(version.format('1.2-tag', 1, 2, true), '1.2'); - - assert.strictEqual(version.format('1.2.3', 1, 2), '1.2'); - assert.strictEqual(version.format('1.2.3-tag', 1, 2), '1.2'); - assert.strictEqual(version.format('1.2.3-tag', 1, 2, true), '1.2'); - }); - }); - - describe('eq', () => { - it('should determine if two versions equal each other', () => { - assert.strictEqual(version.eq('1', '1'), true); - assert.strictEqual(version.eq('1.2', '1.2'), true); - assert.strictEqual(version.eq('1.2.3', '1.2.3'), true); - assert.strictEqual(version.eq('1.2.3.4', '1.2.3.4'), true); - assert.strictEqual(version.eq('1.2.3.4', '1.2.3.5'), true); - - assert.strictEqual(version.eq('1', '2'), false); - assert.strictEqual(version.eq('1.2', '2.2'), false); - assert.strictEqual(version.eq('1.2.3', '2.2.3'), false); - assert.strictEqual(version.eq('1.2.3.4', '2.2.3.4'), false); - assert.strictEqual(version.eq('1.2.3.4', '2.2.3.5'), false); - }); - }); - - describe('lt', () => { - it('should determine if a version is less than', () => { - assert.strictEqual(version.lt('1', '1'), false); - assert.strictEqual(version.lt('1.2', '1.2'), false); - assert.strictEqual(version.lt('1.2.3', '1.2.3'), false); - assert.strictEqual(version.lt('1.2.3.4', '1.2.3.4'), false); - assert.strictEqual(version.lt('1.2.3.4', '1.2.3.5'), false); - - assert.strictEqual(version.lt('1', '2'), true); - assert.strictEqual(version.lt('1.2', '1.3'), true); - assert.strictEqual(version.lt('1.2', '2.2'), true); - assert.strictEqual(version.lt('1.2.3', '2.2.3'), true); - assert.strictEqual(version.lt('1.2.3.4', '2.2.3.4'), true); - assert.strictEqual(version.lt('1.2.3.4', '2.2.3.5'), true); - }); - }); - - describe('lte', () => { - it('should determine if a version is less than or equal', () => { - assert.strictEqual(version.lte('1', '1'), true); - assert.strictEqual(version.lte('1.2', '1.2'), true); - assert.strictEqual(version.lte('1.2.3', '1.2.3'), true); - assert.strictEqual(version.lte('1.2.3.4', '1.2.3.4'), true); - assert.strictEqual(version.lte('1.2.3.4', '1.2.3.5'), true); - - assert.strictEqual(version.lte('1', '2'), true); - assert.strictEqual(version.lte('1.2', '1.3'), true); - assert.strictEqual(version.lte('1.2', '2.2'), true); - assert.strictEqual(version.lte('1.2.3', '2.2.3'), true); - assert.strictEqual(version.lte('1.2.3.4', '2.2.3.4'), true); - assert.strictEqual(version.lte('1.2.3.4', '2.2.3.5'), true); - - assert.strictEqual(version.lte('2', '1'), false); - assert.strictEqual(version.lte('2.2', '1.2'), false); - assert.strictEqual(version.lte('2.2.3', '1.2.3'), false); - assert.strictEqual(version.lte('2.2.3.4', '1.2.3.4'), false); - assert.strictEqual(version.lte('2.2.3.4', '1.2.3.5'), false); - }); - }); - - describe('gt', () => { - it('should determine if a version is greater than', () => { - assert.strictEqual(version.gt('1', '1'), false); - assert.strictEqual(version.gt('1.2', '1.2'), false); - assert.strictEqual(version.gt('1.2.3', '1.2.3'), false); - assert.strictEqual(version.gt('1.2.3.4', '1.2.3.4'), false); - assert.strictEqual(version.gt('1.2.3.4', '1.2.3.5'), false); - - assert.strictEqual(version.gt('1', '2'), false); - assert.strictEqual(version.gt('1.2', '1.3'), false); - assert.strictEqual(version.gt('1.2', '2.2'), false); - assert.strictEqual(version.gt('1.2.3', '2.2.3'), false); - assert.strictEqual(version.gt('1.2.3.4', '2.2.3.4'), false); - assert.strictEqual(version.gt('1.2.3.4', '2.2.3.5'), false); - - assert.strictEqual(version.gt('2', '1'), true); - assert.strictEqual(version.gt('2.2', '1.2'), true); - assert.strictEqual(version.gt('2.2.3', '1.2.3'), true); - assert.strictEqual(version.gt('2.2.3.4', '1.2.3.4'), true); - assert.strictEqual(version.gt('2.2.3.4', '1.2.3.5'), true); - }); - }); - - describe('gte', () => { - it('should determine if a version is greater than or equal', () => { - assert.strictEqual(version.gte('1', '1'), true); - assert.strictEqual(version.gte('1.2', '1.2'), true); - assert.strictEqual(version.gte('1.2.3', '1.2.3'), true); - assert.strictEqual(version.gte('1.2.3.4', '1.2.3.4'), true); - assert.strictEqual(version.gte('1.2.3.4', '1.2.3.5'), true); - - assert.strictEqual(version.gte('2', '1'), true); - assert.strictEqual(version.gte('1.3', '1.2'), true); - assert.strictEqual(version.gte('2.2', '1.2'), true); - assert.strictEqual(version.gte('2.2.3', '1.2.3'), true); - assert.strictEqual(version.gte('2.2.3.4', '1.2.3.4'), true); - assert.strictEqual(version.gte('2.2.3.4', '1.2.3.5'), true); - - assert.strictEqual(version.gte('1', '2'), false); - assert.strictEqual(version.gte('1.2', '2.2'), false); - assert.strictEqual(version.gte('2.2.3', '2.2.3'), true); - assert.strictEqual(version.gte('1.2.3.4', '2.2.3.4'), false); - assert.strictEqual(version.gte('1.2.3.4', '2.2.3.5'), false); - }); - }); - - describe('isValid', () => { - it('should determine if a version is valid', () => { - assert.strictEqual(version.isValid(1), '1.0.0'); - assert.strictEqual(version.isValid('1'), '1.0.0'); - assert.strictEqual(version.isValid('1.2'), '1.2.0'); - assert.strictEqual(version.isValid('1.2.3'), '1.2.3'); - assert.strictEqual(version.isValid('1.2.3.4'), '1.2.3'); - assert.strictEqual(version.isValid('1.2.3-tag'), '1.2.3-tag'); - - assert.strictEqual(version.isValid('a'), null); - }); - }); - - describe('parseMin', () => { - it('should parse the min version from a range', () => { - assert.strictEqual(version.parseMin('1'), '1'); - assert.strictEqual(version.parseMin('1.2'), '1.2'); - assert.strictEqual(version.parseMin('>=1.0'), '1.0'); - assert.strictEqual(version.parseMin('<1.0'), '1.0'); - assert.strictEqual(version.parseMin('>=2.3.3 <=4.2'), '2.3.3'); - assert.strictEqual(version.parseMin('>=2.3.3 <=4.2 || >=1.0'), '1.0'); - assert.strictEqual(version.parseMin('>=2.3.3 <=4.2 || 2.0'), '2.0'); - }); - }); - - describe('parseMax', () => { - it('should parse the max version from a range', () => { - assert.strictEqual(version.parseMax('1'), '1'); - assert.strictEqual(version.parseMax('1.2'), '1.2'); - assert.strictEqual(version.parseMax('>=1.0'), '1.0'); - assert.strictEqual(version.parseMax('<1.0'), '1.0'); - assert.strictEqual(version.parseMax('<18'), '<18'); - assert.strictEqual(version.parseMax('>=2.3.3 <=4.2'), '4.2'); - assert.strictEqual(version.parseMax('>=2.3.3 <=4.2.x'), '4.2'); - assert.strictEqual(version.parseMax('>=2.3.3 <=4.2.x', true), '4.2.x'); - assert.strictEqual(version.parseMax('>=2.3.3 <=4.2 || >=1.0'), '4.2'); - assert.strictEqual(version.parseMax('>=2.3.3 <=4.2 || 5.0'), '5.0'); - }); - }); - - describe('satisfies', () => { - it('in range', function () { - assert.strictEqual(version.satisfies('1.0.0', '1.0.0'), true); - assert.strictEqual(version.satisfies('1.0.0', '*'), true); - assert.strictEqual(version.satisfies('1.0.0', '>=2.0.0 || *'), true); - assert.strictEqual(version.satisfies('1.0.0', '>=1.0.0'), true); - assert.strictEqual(version.satisfies('3.0.0', '>=2.3.3 <=4.2'), true); - assert.strictEqual(version.satisfies('4', '>=2.3.3 <=4.2 || 5.0 || >=6.0'), true); - assert.strictEqual(version.satisfies('5', '>=2.3.3 <=4.2 || 5.0 || >=6.0'), true); - assert.strictEqual(version.satisfies('6', '>=2.3.3 <=4.2 || 5.0 || >=6.0'), true); - assert.strictEqual(version.satisfies('7', '>=2.3.3 <=4.2 || 5.0 || >=6.0'), true); - assert.strictEqual(version.satisfies('18.0.1', '<=18.x'), true); - assert.strictEqual(version.satisfies('18.0.1', '>=18.x'), true); - assert.strictEqual(version.satisfies('18.0.1', '>=19.x'), false); - }); - - it('not in range', function () { - assert.strictEqual(version.satisfies('2.0.0', '1.0.0'), false); - assert.strictEqual(version.satisfies('2.0.0', '>=2.3.3 <=4.2'), false); - assert.strictEqual(version.satisfies('2.3', '>=2.3.3 <=4.2'), false); - assert.strictEqual(version.satisfies('4.3', '>=2.3.3 <=4.2 || 5.0 || >=6.0'), false); - assert.strictEqual(version.satisfies('5.1', '>=2.3.3 <=4.2 || 5.0 || >=6.0'), false); - }); - - it('maybe', function () { - assert.strictEqual(version.satisfies('2.0', '1.0', true), 'maybe'); - assert.strictEqual(version.satisfies('2.0', '>=1.0', true), true); - assert.strictEqual(version.satisfies('2.0', '<1.0', true), 'maybe'); - assert.strictEqual(version.satisfies('2.0', '>=2.3.3 <=4.2', true), false); - assert.strictEqual(version.satisfies('5.0', '>=2.3.3 <=4.2', true), 'maybe'); - assert.strictEqual(version.satisfies('18', '>=10 <=18', true), true); - }); - }); - - describe('sort', () => { - it('should sort an array of versions', () => { - assert.deepStrictEqual( - version.sort([ - '1.2.3', - '1.0', - '0.5', - '10', - '1.2.3-tag', - '4.5.6.7' - ]), - [ - '0.5', - '1.0', - '1.2.3', - '1.2.3-tag', - '4.5.6.7', - '10' - ] - ); - }); - }); -}); From 838165b34f0b715eb96e771d3d05c17c4d173d0d Mon Sep 17 00:00:00 2001 From: Chris Barber Date: Thu, 5 Mar 2026 01:16:55 -0600 Subject: [PATCH 04/16] Remove fs-extra, wire up more node-titanium-sdk, remove unneeded code --- package.json | 1 - pnpm-lock.yaml | 33 --- src/cli.js | 20 +- src/commands/config.js | 2 +- src/commands/info.js | 49 +++- src/commands/module.js | 104 ++------ src/commands/sdk.js | 33 ++- src/util/busyindicator.js | 12 +- src/util/capitalize.js | 8 - src/util/detect.js | 12 +- src/util/expand.js | 20 -- src/util/jdk.js | 213 ---------------- src/util/setup-screens.js | 3 +- src/util/ticonfig.js | 33 ++- src/util/tihelp.js | 2 +- .../android/test-module/1.0.0/manifest | 0 .../commonjs/invalid-platform/1.0.0/manifest | 0 .../commonjs/invalid-version/1.0.0/manifest | 0 .../commonjs/invalid-version/1.0.1/manifest | 0 .../commonjs/invalid-version/1.0.2/manifest | 0 .../commonjs/test-module/1.0.0/manifest | 0 .../ios/test-module/1.0.0/manifest | 0 .../iphone/test-module/1.0.0/manifest | 0 .../windows/test-module/1.0.0/manifest | 0 test/commands/ti-info.test.js | 12 +- test/commands/ti-module.test.js | 234 ++++++++---------- test/commands/ti-sdk.test.js | 7 +- test/commands/ti.test.js | 4 +- test/helpers/init-cli.js | 4 +- test/helpers/init-home.js | 6 +- test/helpers/init-sdk-home.js | 13 +- test/util/capitalize.test.js | 11 - test/util/expand.test.js | 54 ---- test/util/suggest.test.js | 35 --- test/util/ticonfig.test.js | 4 +- test/util/timodule.test.js | 138 ----------- 36 files changed, 260 insertions(+), 807 deletions(-) delete mode 100644 src/util/capitalize.js delete mode 100644 src/util/expand.js delete mode 100644 src/util/jdk.js rename test/commands/fixtures/{module => modules}/android/test-module/1.0.0/manifest (100%) rename test/commands/fixtures/{module => modules}/commonjs/invalid-platform/1.0.0/manifest (100%) rename test/commands/fixtures/{module => modules}/commonjs/invalid-version/1.0.0/manifest (100%) rename test/commands/fixtures/{module => modules}/commonjs/invalid-version/1.0.1/manifest (100%) rename test/commands/fixtures/{module => modules}/commonjs/invalid-version/1.0.2/manifest (100%) rename test/commands/fixtures/{module => modules}/commonjs/test-module/1.0.0/manifest (100%) rename test/commands/fixtures/{module => modules}/ios/test-module/1.0.0/manifest (100%) rename test/commands/fixtures/{module => modules}/iphone/test-module/1.0.0/manifest (100%) rename test/commands/fixtures/{module => modules}/windows/test-module/1.0.0/manifest (100%) delete mode 100644 test/util/capitalize.test.js delete mode 100644 test/util/expand.test.js delete mode 100644 test/util/suggest.test.js delete mode 100644 test/util/timodule.test.js diff --git a/package.json b/package.json index 1b91ee6ce..e4f719bfc 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,6 @@ "chalk": "5.6.2", "commander": "14.0.3", "execa": "9.6.1", - "fs-extra": "11.3.4", "pretty-bytes": "7.1.0", "prompts": "2.4.2", "semver": "7.7.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d848408b9..81295da60 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,9 +20,6 @@ importers: execa: specifier: 9.6.1 version: 9.6.1 - fs-extra: - specifier: 11.3.4 - version: 11.3.4 pretty-bytes: specifier: 7.1.0 version: 7.1.0 @@ -961,10 +958,6 @@ packages: resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} engines: {node: '>=14'} - fs-extra@11.3.4: - resolution: {integrity: sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA==} - engines: {node: '>=14.14'} - fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -994,9 +987,6 @@ packages: resolution: {integrity: sha512-hjrNztw/VajQwOLsMNT1cbJiH2muO3OROCHnbehc8eY5JyD2gqz4AcMHPqgaOR59DjgUjYAYLeH699g/eWi2jw==} engines: {node: '>=18'} - graceful-fs@4.2.11: - resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - has-flag@3.0.0: resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} engines: {node: '>=4'} @@ -1070,9 +1060,6 @@ packages: engines: {node: '>=6'} hasBin: true - jsonfile@6.2.0: - resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==} - kleur@3.0.3: resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} engines: {node: '>=6'} @@ -1335,10 +1322,6 @@ packages: resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} engines: {node: '>=18'} - universalify@2.0.1: - resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} - engines: {node: '>= 10.0.0'} - update-browserslist-db@1.2.3: resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} hasBin: true @@ -2149,12 +2132,6 @@ snapshots: cross-spawn: 7.0.6 signal-exit: 4.1.0 - fs-extra@11.3.4: - dependencies: - graceful-fs: 4.2.11 - jsonfile: 6.2.0 - universalify: 2.0.1 - fsevents@2.3.3: optional: true @@ -2177,8 +2154,6 @@ snapshots: globals@17.4.0: {} - graceful-fs@4.2.11: {} - has-flag@3.0.0: {} has-flag@4.0.0: {} @@ -2232,12 +2207,6 @@ snapshots: json5@2.2.3: {} - jsonfile@6.2.0: - dependencies: - universalify: 2.0.1 - optionalDependencies: - graceful-fs: 4.2.11 - kleur@3.0.3: {} leven@2.1.0: {} @@ -2515,8 +2484,6 @@ snapshots: unicorn-magic@0.3.0: {} - universalify@2.0.1: {} - update-browserslist-db@1.2.3(browserslist@4.28.1): dependencies: browserslist: 4.28.1 diff --git a/src/cli.js b/src/cli.js index 942884716..4309d7ccb 100644 --- a/src/cli.js +++ b/src/cli.js @@ -9,7 +9,7 @@ import { initSDK, typeLabels } from './util/tisdk.js'; import { unique } from './util/unique.js'; import chalk from 'chalk'; import { program, Command, Option } from 'commander'; -import { capitalize, expand, isDir, version } from 'node-titanium-sdk/util'; +import { capitalize, expand, isDir, isFile, version } from 'node-titanium-sdk/util'; import { readdirSync, readFileSync } from 'node:fs'; import { readdir } from 'node:fs/promises'; import { basename, dirname, join } from 'node:path'; @@ -401,7 +401,11 @@ export class CLI { new Promise((resolve, reject) => { const hook = this.createHook(name, data); hook((err, result) => { - err ? reject(err) : resolve(result); + if (err) { + reject(err); + } else { + resolve(result); + } }); }) ), @@ -1067,10 +1071,10 @@ export class CLI { try { p = expand(p); - const isDir = isDir(p); - const files = isDir ? readdirSync(p) : [p]; + const dir = isDir(p); + const files = dir ? readdirSync(p) : [p]; for (const filename of files) { - const file = isDir ? join(p, filename) : filename; + const file = dir ? join(p, filename) : filename; if (!ignoreRE.test(filename) && isFile(file) && jsRE.test(file)) { const name = basename(file).replace(jsRE, ''); this.debugLogger.trace(`Found custom command "${name}"`); @@ -1082,7 +1086,7 @@ export class CLI { .action((...args) => this.executeCommand(args)); } } - } catch { + } catch (e) { // squelch } } @@ -1520,7 +1524,9 @@ export class CLI { opt.validated = true; if (opt.callback) { var val = opt.callback(this.argv[name] || ''); - val !== undefined && (this.argv[name] = val); + if (val !== undefined) { + this.argv[name] = val; + } delete opt.callback; } } diff --git a/src/commands/config.js b/src/commands/config.js index 3eeec6f65..0a4fedfc5 100644 --- a/src/commands/config.js +++ b/src/commands/config.js @@ -1,6 +1,6 @@ /* eslint-disable max-len */ -import { expand } from '../util/expand.js'; +import { expand } from 'node-titanium-sdk/util'; import { ticonfig } from '../util/ticonfig.js'; import { TiError } from '../util/tierror.js'; import chalk from 'chalk'; diff --git a/src/commands/info.js b/src/commands/info.js index 76047f004..b23abbb77 100644 --- a/src/commands/info.js +++ b/src/commands/info.js @@ -5,7 +5,15 @@ import { basename } from 'node:path'; import wrapAnsi from 'wrap-ansi'; const { bold, cyan, gray, magenta, red, yellow } = chalk; -const typesList = ['all', 'os', 'nodejs', 'titanium', 'jdk', 'android', 'ios']; +const typesList = ['all', 'os', 'nodejs', 'titanium', 'java', 'android', 'ios']; + +export const extendedDesc = `Display development environment information. + +You may specify one or more types to filter the information. +Available types: ${typesList.map((t) => cyan(t)).join(', ')} + +For example, to show Node.js and Java information only, run: + __titanium info nodejs java__`; /** * Returns the configuration for the info command. @@ -18,6 +26,13 @@ export function config(_logger, _config, _cli) { return { title: 'Info', skipBanner: true, + args: [ + { + desc: `one or more types to display ${gray('(default: "all")')}`, + name: 'types', + variadic: true, + }, + ], flags: { json: { desc: 'display info as JSON', @@ -32,9 +47,8 @@ export function config(_logger, _config, _cli) { }, types: { abbr: 't', - default: 'all', desc: 'information types to display; you may select one or more', - values: typesList.filter((t) => t !== 'ios' || process.platform === 'darwin'), + hidden: true, }, }, }; @@ -63,9 +77,15 @@ export async function run(logger, config, cli) { // determine the types to display const types = {}; let i = 0; - const specifiedTypes = (cli.argv.types || 'all').toLowerCase().split(','); + const specifiedTypes = cli.argv._[0]?.length ? cli.argv._[0] : (cli.argv.types || 'all').toLowerCase().split(','); for (let type of specifiedTypes) { type = type.trim(); + + // legacy support for "jdk" type + if (type === 'jdk') { + type = 'java'; + } + if (typesList.includes(type)) { types[type] = ++i; } @@ -156,18 +176,20 @@ export async function run(logger, config, cli) { ); } - if (types.all || types.jdk) { + if (types.all || types.java) { sections.push( new Section({ - name: 'jdk', - title: 'Java Development Kit', + name: 'java', + title: 'Java', render() { logger.log(bold(this.title)); - if (data.jdk.version) { - logger.log( - ` ${'Version'.padEnd(indent)} = ${magenta(`${data.jdk.version}_${data.jdk.build}`)}` - ); - logger.log(` ${'Java Home'.padEnd(indent)} = ${magenta(data.jdk.home)}\n`); + logger.log(` ${'JAVA_HOME'.padEnd(indent)} = ${magenta(data.java.home || 'not set')}`); + if (Object.keys(data.java.jdks).length) { + for (const { path, version} of Object.values(data.java.jdks)) { + logger.log(` ${cyan(version)}`); + logger.log(` ${' Path'.padEnd(indent)} = ${magenta(path)}`); + } + logger.log(); } else { logger.log(` ${gray('Not found')}\n`); } @@ -231,7 +253,8 @@ export async function run(logger, config, cli) { logger.log(bold(`${section.title} Issues`)); for (const issue of info.issues) { - const msg = issue.message + const msg = (issue.details || issue.message) + .trim() .split('\n\n') .map((chunk) => { return ( diff --git a/src/commands/module.js b/src/commands/module.js index a1d531879..3688562a0 100644 --- a/src/commands/module.js +++ b/src/commands/module.js @@ -1,13 +1,11 @@ import { arrayify } from '../util/arrayify.js'; -import { capitalize } from '../util/capitalize.js'; -import { expand } from '../util/expand.js'; +import { capitalize, expand, isDir, isFile } from 'node-titanium-sdk/util'; import { TiError } from '../util/tierror.js'; import chalk from 'chalk'; import { detectTiModules } from 'node-titanium-sdk/titanium'; -import { existsSync } from 'node:fs'; import { dirname, join, parse } from 'node:path'; -const { bold, cyan, gray } = chalk; +const { cyan, gray, magenta } = chalk; // if a platform is not in this map, then we just print the capitalized platform name const platformNames = { @@ -100,106 +98,50 @@ ModuleSubcommands.list = { }, async fn(logger, config, cli) { const isJson = cli.argv.json || cli.argv.output === 'json'; - let projectDir; let p = expand(cli.argv['project-dir'] || '.'); - const searchPaths = { - project: [], - config: [], - global: [], - }; - const scopeLabels = { - project: 'Project Modules', - config: 'Configured Path Modules', - global: 'Global Modules', - }; - const confPaths = arrayify(config.get('paths.modules'), true); - const defaultInstallLocation = cli.env.installPath; - const sdkLocations = cli.env.os.sdkPaths.map((p) => expand(p)); + const searchPaths = arrayify(config.get('paths.modules'), true); // attempt to detect if we're in a project folder by scanning for a tiapp.xml // until we hit the root - if (existsSync(p)) { + if (isDir(p)) { for (const { root } = parse(p); p !== root; p = dirname(p)) { - if (existsSync(join(p, 'tiapp.xml'))) { - p = join(p, 'modules'); - projectDir = p; - if (existsSync(p)) { - searchPaths.project.push(p); - } + if (isFile(join(p, 'tiapp.xml'))) { + searchPaths.push(p); break; } } } - // set our paths from the config file - for (let path of confPaths) { - path = expand(path); - if ( - existsSync(path) && - !searchPaths.project.includes(path) && - !searchPaths.config.includes(path) - ) { - searchPaths.config.push(path); - } - } - - // add any modules from various SDK locations - if (defaultInstallLocation && !sdkLocations.includes(defaultInstallLocation)) { - sdkLocations.push(defaultInstallLocation); - } - if (cli.sdk?.path) { - sdkLocations.push(expand(cli.sdk.path, '..', '..', '..')); - } - for (let path of sdkLocations) { - path = expand(path, 'modules'); - if ( - existsSync(path) && - !searchPaths.project.includes(path) && - !searchPaths.config.includes(path) && - !searchPaths.global.includes(path) - ) { - searchPaths.global.push(path); - } - } - - const results = await detect(searchPaths, config, cli.debugLogger); + const { modules } = await detectTiModules({ + searchPaths + }); if (isJson) { - logger.log(JSON.stringify(results, null, '\t')); + logger.log(JSON.stringify(modules, null, '\t')); return; } logger.skipBanner(false); logger.banner(); - for (const [scope, modules] of Object.entries(results)) { - const platforms = Object.keys(modules); - if (scope === 'project' && projectDir === undefined && !platforms.length) { - // no sense printing project modules if there aren't any and the - // user never asked to see them - continue; - } - - logger.log(bold(scopeLabels[scope])); - if (platforms.length) { - let i = 0; - for (const platform of platforms) { - if (i++) { - logger.log(); - } - + if (Object.keys(modules).length) { + for (const [name, platforms] of Object.entries(modules)) { + logger.log(name); + for (const [platform, versions] of Object.entries(platforms)) { const platformName = platformNames[platform.toLowerCase()] || capitalize(platform); logger.log(` ${gray(platformName)}`); - for (const [name, versions] of Object.entries(modules[platform])) { - logger.log(` ${name}`); - for (const [ver, mod] of Object.entries(versions)) { - logger.log(` ${cyan(ver.padEnd(7))} ${mod.modulePath}`); - } + for (const [version, mod] of Object.entries(versions)) { + logger.log(` ${cyan(version)}`); + logger.log(` Path = ${magenta(mod.path)}`); + logger.log(` Author = ${magenta(mod.author || '')}`); + logger.log(` Description = ${magenta(mod.description || '')}`); + logger.log(` Titanium SDK = ${magenta(mod.minsdk ? `>=${mod.minsdk}` : 'any')}`); + logger.log(); } } - } else { - logger.log(gray(' No modules found')); } + } else { + logger.log(gray('No modules found')); logger.log(); } }, diff --git a/src/commands/sdk.js b/src/commands/sdk.js index 9369f51bd..97d23e053 100644 --- a/src/commands/sdk.js +++ b/src/commands/sdk.js @@ -19,7 +19,7 @@ import { version, } from 'node-titanium-sdk/util'; import { createWriteStream } from 'node:fs'; -import { mkdir, readFile, rename, writeFile } from 'node:fs/promises'; +import { mkdir, readdir, readFile, rename, rm, writeFile } from 'node:fs/promises'; import os from 'node:os'; import { basename, dirname, join } from 'node:path'; import { Transform } from 'node:stream'; @@ -432,6 +432,7 @@ SdkSubcommands.install = { logger.log(`\nInstalling SDK files to ${cyan(dest)}`); await mkdir(dest, { recursive: true }); await rm(dest, { force: true, recursive: true }); + await mkdir(dest, { recursive: true }); await rename(src, dest); // step 5: install the modules @@ -460,7 +461,7 @@ SdkSubcommands.install = { } const destDir = join(modulesDest, platform, moduleName, ver); - if (!forceModules && exists(destDir)) { + if (!forceModules && await exists(destDir)) { trace(`Module ${cyan(`${moduleName}@${ver}`)} already installed`); continue; } @@ -473,9 +474,11 @@ SdkSubcommands.install = { if (Object.keys(modules).length) { trace(`Installing ${cyan(Object.keys(modules).length)} modules:`); + console.log(modules); for (const [name, { src, dest }] of Object.entries(modules)) { trace(` ${cyan(name)}`); await rm(dest, { force: true, recursive: true }); + await mkdir(dest, { recursive: true }); await rename(src, dest); } } else { @@ -499,14 +502,14 @@ async function getInstallFile({ branch, config, logger, osName, showProgress, su if (uriMatch && uriMatch[2]) { file = uriMatch[2]; - } else if (subject && exists(subject)) { + } else if (subject && await exists(subject)) { file = subject; } if (file) { file = expand(file); - if (!exists(file)) { + if (!(await exists(file))) { throw new TiError('Specified file does not exist'); } @@ -653,6 +656,7 @@ async function getInstallFile({ branch, config, logger, osName, showProgress, su if (filename) { file = join(downloadDir, filename); await rm(file, { force: true }); + await mkdir(dirname(file), { recursive: true }); await rename(downloadedFile, file); downloadedFile = file; } else { @@ -681,15 +685,15 @@ async function extractSDK({ let renameTo; let forceModules = force; - const onEntry = async (filename, _idx, total) => { + const onEntry = async (entry, _idx, total) => { if (total > 1) { - const m = !name && filename.match(sdkDestRegExp); + const m = !name && entry.fileName.match(sdkDestRegExp); if (m) { name = m[1]; const result = await checkSDKFile({ force, logger, - filename, + filename: entry.fileName, name, noPrompt, osName, @@ -713,31 +717,24 @@ async function extractSDK({ bar?.tick(); } else { - artifact = filename; + artifact = entry.fileName; } }; debugLogger.trace(`Extracting ${file} -> ${tempDir}`); - await extractZip({ - dest: tempDir, - file, - onEntry, - }); + await extractZip(file, tempDir, { onEntry }); if (!artifact) { return { forceModules, name, renameTo, tempDir }; } + // GitHub artifacts are zips containing the SDK zip file debugLogger.trace(`Detected artifact: ${artifact}`); const tempDir2 = join(os.tmpdir(), `titanium-cli-${Math.floor(Math.random() * 1e6)}`); file = join(tempDir, artifact); debugLogger.trace(`Extracting ${file} -> ${tempDir2}`); - await extractZip({ - dest: tempDir2, - file, - onEntry, - }); + await extractZip(file, tempDir2, { onEntry }); await rm(tempDir, { force: true, recursive: true }); return { forceModules, name, renameTo, tempDir: tempDir2 }; diff --git a/src/util/busyindicator.js b/src/util/busyindicator.js index ef99c2192..3615e0ca5 100644 --- a/src/util/busyindicator.js +++ b/src/util/busyindicator.js @@ -10,7 +10,9 @@ export class BusyIndicator { } render() { - this.stream.cursorTo && this.stream.cursorTo(0); + if (this.stream.cursorTo) { + this.stream.cursorTo(0); + } this.stream.write(this.margin + this.sprites[this.current++]); if (this.current >= this.sprites.length) { this.current = 0; @@ -35,9 +37,13 @@ export class BusyIndicator { clearTimeout(this.timer); if (this.running) { this.running = false; - this.stream.cursorTo && this.stream.cursorTo(0); + if (this.stream.cursorTo) { + this.stream.cursorTo(0); + } this.stream.write(' '.repeat(this.margin.length + 2)); - this.stream.cursorTo && this.stream.cursorTo(0); + if (this.stream.cursorTo) { + this.stream.cursorTo(0); + } } } } diff --git a/src/util/capitalize.js b/src/util/capitalize.js deleted file mode 100644 index fc53d4260..000000000 --- a/src/util/capitalize.js +++ /dev/null @@ -1,8 +0,0 @@ -/** - * Capitalizes the specified string. Only the first character is uppercased. - * @param {String} s - The string to capitalize - * @returns {String} The capitalized string - */ -export function capitalize(s) { - return s.substring(0, 1).toUpperCase() + s.substring(1); -} diff --git a/src/util/detect.js b/src/util/detect.js index be03d7aff..e025470fa 100644 --- a/src/util/detect.js +++ b/src/util/detect.js @@ -1,4 +1,4 @@ -import { detect as jdkInfo } from './jdk.js'; +import { detectJDKs } from 'node-titanium-sdk/jdk'; import chalk from 'chalk'; import { detectTitaniumSDKs } from 'node-titanium-sdk/titanium'; import { existsSync } from 'node:fs'; @@ -10,13 +10,15 @@ import { pathToFileURL } from 'node:url'; const { cyan } = chalk; export async function detect(logger, config, cli, types = { all: true }) { - const [os, node, npm, titanium, titaniumCLI, jdk, ...platformData] = await Promise.all([ + const [os, node, npm, titanium, titaniumCLI, java, ...platformData] = await Promise.all([ (types.all || types.os) && osInfo(), (types.all || types.nodejs) && nodeInfo(), (types.all || types.nodejs) && npmInfo(), (types.all || types.titanium) && titaniumSDKInfo(config), (types.all || types.titanium) && titaniumCLIInfo(cli), - (types.all || types.jdk) && jdkInfo(config), + (types.all || types.java) && detectJDKs({ + javaHome: config.get('java.home'), + }), ...Object.keys(cli.sdk?.platforms || {}) .sort() .map(async (name) => { @@ -33,7 +35,7 @@ export async function detect(logger, config, cli, types = { all: true }) { npm, titanium, titaniumCLI, - jdk, + java, }; const platformInfo = []; @@ -162,7 +164,7 @@ async function titaniumSDKInfo(config) { const { sdks } = await detectTitaniumSDKs(config); const results = {}; - for (const sdk of sdks) { + for (const sdk of Object.values(sdks)) { results[sdk.name] = { version: sdk.version, path: sdk.path, diff --git a/src/util/expand.js b/src/util/expand.js deleted file mode 100644 index d8416b57e..000000000 --- a/src/util/expand.js +++ /dev/null @@ -1,20 +0,0 @@ -import { join, resolve } from 'node:path'; - -const homeDirRegExp = /^~([\\|/].*)?$/; -const winRegExp = /^win/; -const winEnvVarRegExp = /(%([^%]*)%)/g; - -export function expand(...segments) { - segments[0] = segments[0].replace( - homeDirRegExp, - (process.env.HOME || process.env.USERPROFILE) + '$1' - ); - if (winRegExp.test(process.platform)) { - return resolve( - join(...segments).replace(winEnvVarRegExp, (_s, m, n) => { - return process.env[n] || m; - }) - ); - } - return resolve(...segments); -} diff --git a/src/util/jdk.js b/src/util/jdk.js deleted file mode 100644 index ae0465057..000000000 --- a/src/util/jdk.js +++ /dev/null @@ -1,213 +0,0 @@ -import { expand } from './expand.js'; -import { existsSync } from 'node:fs'; -import { readdir, realpath } from 'node:fs/promises'; -import { dirname, join, resolve } from 'node:path'; -import which from 'which'; - -const exe = process.platform === 'win32' ? '.exe' : ''; - -/** - * Detects if Java and the JDK are installed. - * @param {Object} [config] - The CLI configuration - * @returns {Promise} - */ -export async function detect(config) { - let javaHome = config.get('java.home', process.env.JAVA_HOME) || null; - const jdkPaths = []; - const requiredTools = ['java', 'javac', 'keytool']; - const executables = {}; - const results = { - jdks: {}, - home: null, - version: null, - build: null, - executables: executables, - issues: [], - }; - const { $ } = await import('execa'); - - // sanity check the Java home - if (javaHome) { - javaHome = expand(javaHome); - if (existsSync(javaHome) && isJDK(javaHome)) { - jdkPaths.push(javaHome); - } - results.home = javaHome; - } - - if (process.platform === 'linux') { - try { - let p = await which('javac'); - p = dirname(dirname(p)); - if (!jdkPaths.includes(p) && isJDK(p)) { - jdkPaths.push(p); - } - } catch {} - } else if (process.platform === 'darwin') { - try { - const { stdout } = await $`/usr/libexec/java_home`; - const p = stdout.trim(); - if (p && !jdkPaths.includes(p) && isJDK(p)) { - jdkPaths.push(p); - } - } catch {} - - try { - let p = await which('javac'); - p = await realpath(dirname(dirname(p))); - if (!jdkPaths.includes(p) && isJDK(p)) { - jdkPaths.push(p); - } - } catch {} - - const dirs = ['/Library/Java/JavaVirtualMachines', '/System/Library/Java/JavaVirtualMachines']; - for (const jvmPath of dirs) { - if (existsSync(jvmPath)) { - for (const name of await readdir(jvmPath)) { - const p = join(jvmPath, name, 'Contents', 'Home'); - if (!jdkPaths.includes(p) && isJDK(p)) { - jdkPaths.push(p); - } - } - } - } - } else if (process.platform === 'win32') { - const dirs = ['%SystemDrive%', '%ProgramFiles%', '%ProgramFiles(x86)%', '%ProgramW6432%', '~']; - for (let dir of dirs) { - dir = expand(dir); - if (existsSync(dir)) { - for (const name of await readdir(dir)) { - const subdir = join(dir, name); - if (isJDK(subdir) && !jdkPaths.includes(subdir)) { - jdkPaths.push(subdir); - } - } - } - } - } - - await Promise.all( - jdkPaths.map(async (home) => { - const jdk = { - home: home, - version: null, - build: null, - executables: {}, - }; - const missingTools = []; - - for (const cmd of requiredTools) { - const p = join(home, `bin/${cmd}${exe}`); - if (existsSync(p)) { - jdk.executables[cmd] = await realpath(p); - } else { - missingTools.push(cmd); - } - } - - if (missingTools.length) { - results.issues.push({ - id: 'JDK_MISSING_PROGRAMS', - type: 'warning', - message: `JDK (Java Development Kit) at ${home} missing required programs: __${missingTools.join(', ')}__ -${ - process.env.JAVA_HOME - ? `Please verify your __JAVA_HOME__ environment variable is correctly set to the JDK install location\n__JAVA_HOME__ is currently set to "${process.env.JAVA_HOME}".` - : 'Please set the __JAVA_HOME__ environment variable to the JDK install location and not the JRE (Java Runtime Environment).' -} -The __JAVA_HOME__ environment variable must point to the JDK and not the JRE (Java Runtime Environment). -You may want to reinstall the JDK by downloading it from __https://www.oracle.com/java/technologies/downloads/__ -or __https://jdk.java.net/archive/__.`, - }); - return; - } - - let arch = '32bit'; - let result; - try { - result = await $`${jdk.executables.javac} -version -d64`; - arch = '64bit'; - } catch { - result = await $`${jdk.executables.javac} -version`; - } - - const re = /^javac (.+?)(?:_(.+))?$/; - let m = result?.stderr?.trim().match(re) || result?.stdout?.trim().match(re); - if (m) { - let name = m[1]; - - jdk.architecture = arch; - jdk.version = m[1]; - jdk.build = m[2]; - - if (jdk.build) { - name += `_${jdk.build}`; - } else { - const { stderr } = await $`${jdk.executables.java} -version`; - m = stderr.trim().match(/\(build .+?\+(\d+(-[-a-zA-Z0-9.]+)?)\)/); - if (m) { - jdk.build = m[1]; - name += `_${m[1]}`; - } - } - - results.jdks[name] = jdk; - - if (results.version === null) { - Object.assign(results, jdk); - } - } - }) - ); - - if (results.version === null) { - results.issues.push({ - id: 'JDK_NOT_INSTALLED', - type: 'error', - message: `JDK (Java Development Kit) not installed. -If you already have installed the JDK, verify your __JAVA_HOME__ environment variable is correctly set. -The JDK is required for Titanium and must be manually downloaded and installed from __https://www.oracle.com/java/technologies/downloads/__ -or __https://jdk.java.net/archive/__`, - }); - } - - return results; -} - -function isJDK(dir) { - if (!existsSync(join(dir, `bin/javac${exe}`))) { - return; - } - - // try to find the jvm lib - let libjvmLocations = []; - - if (process.platform === 'linux') { - if (process.arch === 'x64') { - libjvmLocations = [ - 'lib/amd64/client/libjvm.so', - 'lib/amd64/server/libjvm.so', - 'jre/lib/amd64/client/libjvm.so', - 'jre/lib/amd64/server/libjvm.so', - 'lib/server/libjvm.so', - ]; - } else { - libjvmLocations = [ - 'lib/i386/client/libjvm.so', - 'lib/i386/server/libjvm.so', - 'jre/lib/i386/client/libjvm.so', - 'jre/lib/i386/server/libjvm.so', - ]; - } - } else if (process.platform === 'darwin') { - libjvmLocations = [ - 'jre/lib/server/libjvm.dylib', - '../Libraries/libjvm.dylib', - 'lib/server/libjvm.dylib', - ]; - } else if (process.platform === 'win32') { - libjvmLocations = ['jre/bin/server/jvm.dll', 'jre/bin/client/jvm.dll', 'bin/server/jvm.dll']; - } - - return libjvmLocations.some((p) => existsSync(resolve(dir, p))); -} diff --git a/src/util/setup-screens.js b/src/util/setup-screens.js index a459d24ff..4a3f51259 100644 --- a/src/util/setup-screens.js +++ b/src/util/setup-screens.js @@ -1,11 +1,10 @@ import { BusyIndicator } from './busyindicator.js'; import { detect } from './detect.js'; -import { expand } from './expand.js'; import { prompt } from './prompt.js'; import { detect as proxyDetect } from './proxy.js'; import chalk from 'chalk'; import { detectTitaniumSDKs, getTitaniumReleases } from 'node-titanium-sdk/titanium'; -import { request, version } from 'node-titanium-sdk/util'; +import { expand, request, version } from 'node-titanium-sdk/util'; import dns from 'node:dns/promises'; import { existsSync, unlinkSync, utimesSync, writeFileSync } from 'node:fs'; import os from 'node:os'; diff --git a/src/util/ticonfig.js b/src/util/ticonfig.js index 26cb5cef6..669e0a89c 100644 --- a/src/util/ticonfig.js +++ b/src/util/ticonfig.js @@ -1,7 +1,7 @@ -import { expand } from './expand.js'; +import { existsSync, expand, isFile } from 'node-titanium-sdk/util'; import { TiError } from './tierror.js'; -import fs from 'fs-extra'; import { join } from 'node:path'; +import { mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs'; export class TiConfig { #configFile = ''; @@ -122,7 +122,7 @@ export class TiConfig { load(file) { if (file) { file = expand(file); - if (!fs.existsSync(file)) { + if (!existsSync(file)) { throw new Error(`Unable to open config file "${file}"`); } } else { @@ -133,9 +133,9 @@ export class TiConfig { this.apply(this.#defaults); // if the config file exists, then we load it - if (fs.existsSync(file)) { + if (isFile(file)) { try { - this.apply(fs.readJsonSync(file)); + this.apply(JSON.parse(readFileSync(file, 'utf8'))); this.#configFile = file; } catch { throw new Error(`Unable to parse config file "${file}"`); @@ -152,11 +152,11 @@ export class TiConfig { */ save() { try { - fs.ensureDirSync(this.#titaniumConfigFolder); + mkdirSync(this.#titaniumConfigFolder, { recursive: true }); const tmpFile = `${this.#configFile}.${Date.now()}.tmp`; - fs.writeFileSync(tmpFile, JSON.stringify(this, null, 2)); - fs.renameSync(tmpFile, this.#configFile); + writeFileSync(tmpFile, JSON.stringify(this, null, 2)); + renameSync(tmpFile, this.#configFile); } catch { throw new TiError(`Unable to write config file ${this.#configFile}`, { after: 'Please ensure the Titanium CLI has access to modify this file', @@ -186,11 +186,18 @@ export class TiConfig { // if not an array, try to cast to null, true, false, int or leave as string if (!Array.isArray(value)) { value = value === undefined ? '' : String(value).trim(); - value === 'null' && (value = null); - value === 'true' && (value = true); - value === 'false' && (value = false); - if (String(~~value) === value) { - value = ~~value; + if (value === 'null') { + value = null; + } + if (value === 'true') { + value = true; + } + if (value === 'false') { + value = false; + } + const num = Number.parseInt(value); + if (!isNaN(num) && String(num) === value) { + value = num; } } diff --git a/src/util/tihelp.js b/src/util/tihelp.js index 86e8ec35d..4daec74e5 100644 --- a/src/util/tihelp.js +++ b/src/util/tihelp.js @@ -1,5 +1,5 @@ import { applyCommandConfig } from './apply-command-config.js'; -import { capitalize } from './capitalize.js'; +import { capitalize } from 'node-titanium-sdk/util'; import chalk from 'chalk'; import { Command, Help } from 'commander'; diff --git a/test/commands/fixtures/module/android/test-module/1.0.0/manifest b/test/commands/fixtures/modules/android/test-module/1.0.0/manifest similarity index 100% rename from test/commands/fixtures/module/android/test-module/1.0.0/manifest rename to test/commands/fixtures/modules/android/test-module/1.0.0/manifest diff --git a/test/commands/fixtures/module/commonjs/invalid-platform/1.0.0/manifest b/test/commands/fixtures/modules/commonjs/invalid-platform/1.0.0/manifest similarity index 100% rename from test/commands/fixtures/module/commonjs/invalid-platform/1.0.0/manifest rename to test/commands/fixtures/modules/commonjs/invalid-platform/1.0.0/manifest diff --git a/test/commands/fixtures/module/commonjs/invalid-version/1.0.0/manifest b/test/commands/fixtures/modules/commonjs/invalid-version/1.0.0/manifest similarity index 100% rename from test/commands/fixtures/module/commonjs/invalid-version/1.0.0/manifest rename to test/commands/fixtures/modules/commonjs/invalid-version/1.0.0/manifest diff --git a/test/commands/fixtures/module/commonjs/invalid-version/1.0.1/manifest b/test/commands/fixtures/modules/commonjs/invalid-version/1.0.1/manifest similarity index 100% rename from test/commands/fixtures/module/commonjs/invalid-version/1.0.1/manifest rename to test/commands/fixtures/modules/commonjs/invalid-version/1.0.1/manifest diff --git a/test/commands/fixtures/module/commonjs/invalid-version/1.0.2/manifest b/test/commands/fixtures/modules/commonjs/invalid-version/1.0.2/manifest similarity index 100% rename from test/commands/fixtures/module/commonjs/invalid-version/1.0.2/manifest rename to test/commands/fixtures/modules/commonjs/invalid-version/1.0.2/manifest diff --git a/test/commands/fixtures/module/commonjs/test-module/1.0.0/manifest b/test/commands/fixtures/modules/commonjs/test-module/1.0.0/manifest similarity index 100% rename from test/commands/fixtures/module/commonjs/test-module/1.0.0/manifest rename to test/commands/fixtures/modules/commonjs/test-module/1.0.0/manifest diff --git a/test/commands/fixtures/module/ios/test-module/1.0.0/manifest b/test/commands/fixtures/modules/ios/test-module/1.0.0/manifest similarity index 100% rename from test/commands/fixtures/module/ios/test-module/1.0.0/manifest rename to test/commands/fixtures/modules/ios/test-module/1.0.0/manifest diff --git a/test/commands/fixtures/module/iphone/test-module/1.0.0/manifest b/test/commands/fixtures/modules/iphone/test-module/1.0.0/manifest similarity index 100% rename from test/commands/fixtures/module/iphone/test-module/1.0.0/manifest rename to test/commands/fixtures/modules/iphone/test-module/1.0.0/manifest diff --git a/test/commands/fixtures/module/windows/test-module/1.0.0/manifest b/test/commands/fixtures/modules/windows/test-module/1.0.0/manifest similarity index 100% rename from test/commands/fixtures/module/windows/test-module/1.0.0/manifest rename to test/commands/fixtures/modules/windows/test-module/1.0.0/manifest diff --git a/test/commands/ti-info.test.js b/test/commands/ti-info.test.js index 1643d194c..ea6f6d5c0 100644 --- a/test/commands/ti-info.test.js +++ b/test/commands/ti-info.test.js @@ -1,13 +1,13 @@ import { initCLI } from '../helpers/init-cli.js'; import { stripColor } from '../helpers/strip-color.js'; -import fs from 'fs-extra'; import assert from 'node:assert'; +import { readFileSync } from 'node:fs'; import { dirname, join } from 'node:path'; import { describe, it } from 'node:test'; import { fileURLToPath } from 'node:url'; const __dirname = dirname(fileURLToPath(import.meta.url)); -const pkgJson = fs.readJsonSync(join(__dirname, '../../package.json')); +const pkgJson = JSON.parse(readFileSync(join(__dirname, '../../package.json'), 'utf8')); describe('ti info', () => { it( @@ -39,7 +39,7 @@ describe('ti info', () => { ); assert.match(output, new RegExp(`Titanium CLI\n\\s*CLI Version\\s*= ${pkgJson.version}`)); assert.match(output, /Titanium SDKs/); - assert.match(output, /Java Development Kit/); + assert.match(output, /Java/); assert.match(output, /Issues/); assert.strictEqual(exitCode, 0); @@ -58,7 +58,7 @@ describe('ti info', () => { assert(Object.hasOwn(json, 'npm')); assert(Object.hasOwn(json, 'titanium')); assert(Object.hasOwn(json, 'titaniumCLI')); - assert(Object.hasOwn(json, 'jdk')); + assert(Object.hasOwn(json, 'java')); // legacy ({ exitCode, stdout } = await run(['info', '--output', 'json'])); @@ -69,7 +69,7 @@ describe('ti info', () => { assert(Object.hasOwn(json, 'npm')); assert(Object.hasOwn(json, 'titanium')); assert(Object.hasOwn(json, 'titaniumCLI')); - assert(Object.hasOwn(json, 'jdk')); + assert(Object.hasOwn(json, 'java')); assert.strictEqual(exitCode, 0); }), @@ -202,7 +202,7 @@ describe('ti info', () => { new RegExp(`Titanium CLI\n\\s*CLI Version\\s*= ${pkgJson.version}`) ); assert.doesNotMatch(output, /Titanium SDKs/); - assert.match(output, /Java Development Kit/); + assert.match(output, /Java/); assert.match(output, /Issues/); assert.strictEqual(exitCode, 0); diff --git a/test/commands/ti-module.test.js b/test/commands/ti-module.test.js index 7843b63d5..090d0febe 100644 --- a/test/commands/ti-module.test.js +++ b/test/commands/ti-module.test.js @@ -45,8 +45,7 @@ describe('ti module', () => { const { exitCode, stdout } = await run(['module']); const output = stripColor(stdout); - assert.match(output, /Configured Path Modules/); - assert.match(output, /Global Modules/); + assert.match(output, /No modules found/); assert.strictEqual(exitCode, 0); }) @@ -58,12 +57,7 @@ describe('ti module', () => { const { exitCode, stdout } = await run(['module', '--json']); const json = JSON.parse(stdout); - assert.deepStrictEqual(json, { - project: {}, - config: {}, - global: {}, - }); - + assert.deepStrictEqual(json, {}); assert.strictEqual(exitCode, 0); }) ); @@ -76,7 +70,7 @@ describe('ti module', () => { '--config', JSON.stringify({ paths: { - modules: [join(fixturesDir, 'module')], + modules: [join(fixturesDir, 'modules')], }, }), ]); @@ -84,26 +78,40 @@ describe('ti module', () => { const output = stripColor(stdout); assert.match( output, - new RegExp(`Configured Path Modules + new RegExp(`com.test.module Android - com.test.module - 1.0.0 ${join(fixturesDir, 'module', 'android', 'test-module', '1.0.0').replace(/\\/g, '\\\\')} + 1.0.0 + Path = ${join(fixturesDir, 'modules', 'android', 'test-module', '1.0.0').replace(/\\/g, '\\\\')} + Author = Your Name + Description = testModule + Titanium SDK = >=7.2.0 CommonJS - com.test.module - 1.0 ${join(fixturesDir, 'module', 'commonjs', 'invalid-version', '1.0.1').replace(/\\/g, '\\\\')} - 1.0.0 ${join(fixturesDir, 'module', 'commonjs', 'test-module', '1.0.0').replace(/\\/g, '\\\\')} + 1.0 + Path = ${join(fixturesDir, 'modules', 'commonjs', 'invalid-version', '1.0.1').replace(/\\/g, '\\\\')} + Author = Your Name + Description = testModule + Titanium SDK = >=7.2.0 + + 1.0.0 + Path = ${join(fixturesDir, 'modules', 'commonjs', 'test-module', '1.0.0').replace(/\\/g, '\\\\')} + Author = Your Name + Description = testModule + Titanium SDK = >=7.2.0 iOS - com.test.module - 1.0.0 ${join(fixturesDir, 'module', 'iphone', 'test-module', '1.0.0').replace(/\\/g, '\\\\')} + 1.0.0 + Path = ${join(fixturesDir, 'modules', 'iphone', 'test-module', '1.0.0').replace(/\\/g, '\\\\')} + Author = Your Name + Description = testModule + Titanium SDK = >=7.2.0 Windows - com.test.module - 1.0.0 ${join(fixturesDir, 'module', 'windows', 'test-module', '1.0.0').replace(/\\/g, '\\\\')} - -Global Modules - No modules found`) + 1.0.0 + Path = ${join(fixturesDir, 'modules', 'windows', 'test-module', '1.0.0').replace(/\\/g, '\\\\')} + Author = Your Name + Description = testModule + Titanium SDK = >=7.2.0`) ); assert.strictEqual(exitCode, 0); @@ -118,7 +126,7 @@ Global Modules '--config', JSON.stringify({ paths: { - modules: [join(fixturesDir, 'module')], + modules: [join(fixturesDir, 'modules')], }, }), '--json', @@ -126,129 +134,99 @@ Global Modules const json = JSON.parse(stdout); assert.deepStrictEqual(json, { - project: {}, - config: { + 'com.test.module': { android: { - 'com.test.module': { - '1.0.0': { - version: '1.0.0', - modulePath: join(fixturesDir, 'module', 'android', 'test-module', '1.0.0'), - manifest: { - version: '1.0.0', - apiversion: 4, - architectures: ['arm64-v8a', 'armeabi-v7a', 'x86'], - description: 'testModule', - author: 'Your Name', - license: 'Specify your license', - copyright: 'Copyright (c) 2018 by Your Company', - name: 'testModule', - moduleid: 'com.test.module', - guid: 'dcaea77e-2860-42c1-a57b-319f81da10e0', - platform: 'android', - minsdk: '7.2.0', - }, - platform: ['android'], - }, + '1.0.0': { + apiversion: 4, + architectures: ['arm64-v8a', 'armeabi-v7a', 'x86'], + author: 'Your Name', + copyright: 'Copyright (c) 2018 by Your Company', + description: 'testModule', + guid: 'dcaea77e-2860-42c1-a57b-319f81da10e0', + license: 'Specify your license', + minsdk: '7.2.0', + moduleid: 'com.test.module', + name: 'testModule', + path: join(fixturesDir, 'modules', 'android', 'test-module', '1.0.0'), + platform: 'android', + version: '1.0.0' }, }, commonjs: { - 'com.test.module': { - '1.0': { - version: '1.0', - modulePath: join(fixturesDir, 'module', 'commonjs', 'invalid-version', '1.0.1'), - manifest: { - version: '1.0', - description: 'testModule', - author: 'Your Name', - license: 'Specify your license', - copyright: 'Copyright (c) 2018 by Your Company', - name: 'testModule', - moduleid: 'com.test.module', - guid: 'dcaea77e-2860-42c1-a57b-319f81da10e0', - platform: 'commonjs', - minsdk: '7.2.0', - }, - platform: ['commonjs'], - }, - '1.0.0': { - version: '1.0.0', - modulePath: join(fixturesDir, 'module', 'commonjs', 'test-module', '1.0.0'), - manifest: { - version: '1.0.0', - description: 'testModule', - author: 'Your Name', - license: 'Specify your license', - copyright: 'Copyright (c) 2018 by Your Company', - name: 'testModule', - moduleid: 'com.test.module', - guid: 'dcaea77e-2860-42c1-a57b-319f81da10e0', - platform: 'commonjs', - minsdk: '7.2.0', - }, - platform: ['commonjs'], - }, + '1.0': { + author: 'Your Name', + copyright: 'Copyright (c) 2018 by Your Company', + description: 'testModule', + guid: 'dcaea77e-2860-42c1-a57b-319f81da10e0', + license: 'Specify your license', + minsdk: '7.2.0', + moduleid: 'com.test.module', + name: 'testModule', + path: join(fixturesDir, 'modules', 'commonjs', 'invalid-version', '1.0.1'), + platform: 'commonjs', + version: '1.0', + }, + '1.0.0': { + author: 'Your Name', + copyright: 'Copyright (c) 2018 by Your Company', + description: 'testModule', + guid: 'dcaea77e-2860-42c1-a57b-319f81da10e0', + license: 'Specify your license', + minsdk: '7.2.0', + moduleid: 'com.test.module', + name: 'testModule', + path: join(fixturesDir, 'modules', 'commonjs', 'test-module', '1.0.0'), + platform: 'commonjs', + version: '1.0.0', }, }, ios: { - 'com.test.module': { - '1.0.0': { - version: '1.0.0', - modulePath: join(fixturesDir, 'module', 'iphone', 'test-module', '1.0.0'), - manifest: { - version: '1.0.0', - apiversion: 2, - architectures: ['armv7', 'arm64', 'i386', 'x86_64'], - description: 'testModule', - author: 'Your Name', - license: 'Specify your license', - copyright: 'Copyright (c) 2018 by Your Company', - name: 'testModule', - moduleid: 'com.test.module', - guid: 'dcaea77e-2860-42c1-a57b-319f81da10e0', - platform: 'ios', - minsdk: '7.2.0', - }, - platform: ['ios'], - }, + '1.0.0': { + apiversion: 2, + architectures: ['armv7', 'arm64', 'i386', 'x86_64'], + author: 'Your Name', + copyright: 'Copyright (c) 2018 by Your Company', + description: 'testModule', + guid: 'dcaea77e-2860-42c1-a57b-319f81da10e0', + license: 'Specify your license', + minsdk: '7.2.0', + moduleid: 'com.test.module', + name: 'testModule', + path: join(fixturesDir, 'modules', 'iphone', 'test-module', '1.0.0'), + platform: 'ios', + version: '1.0.0', }, }, windows: { - 'com.test.module': { - '1.0.0': { - version: '1.0.0', - modulePath: join(fixturesDir, 'module', 'windows', 'test-module', '1.0.0'), - manifest: { - version: '1.0.0', - apiversion: 4, - architectures: ['ARM', 'x86'], - description: 'testModule', - author: 'Your Name', - license: 'Specify your license', - copyright: 'Copyright (c) 2018 by Your Company', - name: 'testModule', - moduleid: 'com.test.module', - guid: 'dcaea77e-2860-42c1-a57b-319f81da10e0', - platform: 'windows', - minsdk: '7.2.0', - }, - platform: ['windows'], - }, + '1.0.0': { + apiversion: 4, + architectures: ['ARM', 'x86'], + author: 'Your Name', + copyright: 'Copyright (c) 2018 by Your Company', + description: 'testModule', + guid: 'dcaea77e-2860-42c1-a57b-319f81da10e0', + license: 'Specify your license', + minsdk: '7.2.0', + moduleid: 'com.test.module', + name: 'testModule', + path: join(fixturesDir, 'modules', 'windows', 'test-module', '1.0.0'), + platform: 'windows', + version: '1.0.0', }, }, }, - global: {}, }); assert.strictEqual(exitCode, 0); }) ); - it( - 'should install module during detection', - initCLI(async ({ run }) => { - const { exitCode, _stdout } = await run(['module']); + // it( + // 'should install module during detection', + // initCLI(async ({ run }) => { + // const { exitCode, _stdout } = await run(['module']); - assert.strictEqual(exitCode, 0); - }) - ); + // assert.strictEqual(exitCode, 0); + // }) + // ); }); diff --git a/test/commands/ti-sdk.test.js b/test/commands/ti-sdk.test.js index 552fec82a..c94cda65b 100644 --- a/test/commands/ti-sdk.test.js +++ b/test/commands/ti-sdk.test.js @@ -1,8 +1,9 @@ import { initCLI } from '../helpers/init-cli.js'; import { initSDKHome, initMockSDKHome } from '../helpers/init-sdk-home.js'; import { stripColor } from '../helpers/strip-color.js'; -import fs from 'fs-extra'; +import { exists } from 'node-titanium-sdk/util'; import assert from 'node:assert'; +import { rm } from 'node:fs/promises'; import { join } from 'node:path'; import { describe, it } from 'node:test'; import { fileURLToPath, pathToFileURL } from 'node:url'; @@ -131,8 +132,8 @@ describe('ti sdk', () => { // find the downloaded file and move it to the tmp dir for subsequent tests const src = join(tmpHome, '.titanium', 'downloads', sdkFilename); - if (fs.existsSync(src)) { - await fs.remove(src); + if (await exists(src)) { + await rm(src, { force: true, recursive: true }); } else { throw new Error(`SDK file does not exist: ${src}`); } diff --git a/test/commands/ti.test.js b/test/commands/ti.test.js index 2d0c0fcc6..db5eb2356 100644 --- a/test/commands/ti.test.js +++ b/test/commands/ti.test.js @@ -1,13 +1,13 @@ import { initCLI } from '../helpers/init-cli.js'; import { stripColor } from '../helpers/strip-color.js'; -import fs from 'fs-extra'; import assert from 'node:assert'; +import { readFileSync } from 'node:fs'; import { dirname, join } from 'node:path'; import { describe, it } from 'node:test'; import { fileURLToPath } from 'node:url'; const __dirname = dirname(fileURLToPath(import.meta.url)); -const pkgJson = fs.readJsonSync(join(__dirname, '../../package.json')); +const pkgJson = JSON.parse(readFileSync(join(__dirname, '../../package.json'), 'utf8')); describe('ti', () => { it( diff --git a/test/helpers/init-cli.js b/test/helpers/init-cli.js index f641de32c..5fd0f94b0 100644 --- a/test/helpers/init-cli.js +++ b/test/helpers/init-cli.js @@ -1,6 +1,6 @@ import { initHome } from './init-home.js'; import { execaNode } from 'execa'; -import fs from 'fs-extra'; +import { rm } from 'node:fs/promises'; import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; @@ -38,7 +38,7 @@ export function initCLI(fixture, fn, sharedOpts = {}) { tmpHome, }); } finally { - await fs.remove(tmpHome); + await rm(tmpHome, { recursive: true }); } }; } diff --git a/test/helpers/init-home.js b/test/helpers/init-home.js index b1de0e920..f11c22797 100644 --- a/test/helpers/init-home.js +++ b/test/helpers/init-home.js @@ -1,12 +1,12 @@ import { tmpDirName } from '../helpers/tmp-dir-name.js'; -import fs from 'fs-extra'; +import { cp, mkdir } from 'node:fs/promises'; export async function initHome(fixture) { const tmpHome = tmpDirName(); if (fixture) { - await fs.copy(fixture, tmpHome); + await cp(fixture, tmpHome); } else { - await fs.mkdirp(tmpHome); + await mkdir(tmpHome, { recursive: true }); } return tmpHome; diff --git a/test/helpers/init-sdk-home.js b/test/helpers/init-sdk-home.js index 54cb5bc43..d0c6b3e31 100644 --- a/test/helpers/init-sdk-home.js +++ b/test/helpers/init-sdk-home.js @@ -1,17 +1,22 @@ +import { rm } from 'node:fs/promises'; import { initCLI } from './init-cli.js'; import { tmpDirName } from './tmp-dir-name.js'; -import fs from 'fs-extra'; import { join } from 'node:path'; import { fileURLToPath } from 'node:url'; +import { cpSync, mkdirSync } from 'node:fs'; +import { dirname } from 'node:path'; export function initSDKHome(fn, mock) { const tmpSDKDir = tmpDirName(); if (mock) { const os = process.platform === 'darwin' ? 'osx' : process.platform; - fs.copySync( + const dest = join(tmpSDKDir, 'mobilesdk', os, '0.0.0.GA'); + mkdirSync(dirname(dest), { recursive: true }); + cpSync( join(fileURLToPath(import.meta.url), '../../mock-sdk'), - join(tmpSDKDir, 'mobilesdk', os, '0.0.0.GA') + dest, + { recursive: true } ); } @@ -25,7 +30,7 @@ export function initSDKHome(fn, mock) { tmpSDKDir, }); } finally { - await fs.remove(tmpSDKDir); + await rm(tmpSDKDir, { force: true, recursive: true }); } }); } diff --git a/test/util/capitalize.test.js b/test/util/capitalize.test.js deleted file mode 100644 index 35e85de40..000000000 --- a/test/util/capitalize.test.js +++ /dev/null @@ -1,11 +0,0 @@ -import { capitalize } from '../../src/util/capitalize.js'; -import assert from 'node:assert'; -import { describe, it } from 'node:test'; - -describe('capitalize', () => { - it('should capitalize a string', () => { - assert.strictEqual(capitalize('foo'), 'Foo'); - assert.strictEqual(capitalize('123 foo'), '123 foo'); - assert.strictEqual(capitalize(''), ''); - }); -}); diff --git a/test/util/expand.test.js b/test/util/expand.test.js deleted file mode 100644 index f75f7a1e1..000000000 --- a/test/util/expand.test.js +++ /dev/null @@ -1,54 +0,0 @@ -import { expand } from '../../src/util/expand.js'; -import assert from 'node:assert'; -import { afterEach, beforeEach, describe, it } from 'node:test'; - -const backup = {}; - -describe('expand', () => { - beforeEach(() => { - backup.HOME = process.env.HOME; - backup.USERPROFILE = process.env.USERPROFILE; - backup.SystemRoot = process.env.SystemRoot; - }); - - afterEach(() => { - if (backup.HOME !== undefined) { - process.env.HOME = backup.HOME; - } - if (backup.USERPROFILE !== undefined) { - process.env.USERPROFILE = backup.USERPROFILE; - } - if (backup.SystemRoot !== undefined) { - process.env.SystemRoot = backup.SystemRoot; - } - }); - - const isWin = process.platform === 'win32'; - - it('should resolve the home directory using HOME', () => { - process.env.HOME = isWin ? 'C:\\Users\\username' : '/Users/username'; - delete process.env.USERPROFILE; - - const p = expand('~/foo'); - assert.strictEqual(p, isWin ? 'C:\\Users\\username\\foo' : '/Users/username/foo'); - }); - - it('should resolve the home directory using USERPROFILE', () => { - delete process.env.HOME; - process.env.USERPROFILE = isWin ? 'C:\\Users\\username' : '/Users/username'; - - const p = expand('~/foo'); - assert.strictEqual(p, isWin ? 'C:\\Users\\username\\foo' : '/Users/username/foo'); - }); - - it('should collapse relative segments', () => { - const p = expand('/path/./to/../foo'); - assert.match(p, isWin ? /\\path\\foo/ : /\/path\/foo/); - }); - - (isWin ? it : it.skip)('should resolve environment paths (Windows)', () => { - process.env.SystemRoot = 'C:\\WINDOWS'; - const p = expand('%SystemRoot%\\foo'); - assert.match(isWin ? p : p.substring(process.cwd().length + 1), /\\WINDOWS\\foo/); - }); -}); diff --git a/test/util/suggest.test.js b/test/util/suggest.test.js deleted file mode 100644 index 9eb463a67..000000000 --- a/test/util/suggest.test.js +++ /dev/null @@ -1,35 +0,0 @@ -import { suggest } from '../../src/util/suggest.js'; -import { stripColor } from '../helpers/strip-color.js'; -import assert from 'node:assert'; -import { describe, it } from 'node:test'; - -const cmds = ['build', 'clean', 'config', 'create', 'info']; - -describe('suggest', () => { - it('should suggest a value', () => { - assert.strictEqual(stripColor(suggest('buid', cmds)), 'Did you mean this?\n build\n\n'); - }); - - it('should suggest multiple values', () => { - assert.strictEqual( - stripColor(suggest('cre', cmds)), - 'Did you mean this?\n clean\n create\n\n' - ); - - assert.strictEqual( - stripColor(suggest('eat', cmds)), - 'Did you mean this?\n clean\n create\n\n' - ); - }); - - it('should suggest everything if empty', () => { - assert.strictEqual( - stripColor(suggest('', cmds)), - 'Did you mean this?\n build\n clean\n config\n create\n info\n\n' - ); - }); - - it('should not find any suggestions', () => { - assert.strictEqual(stripColor(suggest('zzz', cmds)), ''); - }); -}); diff --git a/test/util/ticonfig.test.js b/test/util/ticonfig.test.js index 1ec05d7be..9406c0139 100644 --- a/test/util/ticonfig.test.js +++ b/test/util/ticonfig.test.js @@ -1,6 +1,6 @@ import { TiConfig } from '../../src/util/ticonfig.js'; import assert from 'node:assert'; -import { copyFile, mkdir, readFile, rmdir, unlink } from 'node:fs/promises'; +import { cp, mkdir, readFile, rmdir, unlink } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { describe, it } from 'node:test'; @@ -117,7 +117,7 @@ describe('TiConfig', () => { it('should save the config', async () => { const tmpFile = join(tmpdir(), 'ticonfig.json'); - await copyFile(join(fixturesDir, 'good.json'), tmpFile); + await cp(join(fixturesDir, 'good.json'), tmpFile); try { const cfg = new TiConfig(tmpFile); diff --git a/test/util/timodule.test.js b/test/util/timodule.test.js deleted file mode 100644 index ef36850ee..000000000 --- a/test/util/timodule.test.js +++ /dev/null @@ -1,138 +0,0 @@ -import { TiConfig } from '../../src/util/ticonfig.js'; -import { detect } from '../../src/util/timodule.js'; -import { tmpDirName } from '../helpers/tmp-dir-name.js'; -import fs from 'fs-extra'; -import assert from 'node:assert'; -import { join } from 'node:path'; -import { describe, it } from 'node:test'; -import { fileURLToPath } from 'node:url'; - -const fixturesDir = join(fileURLToPath(import.meta.url), '../fixtures/timodule'); -const goodConfig = join(fileURLToPath(import.meta.url), '../fixtures/ticonfig/good.json'); - -describe('timodule', () => { - it('should find nothing if no search paths', async () => { - let results = await detect(); - assert.deepStrictEqual(results, {}); - - results = await detect(null); - assert.deepStrictEqual(results, {}); - - results = await detect({}); - assert.deepStrictEqual(results, {}); - - results = await detect({ global: null }, new TiConfig(goodConfig)); - assert.deepStrictEqual(results, { - global: {}, - }); - }); - - it('should find nothing if search path is empty', async () => { - const results = await detect({ empty: join(fixturesDir, 'empty') }, new TiConfig(goodConfig)); - assert.deepStrictEqual(results, { - empty: {}, - }); - }); - - it('should find modules in a search path', async () => { - const tmpModulesDir = join(tmpDirName(), 'modules'); - try { - await fs.copy(join(fixturesDir, 'modules'), tmpModulesDir); - - const results = await detect({ global: tmpModulesDir }, new TiConfig(goodConfig)); - - assert(Object.hasOwn(results, 'global')); - assert(Object.hasOwn(results.global, 'android')); - assert(Object.hasOwn(results.global, 'commonjs')); - assert(Object.hasOwn(results.global, 'ios')); - - assert(Object.hasOwn(results.global.android, 'com.test.module')); - let mod = results.global.android['com.test.module']; - assert(Object.hasOwn(mod, '1.0.0')); - assert(Object.hasOwn(mod['1.0.0'], 'version')); - assert.strictEqual(mod['1.0.0'].version, '1.0.0'); - assert(Object.hasOwn(mod['1.0.0'], 'platform')); - assert.deepStrictEqual(mod['1.0.0'].platform, ['android']); - assert(Object.hasOwn(mod['1.0.0'], 'manifest')); - assert.deepStrictEqual(mod['1.0.0'].manifest, { - version: '1.0.0', - apiversion: 4, - architectures: ['arm64-v8a', 'armeabi-v7a', 'x86'], - description: 'testModule', - author: 'Your Name', - license: 'Specify your license', - copyright: 'Copyright (c) 2018 by Your Company', - name: 'testModule', - moduleid: 'com.test.module', - guid: 'dcaea77e-2860-42c1-a57b-319f81da10e0', - platform: 'android', - minsdk: '7.2.0', - }); - - assert(Object.hasOwn(results.global.commonjs, 'com.test.module')); - mod = results.global.commonjs['com.test.module']; - assert(Object.hasOwn(mod, '1.0.0')); - assert(Object.hasOwn(mod['1.0.0'], 'version')); - assert.strictEqual(mod['1.0.0'].version, '1.0.0'); - assert(Object.hasOwn(mod['1.0.0'], 'platform')); - assert.deepStrictEqual(mod['1.0.0'].platform, ['commonjs']); - assert(Object.hasOwn(mod['1.0.0'], 'manifest')); - assert.deepStrictEqual(mod['1.0.0'].manifest, { - version: '1.0.0', - description: 'testModule', - author: 'Your Name', - license: 'Specify your license', - copyright: 'Copyright (c) 2018 by Your Company', - name: 'testModule', - moduleid: 'com.test.module', - guid: 'dcaea77e-2860-42c1-a57b-319f81da10e0', - platform: 'commonjs', - minsdk: '7.2.0', - }); - - assert(Object.hasOwn(mod, '1.0')); - assert(Object.hasOwn(mod['1.0'], 'version')); - assert.strictEqual(mod['1.0'].version, '1.0'); - assert(Object.hasOwn(mod['1.0'], 'platform')); - assert.deepStrictEqual(mod['1.0'].platform, ['commonjs']); - assert(Object.hasOwn(mod['1.0'], 'manifest')); - assert.deepStrictEqual(mod['1.0'].manifest, { - version: '1.0', - description: 'testModule', - author: 'Your Name', - license: 'Specify your license', - copyright: 'Copyright (c) 2018 by Your Company', - name: 'testModule', - moduleid: 'com.test.module', - guid: 'dcaea77e-2860-42c1-a57b-319f81da10e0', - platform: 'commonjs', - minsdk: '7.2.0', - }); - - assert(Object.hasOwn(results.global.ios, 'com.test.module')); - mod = results.global.ios['com.test.module']; - assert(Object.hasOwn(mod, '1.0.0')); - assert(Object.hasOwn(mod['1.0.0'], 'version')); - assert.strictEqual(mod['1.0.0'].version, '1.0.0'); - assert(Object.hasOwn(mod['1.0.0'], 'platform')); - assert.deepStrictEqual(mod['1.0.0'].platform, ['ios']); - assert(Object.hasOwn(mod['1.0.0'], 'manifest')); - assert.deepStrictEqual(mod['1.0.0'].manifest, { - version: '1.0.0', - apiversion: 2, - architectures: ['armv7', 'arm64', 'i386', 'x86_64'], - description: 'testModule', - author: 'Your Name', - license: 'Specify your license', - copyright: 'Copyright (c) 2018 by Your Company', - name: 'testModule', - moduleid: 'com.test.module', - guid: 'dcaea77e-2860-42c1-a57b-319f81da10e0', - platform: 'ios', - minsdk: '7.2.0', - }); - } finally { - await fs.remove(tmpModulesDir); - } - }, 60000); -}); From 920fbf887e6a88e6c489d3ee082f9fb5280fa60b Mon Sep 17 00:00:00 2001 From: Chris Barber Date: Thu, 5 Mar 2026 01:32:35 -0600 Subject: [PATCH 05/16] Fix latest sdk, formatting --- src/commands/config.js | 2 +- src/commands/info.js | 6 ++++-- src/commands/module.js | 4 ++-- src/commands/sdk.js | 4 ++-- src/util/detect.js | 9 +++++---- src/util/ticonfig.js | 4 ++-- src/util/tihelp.js | 2 +- src/util/tisdk.js | 5 ++++- test/commands/ti-module.test.js | 2 +- test/helpers/init-sdk-home.js | 12 ++++-------- 10 files changed, 26 insertions(+), 24 deletions(-) diff --git a/src/commands/config.js b/src/commands/config.js index 0a4fedfc5..2bf38e8f4 100644 --- a/src/commands/config.js +++ b/src/commands/config.js @@ -1,9 +1,9 @@ /* eslint-disable max-len */ -import { expand } from 'node-titanium-sdk/util'; import { ticonfig } from '../util/ticonfig.js'; import { TiError } from '../util/tierror.js'; import chalk from 'chalk'; +import { expand } from 'node-titanium-sdk/util'; const { cyan } = chalk; diff --git a/src/commands/info.js b/src/commands/info.js index b23abbb77..471132adf 100644 --- a/src/commands/info.js +++ b/src/commands/info.js @@ -77,7 +77,9 @@ export async function run(logger, config, cli) { // determine the types to display const types = {}; let i = 0; - const specifiedTypes = cli.argv._[0]?.length ? cli.argv._[0] : (cli.argv.types || 'all').toLowerCase().split(','); + const specifiedTypes = cli.argv._[0]?.length + ? cli.argv._[0] + : (cli.argv.types || 'all').toLowerCase().split(','); for (let type of specifiedTypes) { type = type.trim(); @@ -185,7 +187,7 @@ export async function run(logger, config, cli) { logger.log(bold(this.title)); logger.log(` ${'JAVA_HOME'.padEnd(indent)} = ${magenta(data.java.home || 'not set')}`); if (Object.keys(data.java.jdks).length) { - for (const { path, version} of Object.values(data.java.jdks)) { + for (const { path, version } of Object.values(data.java.jdks)) { logger.log(` ${cyan(version)}`); logger.log(` ${' Path'.padEnd(indent)} = ${magenta(path)}`); } diff --git a/src/commands/module.js b/src/commands/module.js index 3688562a0..737da1c32 100644 --- a/src/commands/module.js +++ b/src/commands/module.js @@ -1,8 +1,8 @@ import { arrayify } from '../util/arrayify.js'; -import { capitalize, expand, isDir, isFile } from 'node-titanium-sdk/util'; import { TiError } from '../util/tierror.js'; import chalk from 'chalk'; import { detectTiModules } from 'node-titanium-sdk/titanium'; +import { capitalize, expand, isDir, isFile } from 'node-titanium-sdk/util'; import { dirname, join, parse } from 'node:path'; const { cyan, gray, magenta } = chalk; @@ -113,7 +113,7 @@ ModuleSubcommands.list = { } const { modules } = await detectTiModules({ - searchPaths + searchPaths, }); if (isJson) { diff --git a/src/commands/sdk.js b/src/commands/sdk.js index 97d23e053..600a9cae8 100644 --- a/src/commands/sdk.js +++ b/src/commands/sdk.js @@ -461,7 +461,7 @@ SdkSubcommands.install = { } const destDir = join(modulesDest, platform, moduleName, ver); - if (!forceModules && await exists(destDir)) { + if (!forceModules && (await exists(destDir))) { trace(`Module ${cyan(`${moduleName}@${ver}`)} already installed`); continue; } @@ -502,7 +502,7 @@ async function getInstallFile({ branch, config, logger, osName, showProgress, su if (uriMatch && uriMatch[2]) { file = uriMatch[2]; - } else if (subject && await exists(subject)) { + } else if (subject && (await exists(subject))) { file = subject; } diff --git a/src/util/detect.js b/src/util/detect.js index e025470fa..2df6810c8 100644 --- a/src/util/detect.js +++ b/src/util/detect.js @@ -1,5 +1,5 @@ -import { detectJDKs } from 'node-titanium-sdk/jdk'; import chalk from 'chalk'; +import { detectJDKs } from 'node-titanium-sdk/jdk'; import { detectTitaniumSDKs } from 'node-titanium-sdk/titanium'; import { existsSync } from 'node:fs'; import { readFile } from 'node:fs/promises'; @@ -16,9 +16,10 @@ export async function detect(logger, config, cli, types = { all: true }) { (types.all || types.nodejs) && npmInfo(), (types.all || types.titanium) && titaniumSDKInfo(config), (types.all || types.titanium) && titaniumCLIInfo(cli), - (types.all || types.java) && detectJDKs({ - javaHome: config.get('java.home'), - }), + (types.all || types.java) && + detectJDKs({ + javaHome: config.get('java.home'), + }), ...Object.keys(cli.sdk?.platforms || {}) .sort() .map(async (name) => { diff --git a/src/util/ticonfig.js b/src/util/ticonfig.js index 669e0a89c..07c0970d3 100644 --- a/src/util/ticonfig.js +++ b/src/util/ticonfig.js @@ -1,7 +1,7 @@ -import { existsSync, expand, isFile } from 'node-titanium-sdk/util'; import { TiError } from './tierror.js'; -import { join } from 'node:path'; +import { existsSync, expand, isFile } from 'node-titanium-sdk/util'; import { mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs'; +import { join } from 'node:path'; export class TiConfig { #configFile = ''; diff --git a/src/util/tihelp.js b/src/util/tihelp.js index 4daec74e5..84510de28 100644 --- a/src/util/tihelp.js +++ b/src/util/tihelp.js @@ -1,7 +1,7 @@ import { applyCommandConfig } from './apply-command-config.js'; -import { capitalize } from 'node-titanium-sdk/util'; import chalk from 'chalk'; import { Command, Help } from 'commander'; +import { capitalize } from 'node-titanium-sdk/util'; const { cyan, gray } = chalk; diff --git a/src/util/tisdk.js b/src/util/tisdk.js index 930bacb18..b93715cf5 100644 --- a/src/util/tisdk.js +++ b/src/util/tisdk.js @@ -31,7 +31,8 @@ export async function initSDK({ const tiappFile = join(cwd, 'tiapp.xml'); await tiapp.load(tiappFile); debugLogger.trace(`Loaded ${tiappFile}`); - sdkVersion = tiappSdkVersion = await tiapp.select1('//sdk-version', 'latest'); + const data = tiapp.data(); + sdkVersion = tiappSdkVersion = data.sdkVersion || 'latest'; debugLogger.trace( ` is ${tiappSdkVersion ? `set to ${tiappSdkVersion}` : 'undefined'}` ); @@ -58,7 +59,9 @@ export async function initSDK({ sdkVersion = latest; } + console.log(sdkVersion); sdk = sdks[sdkVersion]; + console.log(sdk); if (promptingEnabled && ((selectedSdk && !sdk) || showSDKPrompt)) { logger.banner(); diff --git a/test/commands/ti-module.test.js b/test/commands/ti-module.test.js index 090d0febe..9f947b618 100644 --- a/test/commands/ti-module.test.js +++ b/test/commands/ti-module.test.js @@ -149,7 +149,7 @@ describe('ti module', () => { name: 'testModule', path: join(fixturesDir, 'modules', 'android', 'test-module', '1.0.0'), platform: 'android', - version: '1.0.0' + version: '1.0.0', }, }, commonjs: { diff --git a/test/helpers/init-sdk-home.js b/test/helpers/init-sdk-home.js index d0c6b3e31..51406729a 100644 --- a/test/helpers/init-sdk-home.js +++ b/test/helpers/init-sdk-home.js @@ -1,10 +1,10 @@ -import { rm } from 'node:fs/promises'; import { initCLI } from './init-cli.js'; import { tmpDirName } from './tmp-dir-name.js'; -import { join } from 'node:path'; -import { fileURLToPath } from 'node:url'; import { cpSync, mkdirSync } from 'node:fs'; +import { rm } from 'node:fs/promises'; +import { join } from 'node:path'; import { dirname } from 'node:path'; +import { fileURLToPath } from 'node:url'; export function initSDKHome(fn, mock) { const tmpSDKDir = tmpDirName(); @@ -13,11 +13,7 @@ export function initSDKHome(fn, mock) { const os = process.platform === 'darwin' ? 'osx' : process.platform; const dest = join(tmpSDKDir, 'mobilesdk', os, '0.0.0.GA'); mkdirSync(dirname(dest), { recursive: true }); - cpSync( - join(fileURLToPath(import.meta.url), '../../mock-sdk'), - dest, - { recursive: true } - ); + cpSync(join(fileURLToPath(import.meta.url), '../../mock-sdk'), dest, { recursive: true }); } return initCLI(async (opts) => { From 2ec0faa2f288781fb1c84024599c34e990db8b1e Mon Sep 17 00:00:00 2001 From: Chris Barber Date: Thu, 5 Mar 2026 01:34:33 -0600 Subject: [PATCH 06/16] Remove debug --- src/util/tisdk.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/util/tisdk.js b/src/util/tisdk.js index b93715cf5..64106d56f 100644 --- a/src/util/tisdk.js +++ b/src/util/tisdk.js @@ -59,9 +59,7 @@ export async function initSDK({ sdkVersion = latest; } - console.log(sdkVersion); sdk = sdks[sdkVersion]; - console.log(sdk); if (promptingEnabled && ((selectedSdk && !sdk) || showSDKPrompt)) { logger.banner(); From 38bb342c686e318963d86bcc1a9703c708ac8525 Mon Sep 17 00:00:00 2001 From: Chris Barber Date: Sun, 8 Mar 2026 23:38:47 -0500 Subject: [PATCH 07/16] Improve jdk errors and show selected jdk --- src/commands/info.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/commands/info.js b/src/commands/info.js index 471132adf..dc3624d2b 100644 --- a/src/commands/info.js +++ b/src/commands/info.js @@ -1,6 +1,7 @@ import { BusyIndicator } from '../util/busyindicator.js'; import { detect } from '../util/detect.js'; import chalk from 'chalk'; +import { realpathSync } from 'node:fs'; import { basename } from 'node:path'; import wrapAnsi from 'wrap-ansi'; @@ -185,10 +186,13 @@ export async function run(logger, config, cli) { title: 'Java', render() { logger.log(bold(this.title)); - logger.log(` ${'JAVA_HOME'.padEnd(indent)} = ${magenta(data.java.home || 'not set')}`); if (Object.keys(data.java.jdks).length) { + let javaHome = config.get('java.home', data.java.home); + if (javaHome) { + javaHome = realpathSync(javaHome); + } for (const { path, version } of Object.values(data.java.jdks)) { - logger.log(` ${cyan(version)}`); + logger.log(` ${cyan(version)}${javaHome === realpathSync(path) ? ' (selected)' : ''}`); logger.log(` ${' Path'.padEnd(indent)} = ${magenta(path)}`); } logger.log(); @@ -255,7 +259,11 @@ export async function run(logger, config, cli) { logger.log(bold(`${section.title} Issues`)); for (const issue of info.issues) { - const msg = (issue.details || issue.message) + let msg = issue.details || issue.message; + if (issue.id === 'JDK_NOT_FOUND') { + msg = msg.slice(0, -1) + ' or configure the path by running: __titanium config java.home /path/to/jdk__'; + } + msg = msg .trim() .split('\n\n') .map((chunk) => { From 2ee4be338fe782aeec2a20bb83b0d56e706fc9da Mon Sep 17 00:00:00 2001 From: Chris Barber Date: Tue, 10 Mar 2026 22:26:26 -0500 Subject: [PATCH 08/16] Formatting --- src/commands/info.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/commands/info.js b/src/commands/info.js index dc3624d2b..51c5a2183 100644 --- a/src/commands/info.js +++ b/src/commands/info.js @@ -192,7 +192,9 @@ export async function run(logger, config, cli) { javaHome = realpathSync(javaHome); } for (const { path, version } of Object.values(data.java.jdks)) { - logger.log(` ${cyan(version)}${javaHome === realpathSync(path) ? ' (selected)' : ''}`); + logger.log( + ` ${cyan(version)}${javaHome === realpathSync(path) ? ' (selected)' : ''}` + ); logger.log(` ${' Path'.padEnd(indent)} = ${magenta(path)}`); } logger.log(); @@ -261,7 +263,9 @@ export async function run(logger, config, cli) { for (const issue of info.issues) { let msg = issue.details || issue.message; if (issue.id === 'JDK_NOT_FOUND') { - msg = msg.slice(0, -1) + ' or configure the path by running: __titanium config java.home /path/to/jdk__'; + msg = + msg.slice(0, -1) + + ' or configure the path by running: __titanium config java.home /path/to/jdk__'; } msg = msg .trim() From a8e5e19cb601be535b5b7a78ff98b49fa117a043 Mon Sep 17 00:00:00 2001 From: Chris Barber Date: Tue, 10 Mar 2026 22:43:50 -0500 Subject: [PATCH 09/16] Add node-titanium-sdk dep --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 4786f7291..b05127a42 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "chalk": "5.6.2", "commander": "14.0.3", "execa": "9.6.1", + "node-titanium-sdk": "github:tidev/node-titaniumsdk#v7", "pretty-bytes": "7.1.0", "prompts": "2.4.2", "semver": "7.7.4", From f2c71d66d366a05c2d524a8c6907d76c7168f827 Mon Sep 17 00:00:00 2001 From: Chris Barber Date: Tue, 10 Mar 2026 23:13:26 -0500 Subject: [PATCH 10/16] Add script to install/build node-titanium-sdk from source --- package.json | 3 +- pnpm-lock.yaml | 1645 ++++++++++++++++++++++++++-- scripts/build-node-titanium-sdk.js | 28 + 3 files changed, 1585 insertions(+), 91 deletions(-) create mode 100644 scripts/build-node-titanium-sdk.js diff --git a/package.json b/package.json index b05127a42..f30297d28 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "fmt": "oxfmt", "fmt:check": "oxfmt --check", "lint": "oxlint", + "postinstall": "node scripts/build-node-titanium-sdk.js", "test": "node scripts/test.js", "test-only": "node scripts/test.js --only" }, @@ -40,7 +41,7 @@ "chalk": "5.6.2", "commander": "14.0.3", "execa": "9.6.1", - "node-titanium-sdk": "github:tidev/node-titaniumsdk#v7", + "node-titanium-sdk": "https://github.com/tidev/node-titanium-sdk/archive/refs/heads/v7.tar.gz", "pretty-bytes": "7.1.0", "prompts": "2.4.2", "semver": "7.7.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a28b5cc70..056a89824 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,6 +20,9 @@ importers: execa: specifier: 9.6.1 version: 9.6.1 + node-titanium-sdk: + specifier: https://github.com/tidev/node-titanium-sdk/archive/refs/heads/v7.tar.gz + version: https://github.com/tidev/node-titanium-sdk/archive/refs/heads/v7.tar.gz pretty-bytes: specifier: 7.1.0 version: 7.1.0 @@ -103,14 +106,39 @@ packages: resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==} engines: {node: '>=6.9.0'} + '@babel/helper-annotate-as-pure@7.27.3': + resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==} + engines: {node: '>=6.9.0'} + '@babel/helper-compilation-targets@7.28.6': resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==} engines: {node: '>=6.9.0'} + '@babel/helper-create-class-features-plugin@7.28.6': + resolution: {integrity: sha512-dTOdvsjnG3xNT9Y0AUg1wAl38y+4Rl4sf9caSQZOXdNqVn+H+HbbJ4IyyHaIqNR6SW9oJpA/RuRjsjCw2IdIow==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-create-regexp-features-plugin@7.28.5': + resolution: {integrity: sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-define-polyfill-provider@0.6.7': + resolution: {integrity: sha512-6Fqi8MtQ/PweQ9xvux65emkLQ83uB+qAVtfHkC9UodyHMIZdxNI01HjLCLUtybElp2KY2XNE0nOgyP1E1vXw9w==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + '@babel/helper-globals@7.28.0': resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} engines: {node: '>=6.9.0'} + '@babel/helper-member-expression-to-functions@7.28.5': + resolution: {integrity: sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==} + engines: {node: '>=6.9.0'} + '@babel/helper-module-imports@7.28.6': resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==} engines: {node: '>=6.9.0'} @@ -121,6 +149,30 @@ packages: peerDependencies: '@babel/core': ^7.0.0 + '@babel/helper-optimise-call-expression@7.27.1': + resolution: {integrity: sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-plugin-utils@7.28.6': + resolution: {integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==} + engines: {node: '>=6.9.0'} + + '@babel/helper-remap-async-to-generator@7.27.1': + resolution: {integrity: sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-replace-supers@7.28.6': + resolution: {integrity: sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-skip-transparent-expression-wrappers@7.27.1': + resolution: {integrity: sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==} + engines: {node: '>=6.9.0'} + '@babel/helper-string-parser@7.27.1': resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} engines: {node: '>=6.9.0'} @@ -133,6 +185,10 @@ packages: resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} engines: {node: '>=6.9.0'} + '@babel/helper-wrap-function@7.28.6': + resolution: {integrity: sha512-z+PwLziMNBeSQJonizz2AGnndLsP2DeGHIxDAn+wdHOGuo4Fo1x1HBPPXeE9TAOPHNNWQKCSlA2VZyYyyibDnQ==} + engines: {node: '>=6.9.0'} + '@babel/helpers@7.28.6': resolution: {integrity: sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==} engines: {node: '>=6.9.0'} @@ -142,6 +198,377 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5': + resolution: {integrity: sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1': + resolution: {integrity: sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1': + resolution: {integrity: sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1': + resolution: {integrity: sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.13.0 + + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.28.6': + resolution: {integrity: sha512-a0aBScVTlNaiUe35UtfxAN7A/tehvvG4/ByO6+46VPKTRSlfnAFsgKy0FUh+qAkQrDTmhDkT+IBOKlOoMUxQ0g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2': + resolution: {integrity: sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-assertions@7.28.6': + resolution: {integrity: sha512-pSJUpFHdx9z5nqTSirOCMtYVP2wFgoWhP0p3g8ONK/4IHhLIBd0B9NYqAvIUAhq+OkhO4VM1tENCt0cjlsNShw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-attributes@7.28.6': + resolution: {integrity: sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-unicode-sets-regex@7.18.6': + resolution: {integrity: sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-arrow-functions@7.27.1': + resolution: {integrity: sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-async-generator-functions@7.29.0': + resolution: {integrity: sha512-va0VdWro4zlBr2JsXC+ofCPB2iG12wPtVGTWFx2WLDOM3nYQZZIGP82qku2eW/JR83sD+k2k+CsNtyEbUqhU6w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-async-to-generator@7.28.6': + resolution: {integrity: sha512-ilTRcmbuXjsMmcZ3HASTe4caH5Tpo93PkTxF9oG2VZsSWsahydmcEHhix9Ik122RcTnZnUzPbmux4wh1swfv7g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-block-scoped-functions@7.27.1': + resolution: {integrity: sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-block-scoping@7.28.6': + resolution: {integrity: sha512-tt/7wOtBmwHPNMPu7ax4pdPz6shjFrmHDghvNC+FG9Qvj7D6mJcoRQIF5dy4njmxR941l6rgtvfSB2zX3VlUIw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-class-properties@7.28.6': + resolution: {integrity: sha512-dY2wS3I2G7D697VHndN91TJr8/AAfXQNt5ynCTI/MpxMsSzHp+52uNivYT5wCPax3whc47DR8Ba7cmlQMg24bw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-class-static-block@7.28.6': + resolution: {integrity: sha512-rfQ++ghVwTWTqQ7w8qyDxL1XGihjBss4CmTgGRCTAC9RIbhVpyp4fOeZtta0Lbf+dTNIVJer6ych2ibHwkZqsQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.12.0 + + '@babel/plugin-transform-classes@7.28.6': + resolution: {integrity: sha512-EF5KONAqC5zAqT783iMGuM2ZtmEBy+mJMOKl2BCvPZ2lVrwvXnB6o+OBWCS+CoeCCpVRF2sA2RBKUxvT8tQT5Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-computed-properties@7.28.6': + resolution: {integrity: sha512-bcc3k0ijhHbc2lEfpFHgx7eYw9KNXqOerKWfzbxEHUGKnS3sz9C4CNL9OiFN1297bDNfUiSO7DaLzbvHQQQ1BQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-destructuring@7.28.5': + resolution: {integrity: sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-dotall-regex@7.28.6': + resolution: {integrity: sha512-SljjowuNKB7q5Oayv4FoPzeB74g3QgLt8IVJw9ADvWy3QnUb/01aw8I4AVv8wYnPvQz2GDDZ/g3GhcNyDBI4Bg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-duplicate-keys@7.27.1': + resolution: {integrity: sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.29.0': + resolution: {integrity: sha512-zBPcW2lFGxdiD8PUnPwJjag2J9otbcLQzvbiOzDxpYXyCuYX9agOwMPGn1prVH0a4qzhCKu24rlH4c1f7yA8rw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-dynamic-import@7.27.1': + resolution: {integrity: sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-explicit-resource-management@7.28.6': + resolution: {integrity: sha512-Iao5Konzx2b6g7EPqTy40UZbcdXE126tTxVFr/nAIj+WItNxjKSYTEw3RC+A2/ZetmdJsgueL1KhaMCQHkLPIg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-exponentiation-operator@7.28.6': + resolution: {integrity: sha512-WitabqiGjV/vJ0aPOLSFfNY1u9U3R7W36B03r5I2KoNix+a3sOhJ3pKFB3R5It9/UiK78NiO0KE9P21cMhlPkw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-export-namespace-from@7.27.1': + resolution: {integrity: sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-for-of@7.27.1': + resolution: {integrity: sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-function-name@7.27.1': + resolution: {integrity: sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-json-strings@7.28.6': + resolution: {integrity: sha512-Nr+hEN+0geQkzhbdgQVPoqr47lZbm+5fCUmO70722xJZd0Mvb59+33QLImGj6F+DkK3xgDi1YVysP8whD6FQAw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-literals@7.27.1': + resolution: {integrity: sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-logical-assignment-operators@7.28.6': + resolution: {integrity: sha512-+anKKair6gpi8VsM/95kmomGNMD0eLz1NQ8+Pfw5sAwWH9fGYXT50E55ZpV0pHUHWf6IUTWPM+f/7AAff+wr9A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-member-expression-literals@7.27.1': + resolution: {integrity: sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-amd@7.27.1': + resolution: {integrity: sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-commonjs@7.28.6': + resolution: {integrity: sha512-jppVbf8IV9iWWwWTQIxJMAJCWBuuKx71475wHwYytrRGQ2CWiDvYlADQno3tcYpS/T2UUWFQp3nVtYfK/YBQrA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-systemjs@7.29.0': + resolution: {integrity: sha512-PrujnVFbOdUpw4UHiVwKvKRLMMic8+eC0CuNlxjsyZUiBjhFdPsewdXCkveh2KqBA9/waD0W1b4hXSOBQJezpQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-umd@7.27.1': + resolution: {integrity: sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-named-capturing-groups-regex@7.29.0': + resolution: {integrity: sha512-1CZQA5KNAD6ZYQLPw7oi5ewtDNxH/2vuCh+6SmvgDfhumForvs8a1o9n0UrEoBD8HU4djO2yWngTQlXl1NDVEQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-new-target@7.27.1': + resolution: {integrity: sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-nullish-coalescing-operator@7.28.6': + resolution: {integrity: sha512-3wKbRgmzYbw24mDJXT7N+ADXw8BC/imU9yo9c9X9NKaLF1fW+e5H1U5QjMUBe4Qo4Ox/o++IyUkl1sVCLgevKg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-numeric-separator@7.28.6': + resolution: {integrity: sha512-SJR8hPynj8outz+SlStQSwvziMN4+Bq99it4tMIf5/Caq+3iOc0JtKyse8puvyXkk3eFRIA5ID/XfunGgO5i6w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-object-rest-spread@7.28.6': + resolution: {integrity: sha512-5rh+JR4JBC4pGkXLAcYdLHZjXudVxWMXbB6u6+E9lRL5TrGVbHt1TjxGbZ8CkmYw9zjkB7jutzOROArsqtncEA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-object-super@7.27.1': + resolution: {integrity: sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-optional-catch-binding@7.28.6': + resolution: {integrity: sha512-R8ja/Pyrv0OGAvAXQhSTmWyPJPml+0TMqXlO5w+AsMEiwb2fg3WkOvob7UxFSL3OIttFSGSRFKQsOhJ/X6HQdQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-optional-chaining@7.28.6': + resolution: {integrity: sha512-A4zobikRGJTsX9uqVFdafzGkqD30t26ck2LmOzAuLL8b2x6k3TIqRiT2xVvA9fNmFeTX484VpsdgmKNA0bS23w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-parameters@7.27.7': + resolution: {integrity: sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-private-methods@7.28.6': + resolution: {integrity: sha512-piiuapX9CRv7+0st8lmuUlRSmX6mBcVeNQ1b4AYzJxfCMuBfB0vBXDiGSmm03pKJw1v6cZ8KSeM+oUnM6yAExg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-private-property-in-object@7.28.6': + resolution: {integrity: sha512-b97jvNSOb5+ehyQmBpmhOCiUC5oVK4PMnpRvO7+ymFBoqYjeDHIU9jnrNUuwHOiL9RpGDoKBpSViarV+BU+eVA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-property-literals@7.27.1': + resolution: {integrity: sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-regenerator@7.29.0': + resolution: {integrity: sha512-FijqlqMA7DmRdg/aINBSs04y8XNTYw/lr1gJ2WsmBnnaNw1iS43EPkJW+zK7z65auG3AWRFXWj+NcTQwYptUog==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-regexp-modifiers@7.28.6': + resolution: {integrity: sha512-QGWAepm9qxpaIs7UM9FvUSnCGlb8Ua1RhyM4/veAxLwt3gMat/LSGrZixyuj4I6+Kn9iwvqCyPTtbdxanYoWYg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-reserved-words@7.27.1': + resolution: {integrity: sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-shorthand-properties@7.27.1': + resolution: {integrity: sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-spread@7.28.6': + resolution: {integrity: sha512-9U4QObUC0FtJl05AsUcodau/RWDytrU6uKgkxu09mLR9HLDAtUMoPuuskm5huQsoktmsYpI+bGmq+iapDcriKA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-sticky-regex@7.27.1': + resolution: {integrity: sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-template-literals@7.27.1': + resolution: {integrity: sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-typeof-symbol@7.27.1': + resolution: {integrity: sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-escapes@7.27.1': + resolution: {integrity: sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-property-regex@7.28.6': + resolution: {integrity: sha512-4Wlbdl/sIZjzi/8St0evF0gEZrgOswVO6aOzqxh1kDZOl9WmLrHq2HtGhnOJZmHZYKP8WZ1MDLCt5DAWwRo57A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-regex@7.27.1': + resolution: {integrity: sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-sets-regex@7.28.6': + resolution: {integrity: sha512-/wHc/paTUmsDYN7SZkpWxogTOBNnlx7nBQYfy6JJlCT7G3mVhltk3e++N7zV0XfgGsrqBxd4rJQt9H16I21Y1Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/preset-env@7.29.0': + resolution: {integrity: sha512-fNEdfc0yi16lt6IZo2Qxk3knHVdfMYX33czNb4v8yWhemoBhibCpQK/uYHtSKIiO+p/zd3+8fYVXhQdOVV608w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/preset-modules@0.1.6-no-external-plugins': + resolution: {integrity: sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==} + peerDependencies: + '@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0 + '@babel/template@7.28.6': resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} engines: {node: '>=6.9.0'} @@ -775,6 +1202,10 @@ packages: '@vitest/utils@4.0.18': resolution: {integrity: sha512-msMRKLMVLWygpK3u2Hybgi4MNjcYJvwTb0Ru09+fOyCXIgT5raYP041DRRdiJiI3k/2U6SEbAETB3YtBrUkCFA==} + '@xmldom/xmldom@0.8.11': + resolution: {integrity: sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw==} + engines: {node: '>=10.0.0'} + '@xmldom/xmldom@0.9.8': resolution: {integrity: sha512-p96FSY54r+WJ50FIOsCOjyj/wavs8921hG5+kVMmZgKcvIKxMXHTrjNJvRgWa/zuX3B6t2lijLNFaOyuxUH+2A==} engines: {node: '>=14.6'} @@ -807,45 +1238,171 @@ packages: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} - balanced-match@4.0.4: - resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} - engines: {node: 18 || 20 || >=22} + babel-helper-evaluate-path@0.5.0: + resolution: {integrity: sha512-mUh0UhS607bGh5wUMAQfOpt2JX2ThXMtppHRdRU1kL7ZLRWIXxoV2UIV1r2cAeeNeU1M5SB5/RSUgUxrK8yOkA==} - baseline-browser-mapping@2.10.0: - resolution: {integrity: sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==} - engines: {node: '>=6.0.0'} - hasBin: true + babel-helper-flip-expressions@0.4.3: + resolution: {integrity: sha512-rSrkRW4YQ2ETCWww9gbsWk4N0x1BOtln349Tk0dlCS90oT68WMLyGR7WvaMp3eAnsVrCqdUtC19lo1avyGPejA==} - basic-auth-parser@0.0.2-1: - resolution: {integrity: sha512-GFj8iVxo9onSU6BnnQvVwqvxh60UcSHJEDnIk3z4B6iOjsKSmqe+ibW0Rsz7YO7IE1HG3D3tqCNIidP46SZVdQ==} + babel-helper-is-nodes-equiv@0.0.1: + resolution: {integrity: sha512-ri/nsMFVRqXn7IyT5qW4/hIAGQxuYUFHa3qsxmPtbk6spZQcYlyDogfVpNm2XYOslH/ULS4VEJGUqQX5u7ACQw==} - brace-expansion@5.0.4: - resolution: {integrity: sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==} - engines: {node: 18 || 20 || >=22} + babel-helper-is-void-0@0.4.3: + resolution: {integrity: sha512-07rBV0xPRM3TM5NVJEOQEkECX3qnHDjaIbFvWYPv+T1ajpUiVLiqTfC+MmiZxY5KOL/Ec08vJdJD9kZiP9UkUg==} - browserslist@4.28.1: - resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} - hasBin: true + babel-helper-mark-eval-scopes@0.4.3: + resolution: {integrity: sha512-+d/mXPP33bhgHkdVOiPkmYoeXJ+rXRWi7OdhwpyseIqOS8CmzHQXHUp/+/Qr8baXsT0kjGpMHHofHs6C3cskdA==} - buffer-crc32@0.2.13: - resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} + babel-helper-remove-or-void@0.4.3: + resolution: {integrity: sha512-eYNceYtcGKpifHDir62gHJadVXdg9fAhuZEXiRQnJJ4Yi4oUTpqpNY//1pM4nVyjjDMPYaC2xSf0I+9IqVzwdA==} - c8@11.0.0: - resolution: {integrity: sha512-e/uRViGHSVIJv7zsaDKM7VRn2390TgHXqUSvYwPHBQaU6L7E9L0n9JbdkwdYPvshDT0KymBmmlwSpms3yBaMNg==} - engines: {node: 20 || >=22} - hasBin: true - peerDependencies: - monocart-coverage-reports: ^2 - peerDependenciesMeta: - monocart-coverage-reports: - optional: true + babel-helper-to-multiple-sequence-expressions@0.5.0: + resolution: {integrity: sha512-m2CvfDW4+1qfDdsrtf4dwOslQC3yhbgyBFptncp4wvtdrDHqueW7slsYv4gArie056phvQFhT2nRcGS4bnm6mA==} - camelcase@5.0.0: - resolution: {integrity: sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==} - engines: {node: '>=6'} + babel-plugin-minify-builtins@0.5.0: + resolution: {integrity: sha512-wpqbN7Ov5hsNwGdzuzvFcjgRlzbIeVv1gMIlICbPj0xkexnfoIDe7q+AZHMkQmAE/F9R5jkrB6TLfTegImlXag==} - caniuse-lite@1.0.30001777: + babel-plugin-minify-constant-folding@0.5.0: + resolution: {integrity: sha512-Vj97CTn/lE9hR1D+jKUeHfNy+m1baNiJ1wJvoGyOBUx7F7kJqDZxr9nCHjO/Ad+irbR3HzR6jABpSSA29QsrXQ==} + + babel-plugin-minify-dead-code-elimination@0.5.2: + resolution: {integrity: sha512-krq9Lwi0QIzyAlcNBXTL4usqUvevB4BzktdEsb8srcXC1AaYqRJiAQw6vdKdJSaXbz6snBvziGr6ch/aoRCfpA==} + + babel-plugin-minify-flip-comparisons@0.4.3: + resolution: {integrity: sha512-8hNwgLVeJzpeLVOVArag2DfTkbKodzOHU7+gAZ8mGBFGPQHK6uXVpg3jh5I/F6gfi5Q5usWU2OKcstn1YbAV7A==} + + babel-plugin-minify-guarded-expressions@0.4.4: + resolution: {integrity: sha512-RMv0tM72YuPPfLT9QLr3ix9nwUIq+sHT6z8Iu3sLbqldzC1Dls8DPCywzUIzkTx9Zh1hWX4q/m9BPoPed9GOfA==} + + babel-plugin-minify-infinity@0.4.3: + resolution: {integrity: sha512-X0ictxCk8y+NvIf+bZ1HJPbVZKMlPku3lgYxPmIp62Dp8wdtbMLSekczty3MzvUOlrk5xzWYpBpQprXUjDRyMA==} + + babel-plugin-minify-mangle-names@0.5.1: + resolution: {integrity: sha512-8KMichAOae2FHlipjNDTo2wz97MdEb2Q0jrn4NIRXzHH7SJ3c5TaNNBkeTHbk9WUsMnqpNUx949ugM9NFWewzw==} + + babel-plugin-minify-numeric-literals@0.4.3: + resolution: {integrity: sha512-5D54hvs9YVuCknfWywq0eaYDt7qYxlNwCqW9Ipm/kYeS9gYhJd0Rr/Pm2WhHKJ8DC6aIlDdqSBODSthabLSX3A==} + + babel-plugin-minify-replace@0.5.0: + resolution: {integrity: sha512-aXZiaqWDNUbyNNNpWs/8NyST+oU7QTpK7J9zFEFSA0eOmtUNMU3fczlTTTlnCxHmq/jYNFEmkkSG3DDBtW3Y4Q==} + + babel-plugin-minify-simplify@0.5.1: + resolution: {integrity: sha512-OSYDSnoCxP2cYDMk9gxNAed6uJDiDz65zgL6h8d3tm8qXIagWGMLWhqysT6DY3Vs7Fgq7YUDcjOomhVUb+xX6A==} + + babel-plugin-minify-type-constructors@0.4.3: + resolution: {integrity: sha512-4ADB0irJ/6BeXWHubjCJmrPbzhxDgjphBMjIjxCc25n4NGJ00NsYqwYt+F/OvE9RXx8KaSW7cJvp+iZX436tnQ==} + + babel-plugin-polyfill-corejs2@0.4.16: + resolution: {integrity: sha512-xaVwwSfebXf0ooE11BJovZYKhFjIvQo7TsyVpETuIeH2JHv0k/T6Y5j22pPTvqYqmpkxdlPAJlyJ0tfOJAoMxw==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-polyfill-corejs3@0.14.1: + resolution: {integrity: sha512-ENp89vM9Pw4kv/koBb5N2f9bDZsR0hpf3BdPMOg/pkS3pwO4dzNnQZVXtBbeyAadgm865DmQG2jMMLqmZXvuCw==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-polyfill-regenerator@0.6.7: + resolution: {integrity: sha512-OTYbUlSwXhNgr4g6efMZgsO8//jA61P7ZbRX3iTT53VON8l+WQS8IAUEVo4a4cWknrg2W8Cj4gQhRYNCJ8GkAA==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-transform-inline-consecutive-adds@0.4.3: + resolution: {integrity: sha512-8D104wbzzI5RlxeVPYeQb9QsUyepiH1rAO5hpPpQ6NPRgQLpIVwkS/Nbx944pm4K8Z+rx7CgjPsFACz/VCBN0Q==} + + babel-plugin-transform-member-expression-literals@6.9.4: + resolution: {integrity: sha512-Xq9/Rarpj+bjOZSl1nBbZYETsNEDDJSrb6Plb1sS3/36FukWFLLRysgecva5KZECjUJTrJoQqjJgtWToaflk5Q==} + + babel-plugin-transform-merge-sibling-variables@6.9.5: + resolution: {integrity: sha512-xj/KrWi6/uP+DrD844h66Qh2cZN++iugEIgH8QcIxhmZZPNP6VpOE9b4gP2FFW39xDAY43kCmYMM6U0QNKN8fw==} + + babel-plugin-transform-minify-booleans@6.9.4: + resolution: {integrity: sha512-9pW9ePng6DZpzGPalcrULuhSCcauGAbn8AeU3bE34HcDkGm8Ldt0ysjGkyb64f0K3T5ilV4mriayOVv5fg0ASA==} + + babel-plugin-transform-property-literals@6.9.4: + resolution: {integrity: sha512-Pf8JHTjTPxecqVyL6KSwD/hxGpoTZjiEgV7nCx0KFQsJYM0nuuoCajbg09KRmZWeZbJ5NGTySABYv8b/hY1eEA==} + + babel-plugin-transform-regexp-constructors@0.4.3: + resolution: {integrity: sha512-JjymDyEyRNhAoNFp09y/xGwYVYzT2nWTGrBrWaL6eCg2m+B24qH2jR0AA8V8GzKJTgC8NW6joJmc6nabvWBD/g==} + + babel-plugin-transform-remove-console@6.9.4: + resolution: {integrity: sha512-88blrUrMX3SPiGkT1GnvVY8E/7A+k6oj3MNvUtTIxJflFzXTw1bHkuJ/y039ouhFMp2prRn5cQGzokViYi1dsg==} + + babel-plugin-transform-remove-debugger@6.9.4: + resolution: {integrity: sha512-Kd+eTBYlXfwoFzisburVwrngsrz4xh9I0ppoJnU/qlLysxVBRgI4Pj+dk3X8F5tDiehp3hhP8oarRMT9v2Z3lw==} + + babel-plugin-transform-remove-undefined@0.5.0: + resolution: {integrity: sha512-+M7fJYFaEE/M9CXa0/IRkDbiV3wRELzA1kKQFCJ4ifhrzLKn/9VCCgj9OFmYWwBd8IB48YdgPkHYtbYq+4vtHQ==} + + babel-plugin-transform-simplify-comparison-operators@6.9.4: + resolution: {integrity: sha512-GLInxhGAQWJ9YIdjwF6dAFlmh4U+kN8pL6Big7nkDzHoZcaDQOtBm28atEhQJq6m9GpAovbiGEShKqXv4BSp0A==} + + babel-plugin-transform-titanium@0.1.1: + resolution: {integrity: sha512-N2ImhDNsfmT5Q68HeNJfg1xE8Z3NsVYWC+/TWLtckscXPjleDJRciIRzhUhI6876VXhxSjbw5s7ylv1NTa/xoA==} + + babel-plugin-transform-undefined-to-void@6.9.4: + resolution: {integrity: sha512-D2UbwxawEY1xVc9svYAUZQM2xarwSNXue2qDIx6CeV2EuMGaes/0su78zlIDIAgE7BvnMw4UpmSo9fDy+znghg==} + + babel-preset-minify@0.5.2: + resolution: {integrity: sha512-v4GL+kk0TfovbRIKZnC3HPbu2cAGmPAby7BsOmuPdMJfHV+4FVdsGXTH/OOGQRKYdjemBuL1+MsE6mobobhe9w==} + + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + baseline-browser-mapping@2.10.0: + resolution: {integrity: sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==} + engines: {node: '>=6.0.0'} + hasBin: true + + basic-auth-parser@0.0.2-1: + resolution: {integrity: sha512-GFj8iVxo9onSU6BnnQvVwqvxh60UcSHJEDnIk3z4B6iOjsKSmqe+ibW0Rsz7YO7IE1HG3D3tqCNIidP46SZVdQ==} + + big-integer@1.6.52: + resolution: {integrity: sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==} + engines: {node: '>=0.6'} + + bplist-creator@0.1.0: + resolution: {integrity: sha512-sXaHZicyEEmY86WyueLTQesbeoH/mquvarJaQNbjuOQO+7gbFcDEWqKmcWA4cOTLzFlfgvkiVxolk1k5bBIpmg==} + + bplist-parser@0.3.1: + resolution: {integrity: sha512-PyJxiNtA5T2PlLIeBot4lbp7rj4OadzjnMZD/G5zuBNt8ei/yCU7+wW0h2bag9vr8c+/WuRWmSxbqAl9hL1rBA==} + engines: {node: '>= 5.10.0'} + + brace-expansion@5.0.4: + resolution: {integrity: sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==} + engines: {node: 18 || 20 || >=22} + + browserslist@4.28.1: + resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + buffer-crc32@0.2.13: + resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} + + buffers@0.1.1: + resolution: {integrity: sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==} + engines: {node: '>=0.2.0'} + + c8@11.0.0: + resolution: {integrity: sha512-e/uRViGHSVIJv7zsaDKM7VRn2390TgHXqUSvYwPHBQaU6L7E9L0n9JbdkwdYPvshDT0KymBmmlwSpms3yBaMNg==} + engines: {node: 20 || >=22} + hasBin: true + peerDependencies: + monocart-coverage-reports: ^2 + peerDependenciesMeta: + monocart-coverage-reports: + optional: true + + camelcase@5.0.0: + resolution: {integrity: sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==} + engines: {node: '>=6'} + + caniuse-lite@1.0.30001777: resolution: {integrity: sha512-tmN+fJxroPndC74efCdp12j+0rk0RHwV5Jwa1zWaFVyw2ZxAuPeG8ZgWC3Wz7uSjT3qMRQ5XHZ4COgQmsCMJAQ==} chai@6.2.2: @@ -884,6 +1441,9 @@ packages: convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + core-js-compat@3.48.0: + resolution: {integrity: sha512-OM4cAF3D6VtH/WkLtWvyNC56EZVXsZdU3iqaMG2B4WvYrlqU831pc4UtG5yp0sE9z8Y02wVN7PjW5Zf9Gt0f1Q==} + core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} @@ -929,6 +1489,10 @@ packages: estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + execa@9.6.1: resolution: {integrity: sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA==} engines: {node: ^18.19.0 || >=20.5.0} @@ -963,6 +1527,9 @@ packages: engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + gensync@1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} @@ -995,6 +1562,10 @@ packages: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + html-escaper@2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} @@ -1005,6 +1576,10 @@ packages: inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + engines: {node: '>= 0.4'} + is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} @@ -1072,6 +1647,12 @@ packages: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} + lodash.debounce@4.0.8: + resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} + + lodash@4.17.23: + resolution: {integrity: sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==} + lru-cache@11.2.6: resolution: {integrity: sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==} engines: {node: 20 || >=22} @@ -1115,6 +1696,10 @@ packages: node-releases@2.0.36: resolution: {integrity: sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==} + node-titanium-sdk@https://github.com/tidev/node-titanium-sdk/archive/refs/heads/v7.tar.gz: + resolution: {integrity: sha512-8/kdpf3Zd3tQft03sepXxY+oqFfKD+agHWcIQengU3g6RhfCsQR09KLo/1HUwHrRuFV2PEgNa7wvArlPnx2Rkw==, tarball: https://github.com/tidev/node-titanium-sdk/archive/refs/heads/v7.tar.gz} + version: 7.0.0 + npm-run-path@6.0.0: resolution: {integrity: sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==} engines: {node: '>=18'} @@ -1161,6 +1746,9 @@ packages: resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} engines: {node: '>=12'} + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + path-scurry@2.0.2: resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==} engines: {node: 18 || 20 || >=22} @@ -1178,6 +1766,10 @@ packages: resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} engines: {node: '>=12'} + plist@3.1.0: + resolution: {integrity: sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==} + engines: {node: '>=10.4.0'} + postcss@8.5.8: resolution: {integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==} engines: {node: ^10 || ^12 || >=14} @@ -1202,10 +1794,33 @@ packages: readable-stream@1.0.34: resolution: {integrity: sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==} + regenerate-unicode-properties@10.2.2: + resolution: {integrity: sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==} + engines: {node: '>=4'} + + regenerate@1.4.2: + resolution: {integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==} + + regexpu-core@6.4.0: + resolution: {integrity: sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==} + engines: {node: '>=4'} + + regjsgen@0.8.0: + resolution: {integrity: sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==} + + regjsparser@0.13.0: + resolution: {integrity: sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q==} + hasBin: true + require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} + resolve@1.22.11: + resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} + engines: {node: '>= 0.4'} + hasBin: true + rollup@4.59.0: resolution: {integrity: sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -1235,9 +1850,16 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} + simple-plist@1.3.1: + resolution: {integrity: sha512-iMSw5i0XseMnrhtIzRb7XpQEXepa9xhWxGUojHBL43SIpQuDQkh3Wpy67ZbDzZVr6EKxvwVChnVpdl8hEVLDiw==} + sisteransi@1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + snooplogg@6.1.1: + resolution: {integrity: sha512-ERUf0zDe+4/wdzoTlz9aj45PCXRO/jckRY6BwlGLl4dZVr8hanHEW13MSYLSqOz9aDbRR8PGnb4arak+Nh4VqQ==} + engines: {node: '>=18.17'} + source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} @@ -1252,6 +1874,13 @@ packages: std-env@3.10.0: resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + stream-buffers@2.2.0: + resolution: {integrity: sha512-uyQK/mx5QjHun80FLJTfaWE7JtwfRMKBLkMne6udYOmvH0CawotVa7TfgYHzAnpphn4+TweIx1QKMnRIbipmUg==} + engines: {node: '>= 0.10.0'} + + stream-splitter@0.3.2: + resolution: {integrity: sha512-9VAHJIhskQFJMbyKbf/5flSXV2HsP9MDFdCp3A8WDBWkZ8tP/SOfkI2c5lEHNNUNzbWdNkJEv6iNvQRJnSbYuA==} + string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -1283,6 +1912,10 @@ packages: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + test-exclude@8.0.0: resolution: {integrity: sha512-ZOffsNrXYggvU1mDGHk54I96r26P8SyMjO5slMKSc7+IWmtB/MQKnEC2fP51imB3/pT6YK5cT5E8f+Dd9KdyOQ==} engines: {node: 20 || >=22} @@ -1318,6 +1951,22 @@ packages: resolution: {integrity: sha512-RqslV2Us5BrllB+JeiZnK4peryVTndy9Dnqq62S3yYRRTj0tFQCwEniUy2167skdGOy3vqRzEvl1Dm4sV2ReDg==} engines: {node: '>=20.18.1'} + unicode-canonical-property-names-ecmascript@2.0.1: + resolution: {integrity: sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==} + engines: {node: '>=4'} + + unicode-match-property-ecmascript@2.0.0: + resolution: {integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==} + engines: {node: '>=4'} + + unicode-match-property-value-ecmascript@2.2.1: + resolution: {integrity: sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==} + engines: {node: '>=4'} + + unicode-property-aliases-ecmascript@2.2.0: + resolution: {integrity: sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==} + engines: {node: '>=4'} + unicorn-magic@0.3.0: resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} engines: {node: '>=18'} @@ -1429,6 +2078,10 @@ packages: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} + xmlbuilder@15.1.1: + resolution: {integrity: sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==} + engines: {node: '>=8.0'} + xpath@0.0.34: resolution: {integrity: sha512-FxF6+rkr1rNSQrhUNYrAFJpRXNzlDoMxeXN5qI84939ylEv3qqPFKa85Oxr6tDaJKqwW6KKyo2v26TSv3k6LeA==} engines: {node: '>=0.6.0'} @@ -1456,104 +2109,659 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} - yoctocolors@2.1.2: - resolution: {integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==} - engines: {node: '>=18'} + yoctocolors@2.1.2: + resolution: {integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==} + engines: {node: '>=18'} + + zod@4.3.6: + resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==} + +snapshots: + + '@actions/core@2.0.3': + dependencies: + '@actions/exec': 2.0.0 + '@actions/http-client': 3.0.2 + + '@actions/exec@2.0.0': + dependencies: + '@actions/io': 2.0.0 + + '@actions/http-client@3.0.2': + dependencies: + tunnel: 0.0.6 + undici: 6.23.0 + + '@actions/io@2.0.0': {} + + '@babel/code-frame@7.29.0': + dependencies: + '@babel/helper-validator-identifier': 7.28.5 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.29.0': {} + + '@babel/core@7.29.0': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@babel/helpers': 7.28.6 + '@babel/parser': 7.29.0 + '@babel/template': 7.28.6 + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.29.1': + dependencies: + '@babel/parser': 7.29.0 + '@babel/types': 7.29.0 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-annotate-as-pure@7.27.3': + dependencies: + '@babel/types': 7.29.0 + + '@babel/helper-compilation-targets@7.28.6': + dependencies: + '@babel/compat-data': 7.29.0 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.28.1 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-create-class-features-plugin@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-member-expression-to-functions': 7.28.5 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/helper-replace-supers': 7.28.6(@babel/core@7.29.0) + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/traverse': 7.29.0 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/helper-create-regexp-features-plugin@7.28.5(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-annotate-as-pure': 7.27.3 + regexpu-core: 6.4.0 + semver: 6.3.1 + + '@babel/helper-define-polyfill-provider@0.6.7(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + debug: 4.4.3 + lodash.debounce: 4.0.8 + resolve: 1.22.11 + transitivePeerDependencies: + - supports-color + + '@babel/helper-globals@7.28.0': {} + + '@babel/helper-member-expression-to-functions@7.28.5': + dependencies: + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-imports@7.28.6': + dependencies: + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-module-imports': 7.28.6 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-optimise-call-expression@7.27.1': + dependencies: + '@babel/types': 7.29.0 + + '@babel/helper-plugin-utils@7.28.6': {} + + '@babel/helper-remap-async-to-generator@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-wrap-function': 7.28.6 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-replace-supers@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-member-expression-to-functions': 7.28.5 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-skip-transparent-expression-wrappers@7.27.1': + dependencies: + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.28.5': {} + + '@babel/helper-validator-option@7.27.1': {} + + '@babel/helper-wrap-function@7.28.6': + dependencies: + '@babel/template': 7.28.6 + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helpers@7.28.6': + dependencies: + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + + '@babel/parser@7.29.0': + dependencies: + '@babel/types': 7.29.0 + + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/plugin-transform-optional-chaining': 7.28.6(@babel/core@7.29.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + + '@babel/plugin-syntax-import-assertions@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-import-attributes@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-arrow-functions@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-async-generator-functions@7.29.0(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.29.0) + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-async-to-generator@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-module-imports': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.29.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-block-scoped-functions@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-block-scoping@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-class-properties@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-class-static-block@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-classes@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-globals': 7.28.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-replace-supers': 7.28.6(@babel/core@7.29.0) + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-computed-properties@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/template': 7.28.6 + + '@babel/plugin-transform-destructuring@7.28.5(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-dotall-regex@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-duplicate-keys@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.29.0(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-dynamic-import@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-explicit-resource-management@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.29.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-exponentiation-operator@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-export-namespace-from@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-for-of@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-function-name@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-json-strings@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-literals@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-logical-assignment-operators@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-member-expression-literals@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-modules-amd@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color -snapshots: + '@babel/plugin-transform-modules-commonjs@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color - '@actions/core@2.0.3': + '@babel/plugin-transform-modules-systemjs@7.29.0(@babel/core@7.29.0)': dependencies: - '@actions/exec': 2.0.0 - '@actions/http-client': 3.0.2 + '@babel/core': 7.29.0 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color - '@actions/exec@2.0.0': + '@babel/plugin-transform-modules-umd@7.27.1(@babel/core@7.29.0)': dependencies: - '@actions/io': 2.0.0 + '@babel/core': 7.29.0 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color - '@actions/http-client@3.0.2': + '@babel/plugin-transform-named-capturing-groups-regex@7.29.0(@babel/core@7.29.0)': dependencies: - tunnel: 0.0.6 - undici: 6.23.0 + '@babel/core': 7.29.0 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 - '@actions/io@2.0.0': {} + '@babel/plugin-transform-new-target@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/code-frame@7.29.0': + '@babel/plugin-transform-nullish-coalescing-operator@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/helper-validator-identifier': 7.28.5 - js-tokens: 4.0.0 - picocolors: 1.1.1 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/compat-data@7.29.0': {} + '@babel/plugin-transform-numeric-separator@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/core@7.29.0': + '@babel/plugin-transform-object-rest-spread@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/code-frame': 7.29.0 - '@babel/generator': 7.29.1 + '@babel/core': 7.29.0 '@babel/helper-compilation-targets': 7.28.6 - '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) - '@babel/helpers': 7.28.6 - '@babel/parser': 7.29.0 - '@babel/template': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.29.0) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.29.0) '@babel/traverse': 7.29.0 - '@babel/types': 7.29.0 - '@jridgewell/remapping': 2.3.5 - convert-source-map: 2.0.0 - debug: 4.4.3 - gensync: 1.0.0-beta.2 - json5: 2.2.3 - semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/generator@7.29.1': + '@babel/plugin-transform-object-super@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/parser': 7.29.0 - '@babel/types': 7.29.0 - '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.31 - jsesc: 3.1.0 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-replace-supers': 7.28.6(@babel/core@7.29.0) + transitivePeerDependencies: + - supports-color - '@babel/helper-compilation-targets@7.28.6': + '@babel/plugin-transform-optional-catch-binding@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/compat-data': 7.29.0 - '@babel/helper-validator-option': 7.27.1 - browserslist: 4.28.1 - lru-cache: 5.1.1 - semver: 6.3.1 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-globals@7.28.0': {} + '@babel/plugin-transform-optional-chaining@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + transitivePeerDependencies: + - supports-color - '@babel/helper-module-imports@7.28.6': + '@babel/plugin-transform-parameters@7.27.7(@babel/core@7.29.0)': dependencies: - '@babel/traverse': 7.29.0 - '@babel/types': 7.29.0 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-private-methods@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-private-property-in-object@7.28.6(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 - '@babel/helper-module-imports': 7.28.6 - '@babel/helper-validator-identifier': 7.28.5 - '@babel/traverse': 7.29.0 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 transitivePeerDependencies: - supports-color - '@babel/helper-string-parser@7.27.1': {} + '@babel/plugin-transform-property-literals@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-validator-identifier@7.28.5': {} + '@babel/plugin-transform-regenerator@7.29.0(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-validator-option@7.27.1': {} + '@babel/plugin-transform-regexp-modifiers@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 - '@babel/helpers@7.28.6': + '@babel/plugin-transform-reserved-words@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/template': 7.28.6 - '@babel/types': 7.29.0 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/parser@7.29.0': + '@babel/plugin-transform-shorthand-properties@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-spread@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-sticky-regex@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-template-literals@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-typeof-symbol@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-unicode-escapes@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-unicode-property-regex@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-unicode-regex@7.27.1(@babel/core@7.29.0)': dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-unicode-sets-regex@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/preset-env@7.29.0(@babel/core@7.29.0)': + dependencies: + '@babel/compat-data': 7.29.0 + '@babel/core': 7.29.0 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-validator-option': 7.27.1 + '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.28.5(@babel/core@7.29.0) + '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.29.0) + '@babel/plugin-syntax-import-assertions': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-syntax-import-attributes': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.29.0) + '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-async-generator-functions': 7.29.0(@babel/core@7.29.0) + '@babel/plugin-transform-async-to-generator': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-block-scoped-functions': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-block-scoping': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-class-properties': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-class-static-block': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-classes': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-computed-properties': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.29.0) + '@babel/plugin-transform-dotall-regex': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-duplicate-keys': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.29.0(@babel/core@7.29.0) + '@babel/plugin-transform-dynamic-import': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-explicit-resource-management': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-exponentiation-operator': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-export-namespace-from': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-for-of': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-function-name': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-json-strings': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-literals': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-logical-assignment-operators': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-member-expression-literals': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-modules-amd': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-modules-systemjs': 7.29.0(@babel/core@7.29.0) + '@babel/plugin-transform-modules-umd': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-named-capturing-groups-regex': 7.29.0(@babel/core@7.29.0) + '@babel/plugin-transform-new-target': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-nullish-coalescing-operator': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-numeric-separator': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-object-rest-spread': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-object-super': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-optional-catch-binding': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-optional-chaining': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.29.0) + '@babel/plugin-transform-private-methods': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-private-property-in-object': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-property-literals': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-regenerator': 7.29.0(@babel/core@7.29.0) + '@babel/plugin-transform-regexp-modifiers': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-reserved-words': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-spread': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-sticky-regex': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-template-literals': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-typeof-symbol': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-unicode-escapes': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-unicode-property-regex': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-unicode-sets-regex': 7.28.6(@babel/core@7.29.0) + '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.29.0) + babel-plugin-polyfill-corejs2: 0.4.16(@babel/core@7.29.0) + babel-plugin-polyfill-corejs3: 0.14.1(@babel/core@7.29.0) + babel-plugin-polyfill-regenerator: 0.6.7(@babel/core@7.29.0) + core-js-compat: 3.48.0 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 '@babel/types': 7.29.0 + esutils: 2.0.3 '@babel/template@7.28.6': dependencies: @@ -1945,6 +3153,8 @@ snapshots: '@vitest/pretty-format': 4.0.18 tinyrainbow: 3.0.3 + '@xmldom/xmldom@0.8.11': {} + '@xmldom/xmldom@0.9.8': {} ansi-regex@5.0.1: {} @@ -1970,12 +3180,159 @@ snapshots: assertion-error@2.0.1: {} + babel-helper-evaluate-path@0.5.0: {} + + babel-helper-flip-expressions@0.4.3: {} + + babel-helper-is-nodes-equiv@0.0.1: {} + + babel-helper-is-void-0@0.4.3: {} + + babel-helper-mark-eval-scopes@0.4.3: {} + + babel-helper-remove-or-void@0.4.3: {} + + babel-helper-to-multiple-sequence-expressions@0.5.0: {} + + babel-plugin-minify-builtins@0.5.0: {} + + babel-plugin-minify-constant-folding@0.5.0: + dependencies: + babel-helper-evaluate-path: 0.5.0 + + babel-plugin-minify-dead-code-elimination@0.5.2: + dependencies: + babel-helper-evaluate-path: 0.5.0 + babel-helper-mark-eval-scopes: 0.4.3 + babel-helper-remove-or-void: 0.4.3 + lodash: 4.17.23 + + babel-plugin-minify-flip-comparisons@0.4.3: + dependencies: + babel-helper-is-void-0: 0.4.3 + + babel-plugin-minify-guarded-expressions@0.4.4: + dependencies: + babel-helper-evaluate-path: 0.5.0 + babel-helper-flip-expressions: 0.4.3 + + babel-plugin-minify-infinity@0.4.3: {} + + babel-plugin-minify-mangle-names@0.5.1: + dependencies: + babel-helper-mark-eval-scopes: 0.4.3 + + babel-plugin-minify-numeric-literals@0.4.3: {} + + babel-plugin-minify-replace@0.5.0: {} + + babel-plugin-minify-simplify@0.5.1: + dependencies: + babel-helper-evaluate-path: 0.5.0 + babel-helper-flip-expressions: 0.4.3 + babel-helper-is-nodes-equiv: 0.0.1 + babel-helper-to-multiple-sequence-expressions: 0.5.0 + + babel-plugin-minify-type-constructors@0.4.3: + dependencies: + babel-helper-is-void-0: 0.4.3 + + babel-plugin-polyfill-corejs2@0.4.16(@babel/core@7.29.0): + dependencies: + '@babel/compat-data': 7.29.0 + '@babel/core': 7.29.0 + '@babel/helper-define-polyfill-provider': 0.6.7(@babel/core@7.29.0) + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-corejs3@0.14.1(@babel/core@7.29.0): + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-define-polyfill-provider': 0.6.7(@babel/core@7.29.0) + core-js-compat: 3.48.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-regenerator@0.6.7(@babel/core@7.29.0): + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-define-polyfill-provider': 0.6.7(@babel/core@7.29.0) + transitivePeerDependencies: + - supports-color + + babel-plugin-transform-inline-consecutive-adds@0.4.3: {} + + babel-plugin-transform-member-expression-literals@6.9.4: {} + + babel-plugin-transform-merge-sibling-variables@6.9.5: {} + + babel-plugin-transform-minify-booleans@6.9.4: {} + + babel-plugin-transform-property-literals@6.9.4: + dependencies: + esutils: 2.0.3 + + babel-plugin-transform-regexp-constructors@0.4.3: {} + + babel-plugin-transform-remove-console@6.9.4: {} + + babel-plugin-transform-remove-debugger@6.9.4: {} + + babel-plugin-transform-remove-undefined@0.5.0: + dependencies: + babel-helper-evaluate-path: 0.5.0 + + babel-plugin-transform-simplify-comparison-operators@6.9.4: {} + + babel-plugin-transform-titanium@0.1.1: {} + + babel-plugin-transform-undefined-to-void@6.9.4: {} + + babel-preset-minify@0.5.2: + dependencies: + babel-plugin-minify-builtins: 0.5.0 + babel-plugin-minify-constant-folding: 0.5.0 + babel-plugin-minify-dead-code-elimination: 0.5.2 + babel-plugin-minify-flip-comparisons: 0.4.3 + babel-plugin-minify-guarded-expressions: 0.4.4 + babel-plugin-minify-infinity: 0.4.3 + babel-plugin-minify-mangle-names: 0.5.1 + babel-plugin-minify-numeric-literals: 0.4.3 + babel-plugin-minify-replace: 0.5.0 + babel-plugin-minify-simplify: 0.5.1 + babel-plugin-minify-type-constructors: 0.4.3 + babel-plugin-transform-inline-consecutive-adds: 0.4.3 + babel-plugin-transform-member-expression-literals: 6.9.4 + babel-plugin-transform-merge-sibling-variables: 6.9.5 + babel-plugin-transform-minify-booleans: 6.9.4 + babel-plugin-transform-property-literals: 6.9.4 + babel-plugin-transform-regexp-constructors: 0.4.3 + babel-plugin-transform-remove-console: 6.9.4 + babel-plugin-transform-remove-debugger: 6.9.4 + babel-plugin-transform-remove-undefined: 0.5.0 + babel-plugin-transform-simplify-comparison-operators: 6.9.4 + babel-plugin-transform-undefined-to-void: 6.9.4 + lodash: 4.17.23 + balanced-match@4.0.4: {} + base64-js@1.5.1: {} + baseline-browser-mapping@2.10.0: {} basic-auth-parser@0.0.2-1: {} + big-integer@1.6.52: {} + + bplist-creator@0.1.0: + dependencies: + stream-buffers: 2.2.0 + + bplist-parser@0.3.1: + dependencies: + big-integer: 1.6.52 + brace-expansion@5.0.4: dependencies: balanced-match: 4.0.4 @@ -1990,6 +3347,8 @@ snapshots: buffer-crc32@0.2.13: {} + buffers@0.1.1: {} + c8@11.0.0: dependencies: '@bcoe/v8-coverage': 1.0.2 @@ -2040,6 +3399,10 @@ snapshots: convert-source-map@2.0.0: {} + core-js-compat@3.48.0: + dependencies: + browserslist: 4.28.1 + core-util-is@1.0.3: {} cross-spawn@7.0.6: @@ -2097,6 +3460,8 @@ snapshots: dependencies: '@types/estree': 1.0.8 + esutils@2.0.3: {} + execa@9.6.1: dependencies: '@sindresorhus/merge-streams': 4.0.0 @@ -2135,6 +3500,8 @@ snapshots: fsevents@2.3.3: optional: true + function-bind@1.1.2: {} + gensync@1.0.0-beta.2: {} get-caller-file@2.0.5: {} @@ -2158,12 +3525,20 @@ snapshots: has-flag@4.0.0: {} + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + html-escaper@2.0.2: {} human-signals@8.0.1: {} inherits@2.0.4: {} + is-core-module@2.16.1: + dependencies: + hasown: 2.0.2 + is-fullwidth-code-point@3.0.0: {} is-plain-obj@4.1.0: {} @@ -2215,6 +3590,10 @@ snapshots: dependencies: p-locate: 5.0.0 + lodash.debounce@4.0.8: {} + + lodash@4.17.23: {} + lru-cache@11.2.6: {} lru-cache@5.1.1: @@ -2253,6 +3632,26 @@ snapshots: node-releases@2.0.36: {} + node-titanium-sdk@https://github.com/tidev/node-titanium-sdk/archive/refs/heads/v7.tar.gz: + dependencies: + '@babel/core': 7.29.0 + '@babel/parser': 7.29.0 + '@babel/plugin-transform-property-literals': 7.27.1(@babel/core@7.29.0) + '@babel/preset-env': 7.29.0(@babel/core@7.29.0) + '@xmldom/xmldom': 0.8.11 + babel-plugin-transform-titanium: 0.1.1 + babel-preset-minify: 0.5.2 + semver: 7.7.4 + simple-plist: 1.3.1 + snooplogg: 6.1.1 + stream-splitter: 0.3.2 + undici: 7.22.0 + which: 6.0.1 + yauzl: 3.2.1 + zod: 4.3.6 + transitivePeerDependencies: + - supports-color + npm-run-path@6.0.0: dependencies: path-key: 4.0.0 @@ -2322,6 +3721,8 @@ snapshots: path-key@4.0.0: {} + path-parse@1.0.7: {} + path-scurry@2.0.2: dependencies: lru-cache: 11.2.6 @@ -2335,6 +3736,12 @@ snapshots: picomatch@4.0.3: {} + plist@3.1.0: + dependencies: + '@xmldom/xmldom': 0.8.11 + base64-js: 1.5.1 + xmlbuilder: 15.1.1 + postcss@8.5.8: dependencies: nanoid: 3.3.11 @@ -2367,8 +3774,35 @@ snapshots: isarray: 0.0.1 string_decoder: 0.10.31 + regenerate-unicode-properties@10.2.2: + dependencies: + regenerate: 1.4.2 + + regenerate@1.4.2: {} + + regexpu-core@6.4.0: + dependencies: + regenerate: 1.4.2 + regenerate-unicode-properties: 10.2.2 + regjsgen: 0.8.0 + regjsparser: 0.13.0 + unicode-match-property-ecmascript: 2.0.0 + unicode-match-property-value-ecmascript: 2.2.1 + + regjsgen@0.8.0: {} + + regjsparser@0.13.0: + dependencies: + jsesc: 3.1.0 + require-directory@2.1.1: {} + resolve@1.22.11: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + rollup@4.59.0: dependencies: '@types/estree': 1.0.8 @@ -2414,8 +3848,16 @@ snapshots: signal-exit@4.1.0: {} + simple-plist@1.3.1: + dependencies: + bplist-creator: 0.1.0 + bplist-parser: 0.3.1 + plist: 3.1.0 + sisteransi@1.0.5: {} + snooplogg@6.1.1: {} + source-map-js@1.2.1: {} stack-utils@2.0.6: @@ -2426,6 +3868,12 @@ snapshots: std-env@3.10.0: {} + stream-buffers@2.2.0: {} + + stream-splitter@0.3.2: + dependencies: + buffers: 0.1.1 + string-width@4.2.3: dependencies: emoji-regex: 8.0.0 @@ -2457,6 +3905,8 @@ snapshots: dependencies: has-flag: 4.0.0 + supports-preserve-symlinks-flag@1.0.0: {} + test-exclude@8.0.0: dependencies: '@istanbuljs/schema': 0.1.3 @@ -2482,6 +3932,17 @@ snapshots: undici@7.22.0: {} + unicode-canonical-property-names-ecmascript@2.0.1: {} + + unicode-match-property-ecmascript@2.0.0: + dependencies: + unicode-canonical-property-names-ecmascript: 2.0.1 + unicode-property-aliases-ecmascript: 2.2.0 + + unicode-match-property-value-ecmascript@2.2.1: {} + + unicode-property-aliases-ecmascript@2.2.0: {} + unicorn-magic@0.3.0: {} update-browserslist-db@1.2.3(browserslist@4.28.1): @@ -2567,6 +4028,8 @@ snapshots: string-width: 4.2.3 strip-ansi: 6.0.1 + xmlbuilder@15.1.1: {} + xpath@0.0.34: {} y18n@5.0.8: {} @@ -2593,3 +4056,5 @@ snapshots: yocto-queue@0.1.0: {} yoctocolors@2.1.2: {} + + zod@4.3.6: {} diff --git a/scripts/build-node-titanium-sdk.js b/scripts/build-node-titanium-sdk.js new file mode 100644 index 000000000..0b45e2442 --- /dev/null +++ b/scripts/build-node-titanium-sdk.js @@ -0,0 +1,28 @@ +#!/usr/bin/env node +/** + * Builds node-titanium-sdk in a temp directory (outside node_modules) because + * tsdown refuses to process config files under node_modules. + */ +import { cpSync, existsSync, mkdirSync, rmSync } from 'node:fs'; +import { tmpdir } from 'node:os'; +import { join } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { execa } from 'execa'; + +const root = join(fileURLToPath(import.meta.url), '..', '..'); +const sdkPath = join(root, 'node_modules', 'node-titanium-sdk'); + +if (!existsSync(sdkPath) || existsSync(join(sdkPath, 'dist'))) { + process.exit(0); +} + +const buildDir = join(tmpdir(), `node-titanium-sdk-build-${process.pid}`); +try { + mkdirSync(buildDir, { recursive: true }); + cpSync(sdkPath, buildDir, { recursive: true, dereference: true }); + await execa('pnpm', ['install'], { cwd: buildDir }); + await execa('pnpm', ['run', 'build'], { cwd: buildDir }); + cpSync(join(buildDir, 'dist'), join(sdkPath, 'dist'), { recursive: true }); +} finally { + rmSync(buildDir, { recursive: true, force: true }); +} From fc717d068c36f749faa7d3a304db811a6d4e735f Mon Sep 17 00:00:00 2001 From: Chris Barber Date: Tue, 10 Mar 2026 23:18:28 -0500 Subject: [PATCH 11/16] Fix windows --- scripts/build-node-titanium-sdk.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/scripts/build-node-titanium-sdk.js b/scripts/build-node-titanium-sdk.js index 0b45e2442..edc30c423 100644 --- a/scripts/build-node-titanium-sdk.js +++ b/scripts/build-node-titanium-sdk.js @@ -3,7 +3,7 @@ * Builds node-titanium-sdk in a temp directory (outside node_modules) because * tsdown refuses to process config files under node_modules. */ -import { cpSync, existsSync, mkdirSync, rmSync } from 'node:fs'; +import { cpSync, existsSync, mkdirSync, readdirSync, realpathSync, rmSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { fileURLToPath } from 'node:url'; @@ -16,10 +16,17 @@ if (!existsSync(sdkPath) || existsSync(join(sdkPath, 'dist'))) { process.exit(0); } +const sdkRealPath = realpathSync(sdkPath); + const buildDir = join(tmpdir(), `node-titanium-sdk-build-${process.pid}`); try { mkdirSync(buildDir, { recursive: true }); - cpSync(sdkPath, buildDir, { recursive: true, dereference: true }); + for (const entry of readdirSync(sdkRealPath, { withFileTypes: true })) { + cpSync(join(sdkRealPath, entry.name), join(buildDir, entry.name), { + recursive: true, + dereference: true, + }); + } await execa('pnpm', ['install'], { cwd: buildDir }); await execa('pnpm', ['run', 'build'], { cwd: buildDir }); cpSync(join(buildDir, 'dist'), join(sdkPath, 'dist'), { recursive: true }); From dd641bee7a8e6972df30b05a9560cea4ac70eb7b Mon Sep 17 00:00:00 2001 From: Chris Barber Date: Wed, 11 Mar 2026 00:04:38 -0500 Subject: [PATCH 12/16] Move to vitest and lefthook --- lefthook.yml | 6 + package.json | 12 +- pnpm-lock.yaml | 161 ++++++++++++++++++++++++++ scripts/build-node-titanium-sdk.js | 2 +- src/cli.js | 2 +- test/cli/hook.test.js | 23 ++-- test/commands/custom.test.js | 25 ++-- test/commands/ti-build.test.js | 15 ++- test/commands/ti-clean.test.js | 15 ++- test/commands/ti-config.test.js | 153 +++++++++++++------------ test/commands/ti-create.test.js | 20 ++-- test/commands/ti-info.test.js | 167 +++++++++++++-------------- test/commands/ti-module.test.js | 41 ++++--- test/commands/ti-project.test.js | 19 ++-- test/commands/ti-sdk.test.js | 176 ++++++++++++++--------------- test/commands/ti-setup.test.js | 15 ++- test/commands/ti.test.js | 23 ++-- test/util/arrayify.test.js | 17 ++- test/util/busyindicator.test.js | 7 +- test/util/columns.test.js | 16 +-- test/util/detect.test.js | 27 +++-- test/util/logger.test.js | 90 +++++++-------- test/util/progress.test.js | 12 +- test/util/proxy.test.js | 15 ++- test/util/ticonfig.test.js | 121 ++++++++------------ test/util/tierror.test.js | 11 +- test/util/unique.test.js | 14 +-- vitest.config.ts | 17 +++ 28 files changed, 667 insertions(+), 555 deletions(-) create mode 100644 lefthook.yml create mode 100644 vitest.config.ts diff --git a/lefthook.yml b/lefthook.yml new file mode 100644 index 000000000..382cc11b1 --- /dev/null +++ b/lefthook.yml @@ -0,0 +1,6 @@ +pre-commit: + parallel: true + commands: + check: + glob: '*.{js,ts,jsx,tsx}' + run: pnpm fmt && pnpm check && git update-index --again diff --git a/package.json b/package.json index f30297d28..87897ba29 100644 --- a/package.json +++ b/package.json @@ -28,13 +28,13 @@ ], "type": "module", "scripts": { - "coverage": "node scripts/test.js --coverage", + "check": "pnpm lint && pnpm fmt:check", + "coverage": "vitest --coverage", "fmt": "oxfmt", "fmt:check": "oxfmt --check", "lint": "oxlint", "postinstall": "node scripts/build-node-titanium-sdk.js", - "test": "node scripts/test.js", - "test-only": "node scripts/test.js --only" + "test": "cross-env CI=1 vitest" }, "dependencies": { "@xmldom/xmldom": "0.9.8", @@ -54,13 +54,17 @@ "devDependencies": { "@reporters/github": "1.12.0", "@vitest/coverage-istanbul": "4.0.18", + "@vitest/coverage-v8": "4.0.18", "c8": "11.0.0", + "cross-env": "10.1.0", "glob": "13.0.6", "globals": "17.4.0", + "lefthook": "2.1.3", "memory-streams": "0.1.3", "oxfmt": "0.37.0", "oxlint": "1.52.0", - "proxy": "2.2.0" + "proxy": "2.2.0", + "vitest": "4.0.18" }, "preferGlobal": true, "engines": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 056a89824..0d8d4e1b3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -54,15 +54,24 @@ importers: '@vitest/coverage-istanbul': specifier: 4.0.18 version: 4.0.18(vitest@4.0.18) + '@vitest/coverage-v8': + specifier: 4.0.18 + version: 4.0.18(vitest@4.0.18) c8: specifier: 11.0.0 version: 11.0.0 + cross-env: + specifier: 10.1.0 + version: 10.1.0 glob: specifier: 13.0.6 version: 13.0.6 globals: specifier: 17.4.0 version: 17.4.0 + lefthook: + specifier: 2.1.3 + version: 2.1.3 memory-streams: specifier: 0.1.3 version: 0.1.3 @@ -75,6 +84,9 @@ importers: proxy: specifier: 2.2.0 version: 2.2.0 + vitest: + specifier: 4.0.18 + version: 4.0.18 packages: @@ -585,6 +597,9 @@ packages: resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} engines: {node: '>=18'} + '@epic-web/invariant@1.0.0': + resolution: {integrity: sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==} + '@esbuild/aix-ppc64@0.27.3': resolution: {integrity: sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==} engines: {node: '>=18'} @@ -1173,6 +1188,15 @@ packages: peerDependencies: vitest: 4.0.18 + '@vitest/coverage-v8@4.0.18': + resolution: {integrity: sha512-7i+N2i0+ME+2JFZhfuz7Tg/FqKtilHjGyGvoHYQ6iLV0zahbsJ9sljC9OcFcPDbhYKCet+sG8SsVqlyGvPflZg==} + peerDependencies: + '@vitest/browser': 4.0.18 + vitest: 4.0.18 + peerDependenciesMeta: + '@vitest/browser': + optional: true + '@vitest/expect@4.0.18': resolution: {integrity: sha512-8sCWUyckXXYvx4opfzVY03EOiYVxyNrHS5QxX3DAIi5dpJAAkyJezHCP77VMX4HKA2LDT/Jpfo8i2r5BE3GnQQ==} @@ -1238,6 +1262,9 @@ packages: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} + ast-v8-to-istanbul@0.3.12: + resolution: {integrity: sha512-BRRC8VRZY2R4Z4lFIL35MwNXmwVqBityvOIwETtsCSwvjl0IdgFsy9NhdaA6j74nUdtJJlIypeRhpDam19Wq3g==} + babel-helper-evaluate-path@0.5.0: resolution: {integrity: sha512-mUh0UhS607bGh5wUMAQfOpt2JX2ThXMtppHRdRU1kL7ZLRWIXxoV2UIV1r2cAeeNeU1M5SB5/RSUgUxrK8yOkA==} @@ -1447,6 +1474,11 @@ packages: core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + cross-env@10.1.0: + resolution: {integrity: sha512-GsYosgnACZTADcmEyJctkJIoqAhHjttw7RsFrVoJNXbsWWqaq6Ym+7kZjq6mS45O0jij6vtiReppKQEtqWy6Dw==} + engines: {node: '>=20'} + hasBin: true + cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -1622,6 +1654,9 @@ packages: resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} engines: {node: '>=8'} + js-tokens@10.0.0: + resolution: {integrity: sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==} + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -1639,6 +1674,60 @@ packages: resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} engines: {node: '>=6'} + lefthook-darwin-arm64@2.1.3: + resolution: {integrity: sha512-VMSQK5ZUh66mKrEpHt5U81BxOg5xAXLoLZIK6e++4uc28tj8zGBqV9+tZqSRElXXzlnHbfdDVCMaKlTuqUy0Rg==} + cpu: [arm64] + os: [darwin] + + lefthook-darwin-x64@2.1.3: + resolution: {integrity: sha512-4QhepF4cf+fa7sDow29IEuCfm/6LuV+oVyQGpnr5it1DEZIEEoa6vdH/x4tutYhAg/HH7I2jHq6FGz96HRiJEQ==} + cpu: [x64] + os: [darwin] + + lefthook-freebsd-arm64@2.1.3: + resolution: {integrity: sha512-kysx/9pjifOgcTZOj1bR0i74FAbMv3BDfrpZDKniBOo4Dp0hXhyOtUmRn4nWKL0bN+cqc4ZePAq4Qdm4fxWafA==} + cpu: [arm64] + os: [freebsd] + + lefthook-freebsd-x64@2.1.3: + resolution: {integrity: sha512-TLuPHQNg6iihShchrh5DrHvoCZO8FajZBMAEwLIKWlm6bkCcXbYNxy4dBaVK8lzHtS/Kv1bnH0D3BcK65iZFVQ==} + cpu: [x64] + os: [freebsd] + + lefthook-linux-arm64@2.1.3: + resolution: {integrity: sha512-e5x4pq1aZAXc0C642V4HaUoKtcHVmGW1HBIDNfWUhtsThBKjhZBXPspecaAHIRA/8VtsXS3RnJ4VhQpgfrCbww==} + cpu: [arm64] + os: [linux] + + lefthook-linux-x64@2.1.3: + resolution: {integrity: sha512-yeVAiV5hoE6Qq8dQDB4XC14x4N9mhn+FetxzqDu5LVci0/sOPqyPq2b0YUtNwJ1ZUKawTz4I/oqnUsHkQrGH0w==} + cpu: [x64] + os: [linux] + + lefthook-openbsd-arm64@2.1.3: + resolution: {integrity: sha512-8QVvRxIosV6NL2XrbifOPGVhMFE43h02BUNEHYhZhyad7BredfAakg9dA9J/NO0I3eMdvCYU50ubFyDGIqUJog==} + cpu: [arm64] + os: [openbsd] + + lefthook-openbsd-x64@2.1.3: + resolution: {integrity: sha512-YTS9qeW9PzzKg9Rk55mQprLIl1OdAIIjeOH8DF+MPWoAPkRqeUyq8Q2Bdlf3+Swy+kJOjoiU1pKvpjjc8upv9Q==} + cpu: [x64] + os: [openbsd] + + lefthook-windows-arm64@2.1.3: + resolution: {integrity: sha512-Nlp80pWyF67GmxgM5NQmL7JTTccbJAvCNtS5QwHmKq3pJ9Xi0UegP9pGME520n06Rhp+gX7H4boXhm2D5hAghg==} + cpu: [arm64] + os: [win32] + + lefthook-windows-x64@2.1.3: + resolution: {integrity: sha512-KByBhvqgUNhjO/03Mr0y66D9B1ZnII7AB0x17cumwHMOYoDaPJh/AlgmEduqUpatqli3lnFzWD0DUkAY6pq/SA==} + cpu: [x64] + os: [win32] + + lefthook@2.1.3: + resolution: {integrity: sha512-2W8PP/EGCvyS/x+Xza0Lgvn/EM3FKnr6m6xkfzpl6RKHl8TwPvs9iYZFQL99CnWTTvO+1mtQvIxGE/bD05038Q==} + hasBin: true + leven@2.1.0: resolution: {integrity: sha512-nvVPLpIHUxCUoRLrFqTgSxXJ614d8AgQoWl7zPe/2VadE8+1dpU3LBhowRuBAcuwruWtOdD8oYC9jDNJjXDPyA==} engines: {node: '>=0.10.0'} @@ -2788,6 +2877,8 @@ snapshots: '@bcoe/v8-coverage@1.0.2': {} + '@epic-web/invariant@1.0.0': {} + '@esbuild/aix-ppc64@0.27.3': optional: true @@ -3114,6 +3205,20 @@ snapshots: transitivePeerDependencies: - supports-color + '@vitest/coverage-v8@4.0.18(vitest@4.0.18)': + dependencies: + '@bcoe/v8-coverage': 1.0.2 + '@vitest/utils': 4.0.18 + ast-v8-to-istanbul: 0.3.12 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-report: 3.0.1 + istanbul-reports: 3.2.0 + magicast: 0.5.2 + obug: 2.1.1 + std-env: 3.10.0 + tinyrainbow: 3.0.3 + vitest: 4.0.18 + '@vitest/expect@4.0.18': dependencies: '@standard-schema/spec': 1.1.0 @@ -3180,6 +3285,12 @@ snapshots: assertion-error@2.0.1: {} + ast-v8-to-istanbul@0.3.12: + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + estree-walker: 3.0.3 + js-tokens: 10.0.0 + babel-helper-evaluate-path@0.5.0: {} babel-helper-flip-expressions@0.4.3: {} @@ -3405,6 +3516,11 @@ snapshots: core-util-is@1.0.3: {} + cross-env@10.1.0: + dependencies: + '@epic-web/invariant': 1.0.0 + cross-spawn: 7.0.6 + cross-spawn@7.0.6: dependencies: path-key: 3.1.1 @@ -3576,6 +3692,8 @@ snapshots: html-escaper: 2.0.2 istanbul-lib-report: 3.0.1 + js-tokens@10.0.0: {} + js-tokens@4.0.0: {} jsesc@3.1.0: {} @@ -3584,6 +3702,49 @@ snapshots: kleur@3.0.3: {} + lefthook-darwin-arm64@2.1.3: + optional: true + + lefthook-darwin-x64@2.1.3: + optional: true + + lefthook-freebsd-arm64@2.1.3: + optional: true + + lefthook-freebsd-x64@2.1.3: + optional: true + + lefthook-linux-arm64@2.1.3: + optional: true + + lefthook-linux-x64@2.1.3: + optional: true + + lefthook-openbsd-arm64@2.1.3: + optional: true + + lefthook-openbsd-x64@2.1.3: + optional: true + + lefthook-windows-arm64@2.1.3: + optional: true + + lefthook-windows-x64@2.1.3: + optional: true + + lefthook@2.1.3: + optionalDependencies: + lefthook-darwin-arm64: 2.1.3 + lefthook-darwin-x64: 2.1.3 + lefthook-freebsd-arm64: 2.1.3 + lefthook-freebsd-x64: 2.1.3 + lefthook-linux-arm64: 2.1.3 + lefthook-linux-x64: 2.1.3 + lefthook-openbsd-arm64: 2.1.3 + lefthook-openbsd-x64: 2.1.3 + lefthook-windows-arm64: 2.1.3 + lefthook-windows-x64: 2.1.3 + leven@2.1.0: {} locate-path@6.0.0: diff --git a/scripts/build-node-titanium-sdk.js b/scripts/build-node-titanium-sdk.js index edc30c423..d3c2d2dca 100644 --- a/scripts/build-node-titanium-sdk.js +++ b/scripts/build-node-titanium-sdk.js @@ -1,4 +1,5 @@ #!/usr/bin/env node +import { execa } from 'execa'; /** * Builds node-titanium-sdk in a temp directory (outside node_modules) because * tsdown refuses to process config files under node_modules. @@ -7,7 +8,6 @@ import { cpSync, existsSync, mkdirSync, readdirSync, realpathSync, rmSync } from import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { fileURLToPath } from 'node:url'; -import { execa } from 'execa'; const root = join(fileURLToPath(import.meta.url), '..', '..'); const sdkPath = join(root, 'node_modules', 'node-titanium-sdk'); diff --git a/src/cli.js b/src/cli.js index 4309d7ccb..341bbcdd0 100644 --- a/src/cli.js +++ b/src/cli.js @@ -1086,7 +1086,7 @@ export class CLI { .action((...args) => this.executeCommand(args)); } } - } catch (e) { + } catch { // squelch } } diff --git a/test/cli/hook.test.js b/test/cli/hook.test.js index 0c18abd56..6facc1a94 100644 --- a/test/cli/hook.test.js +++ b/test/cli/hook.test.js @@ -1,7 +1,6 @@ import { CLI } from '../../src/cli.js'; -import assert from 'node:assert'; -import { describe, it } from 'node:test'; -import { setTimeout } from 'timers/promises'; +import { setTimeout } from 'node:timers/promises'; +import { describe, expect, it } from 'vitest'; describe('CLI hooks', () => { it('should fire sync event hooks', async () => { @@ -29,14 +28,14 @@ describe('CLI hooks', () => { }); await cli.emit('foo', { count: 2 }); - assert.strictEqual(fooCounter, 2); + expect(fooCounter).toBe(2); await cli.emit('bar'); - assert.strictEqual(barCounter, 1); + expect(barCounter).toBe(1); await cli.emit('baz'); - assert.strictEqual(bazPreCounter, 1); - assert.strictEqual(bazPostCounter, 1); + expect(bazPreCounter).toBe(1); + expect(bazPostCounter).toBe(1); }); it('should fire async event hooks', async () => { @@ -50,7 +49,7 @@ describe('CLI hooks', () => { }); await cli.emit('foo', { count: 2 }); - assert.strictEqual(fooCounter, 2); + expect(fooCounter).toBe(2); let barCounter = 0; @@ -60,7 +59,7 @@ describe('CLI hooks', () => { }); await cli.emit('bar'); - assert.strictEqual(barCounter, 1); + expect(barCounter).toBe(1); }); it('should fire function hooks', async () => { @@ -77,7 +76,7 @@ describe('CLI hooks', () => { cb(); }); await new Promise((resolve) => foo(3, resolve)); - assert.strictEqual(fooCounter, 7); + expect(fooCounter).toBe(7); let barCounter = 0; @@ -95,7 +94,7 @@ describe('CLI hooks', () => { cb(9); }); await new Promise((resolve) => bar(resolve)); - assert.strictEqual(barCounter, 13); + expect(barCounter).toBe(13); }); it('should fire event hook with a data payload', async () => { @@ -122,6 +121,6 @@ describe('CLI hooks', () => { }); }); - assert.strictEqual(foo.counter, 1); + expect(foo.counter).toBe(1); }); }); diff --git a/test/commands/custom.test.js b/test/commands/custom.test.js index b81efc33f..85df119a3 100644 --- a/test/commands/custom.test.js +++ b/test/commands/custom.test.js @@ -1,9 +1,8 @@ import { initCLI } from '../helpers/init-cli.js'; import { stripColor } from '../helpers/strip-color.js'; -import assert from 'node:assert'; import { dirname, join } from 'node:path'; -import { describe, it } from 'node:test'; import { fileURLToPath } from 'node:url'; +import { describe, expect, it } from 'vitest'; const __dirname = dirname(fileURLToPath(import.meta.url)); @@ -21,15 +20,15 @@ describe('Custom command', () => { ]); const output = stripColor(stdout); - assert.match(output, /Titanium Command-Line Interface/); - assert.match(output, /Usage: titanium/); - assert.match(output, /Commands:/); - assert.match(output, /foo/); - // assert.match(output, /an example of a custom command/); - assert.match(output, /Global Options:/); - assert.match(output, /-h, --help/); + expect(output).toMatch(/Titanium Command-Line Interface/); + expect(output).toMatch(/Usage: titanium/); + expect(output).toMatch(/Commands:/); + expect(output).toMatch(/foo/); + // expect(output).toMatch(/an example of a custom command/); + expect(output).toMatch(/Global Options:/); + expect(output).toMatch(/-h, --help/); - assert.strictEqual(exitCode, 0); + expect(exitCode).toBe(0); }) ); @@ -47,10 +46,10 @@ describe('Custom command', () => { ]); const output = stripColor(stdout); - assert.match(output, /Titanium Command-Line Interface/); - assert.match(output, /Foo!/); + expect(output).toMatch(/Titanium Command-Line Interface/); + expect(output).toMatch(/Foo!/); - assert.strictEqual(exitCode, 0); + expect(exitCode).toBe(0); }) ); }); diff --git a/test/commands/ti-build.test.js b/test/commands/ti-build.test.js index f8eb49aef..5338d65e5 100644 --- a/test/commands/ti-build.test.js +++ b/test/commands/ti-build.test.js @@ -1,7 +1,6 @@ import { initMockSDKHome } from '../helpers/init-sdk-home.js'; import { stripColor } from '../helpers/strip-color.js'; -import assert from 'node:assert'; -import { describe, it } from 'node:test'; +import { describe, expect, it } from 'vitest'; describe('ti build', () => { it( @@ -10,13 +9,13 @@ describe('ti build', () => { const { exitCode, stdout } = await run(['build', '-h']); const output = stripColor(stdout); - assert.match(output, /Titanium Command-Line Interface/); - assert.match(output, /Usage: titanium build \[options\]/); - assert.match(output, /Builds an existing app or module project./); - assert.match(output, /Build Options:/); - assert.match(output, /Global Options:/); + expect(output).toMatch(/Titanium Command-Line Interface/); + expect(output).toMatch(/Usage: titanium build \[options\]/); + expect(output).toMatch(/Builds an existing app or module project./); + expect(output).toMatch(/Build Options:/); + expect(output).toMatch(/Global Options:/); - assert.strictEqual(exitCode, 0); + expect(exitCode).toBe(0); }) ); }); diff --git a/test/commands/ti-clean.test.js b/test/commands/ti-clean.test.js index 037fc350a..b12110499 100644 --- a/test/commands/ti-clean.test.js +++ b/test/commands/ti-clean.test.js @@ -1,7 +1,6 @@ import { initMockSDKHome } from '../helpers/init-sdk-home.js'; import { stripColor } from '../helpers/strip-color.js'; -import assert from 'node:assert'; -import { describe, it } from 'node:test'; +import { describe, expect, it } from 'vitest'; describe('ti clean', () => { it( @@ -10,13 +9,13 @@ describe('ti clean', () => { const { exitCode, stdout } = await run(['clean', '-h']); const output = stripColor(stdout); - assert.match(output, /Titanium Command-Line Interface/); - assert.match(output, /Usage: titanium clean \[options\]/); - assert.match(output, /Removes previous build directories./); - assert.match(output, /Clean Options:/); - assert.match(output, /Global Options:/); + expect(output).toMatch(/Titanium Command-Line Interface/); + expect(output).toMatch(/Usage: titanium clean \[options\]/); + expect(output).toMatch(/Removes previous build directories./); + expect(output).toMatch(/Clean Options:/); + expect(output).toMatch(/Global Options:/); - assert.strictEqual(exitCode, 0); + expect(exitCode).toBe(0); }) ); }); diff --git a/test/commands/ti-config.test.js b/test/commands/ti-config.test.js index ee9d2e506..34c5e1fd8 100644 --- a/test/commands/ti-config.test.js +++ b/test/commands/ti-config.test.js @@ -1,9 +1,8 @@ import { initCLI } from '../helpers/init-cli.js'; import { stripColor } from '../helpers/strip-color.js'; -import assert from 'node:assert'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; -import { describe, it } from 'node:test'; +import { describe, expect, it } from 'vitest'; describe('ti config', () => { it( @@ -12,12 +11,12 @@ describe('ti config', () => { const { exitCode, stdout } = await run(['config', '-h']); const output = stripColor(stdout); - assert.match(output, /Titanium Command-Line Interface/); - assert.match(output, /Usage: titanium config \[options\] \[key\] \[value\]/); - assert.match(output, /Config Arguments:/); - assert.match(output, /Config Options:/); - assert.match(output, /Global Options:/); - assert.strictEqual(exitCode, 0); + expect(output).toMatch(/Titanium Command-Line Interface/); + expect(output).toMatch(/Usage: titanium config \[options\] \[key\] \[value\]/); + expect(output).toMatch(/Config Arguments:/); + expect(output).toMatch(/Config Options:/); + expect(output).toMatch(/Global Options:/); + expect(exitCode).toBe(0); }) ); @@ -27,8 +26,8 @@ describe('ti config', () => { const { exitCode, stdout } = await run(['config']); const output = stripColor(stdout); - assert.match(output, /cli.colors\s+= (?:true|false)/); - assert.strictEqual(exitCode, 0); + expect(output).toMatch(/cli.colors\s+= (?:true|false)/); + expect(exitCode).toBe(0); }) ); @@ -38,9 +37,9 @@ describe('ti config', () => { const { exitCode, stdout } = await run(['config', '--json']); const json = JSON.parse(stdout); - assert(Object.hasOwn(json, 'cli')); - assert(Object.hasOwn(json.cli, 'colors')); - assert.strictEqual(exitCode, 0); + expect(Object.hasOwn(json, 'cli')).toBe(true); + expect(Object.hasOwn(json.cli, 'colors')).toBe(true); + expect(exitCode).toBe(0); }) ); @@ -50,8 +49,8 @@ describe('ti config', () => { const { exitCode, stdout } = await run(['config', 'cli']); const output = stripColor(stdout); - assert.match(output, /cli.colors\s+= (?:true|false)/); - assert.strictEqual(exitCode, 0); + expect(output).toMatch(/cli.colors\s+= (?:true|false)/); + expect(exitCode).toBe(0); }) ); @@ -61,8 +60,8 @@ describe('ti config', () => { const { exitCode, stdout } = await run(['config', 'cli', '--json']); const json = JSON.parse(stdout); - assert(Object.hasOwn(json, 'colors')); - assert.strictEqual(exitCode, 0); + expect(Object.hasOwn(json, 'colors')).toBe(true); + expect(exitCode).toBe(0); }) ); @@ -72,8 +71,8 @@ describe('ti config', () => { const { exitCode, stdout } = await run(['config', 'cli.colors']); const output = stripColor(stdout); - assert.match(output, /true|false/); - assert.strictEqual(exitCode, 0); + expect(output).toMatch(/true|false/); + expect(exitCode).toBe(0); }) ); @@ -82,8 +81,8 @@ describe('ti config', () => { initCLI(async ({ run }) => { const { exitCode, stdout } = await run(['config', 'cli.colors', '--json']); - assert.match(stdout, /true|false/); - assert.strictEqual(exitCode, 0); + expect(stdout).toMatch(/true|false/); + expect(exitCode).toBe(0); }) ); @@ -93,8 +92,8 @@ describe('ti config', () => { const { exitCode, stderr } = await run(['config', '123']); const output = stripColor(stderr); - assert.match(output, /Invalid key "123"/); - assert.strictEqual(exitCode, 1); + expect(output).toMatch(/Invalid key "123"/); + expect(exitCode).toBe(1); }) ); @@ -104,8 +103,8 @@ describe('ti config', () => { const { exitCode, stderr } = await run(['config', 'does_not_exist']); const output = stripColor(stderr); - assert.match(output, /Key "does_not_exist" not found/); - assert.strictEqual(exitCode, 1); + expect(output).toMatch(/Key "does_not_exist" not found/); + expect(exitCode).toBe(1); }) ); @@ -115,11 +114,11 @@ describe('ti config', () => { const { exitCode, stderr } = await run(['config', 'does_not_exist', '--json']); const json = JSON.parse(stderr); - assert.deepStrictEqual(json, { + expect(json).toEqual({ success: false, error: 'Key "does_not_exist" not found', }); - assert.strictEqual(exitCode, 1); + expect(exitCode).toBe(1); }) ); @@ -129,13 +128,13 @@ describe('ti config', () => { let { exitCode, stdout } = await run(['config', 'foo', 'bar']); let output = stripColor(stdout); - assert.match(output, /foo saved/); - assert.strictEqual(exitCode, 0); + expect(output).toMatch(/foo saved/); + expect(exitCode).toBe(0); ({ exitCode, stdout } = await run(['config', 'foo'])); output = stripColor(stdout); - assert.match(output, /bar/); - assert.strictEqual(exitCode, 0); + expect(output).toMatch(/bar/); + expect(exitCode).toBe(0); }) ); @@ -145,13 +144,13 @@ describe('ti config', () => { let { exitCode, stdout } = await run(['config', 'foo.bar', 'baz']); let output = stripColor(stdout); - assert.match(output, /foo.bar saved/); - assert.strictEqual(exitCode, 0); + expect(output).toMatch(/foo.bar saved/); + expect(exitCode).toBe(0); ({ exitCode, stdout } = await run(['config'])); output = stripColor(stdout); - assert.match(output, /foo.bar\s+= "baz"/); - assert.strictEqual(exitCode, 0); + expect(output).toMatch(/foo.bar\s+= "baz"/); + expect(exitCode).toBe(0); }) ); @@ -161,23 +160,23 @@ describe('ti config', () => { let { exitCode, stdout, stderr } = await run(['config', 'foo', 'bar']); let output = stripColor(stdout); - assert.match(output, /foo saved/); - assert.strictEqual(exitCode, 0); + expect(output).toMatch(/foo saved/); + expect(exitCode).toBe(0); ({ exitCode, stdout } = await run(['config', 'foo'])); output = stripColor(stdout); - assert.match(output, /bar/); - assert.strictEqual(exitCode, 0); + expect(output).toMatch(/bar/); + expect(exitCode).toBe(0); ({ exitCode, stdout } = await run(['config', 'foo', '--remove'])); output = stripColor(stdout); - assert.match(output, /"foo" removed/); - assert.strictEqual(exitCode, 0); + expect(output).toMatch(/"foo" removed/); + expect(exitCode).toBe(0); ({ exitCode, stderr } = await run(['config', 'foo'])); output = stripColor(stderr); - assert.match(output, /Key "foo" not found/); - assert.strictEqual(exitCode, 1); + expect(output).toMatch(/Key "foo" not found/); + expect(exitCode).toBe(1); }) ); @@ -187,23 +186,23 @@ describe('ti config', () => { let { exitCode, stdout, stderr } = await run(['config', 'foo.bar', 'baz']); let output = stripColor(stdout); - assert.match(output, /foo.bar saved/); - assert.strictEqual(exitCode, 0); + expect(output).toMatch(/foo.bar saved/); + expect(exitCode).toBe(0); ({ exitCode, stdout } = await run(['config'])); output = stripColor(stdout); - assert.match(output, /foo.bar\s+= "baz"/); - assert.strictEqual(exitCode, 0); + expect(output).toMatch(/foo.bar\s+= "baz"/); + expect(exitCode).toBe(0); ({ exitCode, stdout } = await run(['config', 'foo', '--remove'])); output = stripColor(stdout); - assert.match(output, /"foo" removed/); - assert.strictEqual(exitCode, 0); + expect(output).toMatch(/"foo" removed/); + expect(exitCode).toBe(0); ({ exitCode, stderr } = await run(['config', 'foo'])); output = stripColor(stderr); - assert.match(output, /Key "foo" not found/); - assert.strictEqual(exitCode, 1); + expect(output).toMatch(/Key "foo" not found/); + expect(exitCode).toBe(1); }) ); @@ -213,9 +212,9 @@ describe('ti config', () => { const { exitCode, stderr } = await run(['config', '--remove']); const output = stripColor(stderr); - assert.match(output, /Missing key of the config setting to remove/); + expect(output).toMatch(/Missing key of the config setting to remove/); - assert.strictEqual(exitCode, 1); + expect(exitCode).toBe(1); }) ); @@ -225,9 +224,9 @@ describe('ti config', () => { const { exitCode, stderr } = await run(['config', 'foo', 'bar', '--remove']); const output = stripColor(stderr); - assert.match(output, /Too many arguments for "--remove" flag/); + expect(output).toMatch(/Too many arguments for "--remove" flag/); - assert.strictEqual(exitCode, 1); + expect(exitCode).toBe(1); }) ); @@ -237,8 +236,8 @@ describe('ti config', () => { const { exitCode, stderr } = await run(['config', 'paths.foo', 'bar']); const output = stripColor(stderr); - assert.match(output, /Unsupported key "paths.foo"/); - assert.strictEqual(exitCode, 1); + expect(output).toMatch(/Unsupported key "paths.foo"/); + expect(exitCode).toBe(1); }) ); @@ -251,56 +250,56 @@ describe('ti config', () => { let { exitCode, stdout } = await run(['config', 'paths.modules', fooPath]); let output = stripColor(stdout); - assert.match(output, /paths.modules saved/); - assert.strictEqual(exitCode, 0); + expect(output).toMatch(/paths.modules saved/); + expect(exitCode).toBe(0); ({ exitCode, stdout } = await run(['config', 'paths.modules', '--json'])); let json = JSON.parse(stdout); - assert.deepStrictEqual(json, [fooPath]); - assert.strictEqual(exitCode, 0); + expect(json).toEqual([fooPath]); + expect(exitCode).toBe(0); ({ exitCode, stdout } = await run(['config', 'paths.modules', barPath, '--json'])); json = JSON.parse(stdout); - assert.deepStrictEqual(json, { success: true }); - assert.strictEqual(exitCode, 0); + expect(json).toEqual({ success: true }); + expect(exitCode).toBe(0); ({ exitCode, stdout } = await run(['config', 'paths'])); output = stripColor(stdout); - assert.match(output, new RegExp(`= "${barPath.replace(/\\/g, '\\\\\\\\')}"`)); - assert.strictEqual(exitCode, 0); + expect(output).toMatch(new RegExp(`= "${barPath.replace(/\\/g, '\\\\\\\\')}"`)); + expect(exitCode).toBe(0); ({ exitCode, stdout } = await run(['config', 'paths.modules', bazPath, '--append'])); output = stripColor(stdout); - assert.match(output, /paths.modules saved/); - assert.strictEqual(exitCode, 0); + expect(output).toMatch(/paths.modules saved/); + expect(exitCode).toBe(0); ({ exitCode, stdout } = await run(['config', 'paths.modules'])); output = stripColor(stdout); - assert.match( + expect(output).toMatch( output, new RegExp(`${barPath.replace(/\\/g, '\\\\')}\n${bazPath.replace(/\\/g, '\\\\')}`) ); - assert.strictEqual(exitCode, 0); + expect(exitCode).toBe(0); ({ exitCode, stdout } = await run(['config', 'paths.modules', '--json'])); json = JSON.parse(stdout); - assert.deepStrictEqual(json, [barPath, bazPath]); - assert.strictEqual(exitCode, 0); + expect(json).toEqual([barPath, bazPath]); + expect(exitCode).toBe(0); ({ exitCode, stdout } = await run(['config', 'paths.modules', barPath, '--remove'])); output = stripColor(stdout); - assert.match(output, /paths.modules saved/); - assert.strictEqual(exitCode, 0); + expect(output).toMatch(/paths.modules saved/); + expect(exitCode).toBe(0); ({ exitCode, stdout } = await run(['config', 'paths.modules', '--json'])); json = JSON.parse(stdout); - assert.deepStrictEqual(json, [bazPath]); - assert.strictEqual(exitCode, 0); + expect(json).toEqual([bazPath]); + expect(exitCode).toBe(0); ({ exitCode, stdout } = await run(['config', 'paths.modules', barPath, '--remove'])); output = stripColor(stdout); - assert.match(output, /paths.modules saved/); - assert.strictEqual(exitCode, 0); + expect(output).toMatch(/paths.modules saved/); + expect(exitCode).toBe(0); }) ); }); diff --git a/test/commands/ti-create.test.js b/test/commands/ti-create.test.js index eca263666..8db9fe998 100644 --- a/test/commands/ti-create.test.js +++ b/test/commands/ti-create.test.js @@ -1,7 +1,6 @@ import { initMockSDKHome } from '../helpers/init-sdk-home.js'; import { stripColor } from '../helpers/strip-color.js'; -import assert from 'node:assert'; -import { describe, it } from 'node:test'; +import { describe, expect, it } from 'vitest'; describe('ti create', () => { it( @@ -10,18 +9,17 @@ describe('ti create', () => { const { exitCode, stdout } = await run(['create', '-h']); const output = stripColor(stdout); - assert.match(output, /Titanium Command-Line Interface/); - assert.match(output, /Usage: titanium create \[options\]/); - assert.match( - output, + expect(output).toMatch(/Titanium Command-Line Interface/); + expect(output).toMatch(/Usage: titanium create \[options\]/); + expect(output).toMatch( /Creates a new Titanium application, native module, or Apple Watch™ app./ ); - assert.match(output, /Create Options:/); - assert.match(output, /Create --type=app Options:/); - assert.match(output, /Create --type=module Options:/); - assert.match(output, /Global Options:/); + expect(output).toMatch(/Create Options:/); + expect(output).toMatch(/Create --type=app Options:/); + expect(output).toMatch(/Create --type=module Options:/); + expect(output).toMatch(/Global Options:/); - assert.strictEqual(exitCode, 0); + expect(exitCode).toBe(0); }) ); }); diff --git a/test/commands/ti-info.test.js b/test/commands/ti-info.test.js index ea6f6d5c0..f05cfb6c1 100644 --- a/test/commands/ti-info.test.js +++ b/test/commands/ti-info.test.js @@ -1,10 +1,9 @@ import { initCLI } from '../helpers/init-cli.js'; import { stripColor } from '../helpers/strip-color.js'; -import assert from 'node:assert'; import { readFileSync } from 'node:fs'; import { dirname, join } from 'node:path'; -import { describe, it } from 'node:test'; import { fileURLToPath } from 'node:url'; +import { describe, expect, it } from 'vitest'; const __dirname = dirname(fileURLToPath(import.meta.url)); const pkgJson = JSON.parse(readFileSync(join(__dirname, '../../package.json'), 'utf8')); @@ -16,12 +15,12 @@ describe('ti info', () => { const { exitCode, stdout } = await run(['info', '-h']); const output = stripColor(stdout); - assert.match(output, /Titanium Command-Line Interface/); - assert.match(output, /Usage: titanium info/); - assert.match(output, /Info Options:/); - assert.match(output, /Global Options:/); + expect(output).toMatch(/Titanium Command-Line Interface/); + expect(output).toMatch(/Usage: titanium info/); + expect(output).toMatch(/Info Options:/); + expect(output).toMatch(/Global Options:/); - assert.strictEqual(exitCode, 0); + expect(exitCode).toBe(0); }) ); @@ -31,18 +30,17 @@ describe('ti info', () => { const { exitCode, stdout } = await run(['info']); const output = stripColor(stdout); - assert.match(output, /Titanium Command-Line Interface/); - assert.match(output, /Operating System/); - assert.match( - output, + expect(output).toMatch(/Titanium Command-Line Interface/); + expect(output).toMatch(/Operating System/); + expect(output).toMatch( new RegExp(`Node.js\n\\s*Node.js Version\\s*= ${process.versions.node}`) ); - assert.match(output, new RegExp(`Titanium CLI\n\\s*CLI Version\\s*= ${pkgJson.version}`)); - assert.match(output, /Titanium SDKs/); - assert.match(output, /Java/); - assert.match(output, /Issues/); + expect(output).toMatch(new RegExp(`Titanium CLI\n\\s*CLI Version\\s*= ${pkgJson.version}`)); + expect(output).toMatch(/Titanium SDKs/); + expect(output).toMatch(/Java/); + expect(output).toMatch(/Issues/); - assert.strictEqual(exitCode, 0); + expect(exitCode).toBe(0); }), 60000 ); @@ -53,25 +51,25 @@ describe('ti info', () => { let { exitCode, stdout } = await run(['info', '--json']); let json = JSON.parse(stdout); - assert(Object.hasOwn(json, 'os')); - assert(Object.hasOwn(json, 'node')); - assert(Object.hasOwn(json, 'npm')); - assert(Object.hasOwn(json, 'titanium')); - assert(Object.hasOwn(json, 'titaniumCLI')); - assert(Object.hasOwn(json, 'java')); + expect(Object.hasOwn(json, 'os')).toBe(true); + expect(Object.hasOwn(json, 'node')).toBe(true); + expect(Object.hasOwn(json, 'npm')).toBe(true); + expect(Object.hasOwn(json, 'titanium')).toBe(true); + expect(Object.hasOwn(json, 'titaniumCLI')).toBe(true); + expect(Object.hasOwn(json, 'java')).toBe(true); // legacy ({ exitCode, stdout } = await run(['info', '--output', 'json'])); json = JSON.parse(stdout); - assert(Object.hasOwn(json, 'os')); - assert(Object.hasOwn(json, 'node')); - assert(Object.hasOwn(json, 'npm')); - assert(Object.hasOwn(json, 'titanium')); - assert(Object.hasOwn(json, 'titaniumCLI')); - assert(Object.hasOwn(json, 'java')); - - assert.strictEqual(exitCode, 0); + expect(Object.hasOwn(json, 'os')).toBe(true); + expect(Object.hasOwn(json, 'node')).toBe(true); + expect(Object.hasOwn(json, 'npm')).toBe(true); + expect(Object.hasOwn(json, 'titanium')).toBe(true); + expect(Object.hasOwn(json, 'titaniumCLI')).toBe(true); + expect(Object.hasOwn(json, 'java')).toBe(true); + + expect(exitCode).toBe(0); }), 60000 ); @@ -82,21 +80,19 @@ describe('ti info', () => { const { exitCode, stdout } = await run(['info', '--types', 'os']); const output = stripColor(stdout); - assert.match(output, /Titanium Command-Line Interface/); - assert.match(output, /Operating System/); - assert.doesNotMatch( - output, + expect(output).toMatch(/Titanium Command-Line Interface/); + expect(output).toMatch(/Operating System/); + expect(output).not.toMatch( new RegExp(`Node.js\n\\s*Node.js Version\\s*= ${process.versions.node}`) ); - assert.doesNotMatch( - output, + expect(output).not.toMatch( new RegExp(`Titanium CLI\n\\s*CLI Version\\s*= ${pkgJson.version}`) ); - assert.doesNotMatch(output, /Titanium SDKs/); - assert.doesNotMatch(output, /Java Development Kit/); - assert.match(output, /Issues/); + expect(output).not.toMatch(/Titanium SDKs/); + expect(output).not.toMatch(/Java/); + expect(output).toMatch(/Issues/); - assert.strictEqual(exitCode, 0); + expect(exitCode).toBe(0); }), 60000 ); @@ -107,14 +103,14 @@ describe('ti info', () => { const { exitCode, stdout } = await run(['info', '--types', 'os', '--json']); const json = JSON.parse(stdout); - assert(Object.hasOwn(json, 'os')); - assert(!Object.hasOwn(json, 'node')); - assert(!Object.hasOwn(json, 'npm')); - assert(!Object.hasOwn(json, 'titanium')); - assert(!Object.hasOwn(json, 'titaniumCLI')); - assert(!Object.hasOwn(json, 'jdk')); - - assert.strictEqual(exitCode, 0); + expect(Object.hasOwn(json, 'os')).toBe(true); + expect(Object.hasOwn(json, 'node')).toBe(false); + expect(Object.hasOwn(json, 'npm')).toBe(false); + expect(Object.hasOwn(json, 'titanium')).toBe(false); + expect(Object.hasOwn(json, 'titaniumCLI')).toBe(false); + expect(Object.hasOwn(json, 'java')).toBe(false); + + expect(exitCode).toBe(0); }), 60000 ); @@ -125,21 +121,19 @@ describe('ti info', () => { const { exitCode, stdout } = await run(['info', '--types', 'nodejs']); const output = stripColor(stdout); - assert.match(output, /Titanium Command-Line Interface/); - assert.doesNotMatch(output, /Operating System/); - assert.match( - output, + expect(output).toMatch(/Titanium Command-Line Interface/); + expect(output).not.toMatch(/Operating System/); + expect(output).toMatch( new RegExp(`Node.js\n\\s*Node.js Version\\s*= ${process.versions.node}`) ); - assert.doesNotMatch( - output, + expect(output).not.toMatch( new RegExp(`Titanium CLI\n\\s*CLI Version\\s*= ${pkgJson.version}`) ); - assert.doesNotMatch(output, /Titanium SDKs/); - assert.doesNotMatch(output, /Java Development Kit/); - assert.match(output, /Issues/); + expect(output).not.toMatch(/Titanium SDKs/); + expect(output).not.toMatch(/Java/); + expect(output).toMatch(/Issues/); - assert.strictEqual(exitCode, 0); + expect(exitCode).toBe(0); }), 60000 ); @@ -150,15 +144,15 @@ describe('ti info', () => { const { exitCode, stdout } = await run(['info', '--types', 'nodejs', '--json']); const json = JSON.parse(stdout); - assert(!Object.hasOwn(json, 'os')); - assert(Object.hasOwn(json, 'node')); - assert.strictEqual(json.node.version, process.versions.node); - assert(Object.hasOwn(json, 'npm')); - assert(!Object.hasOwn(json, 'titanium')); - assert(!Object.hasOwn(json, 'titaniumCLI')); - assert(!Object.hasOwn(json, 'jdk')); - - assert.strictEqual(exitCode, 0); + expect(Object.hasOwn(json, 'os')).toBe(false); + expect(Object.hasOwn(json, 'node')).toBe(true); + expect(json.node.version).toBe(process.versions.node); + expect(Object.hasOwn(json, 'npm')).toBe(true); + expect(Object.hasOwn(json, 'titanium')).toBe(false); + expect(Object.hasOwn(json, 'titaniumCLI')).toBe(false); + expect(Object.hasOwn(json, 'java')).toBe(false); + + expect(exitCode).toBe(0); }), 60000 ); @@ -169,43 +163,40 @@ describe('ti info', () => { const { exitCode, stdout } = await run(['info', '--types', 'titanium']); const output = stripColor(stdout); - assert.match(output, /Titanium Command-Line Interface/); - assert.doesNotMatch(output, /Operating System/); - assert.doesNotMatch( - output, + expect(output).toMatch(/Titanium Command-Line Interface/); + expect(output).not.toMatch(/Operating System/); + expect(output).not.toMatch( new RegExp(`Node.js\n\\s*Node.js Version\\s*= ${process.versions.node}`) ); - assert.match(output, new RegExp(`Titanium CLI\n\\s*CLI Version\\s*= ${pkgJson.version}`)); - assert.match(output, /Titanium SDKs/); - assert.doesNotMatch(output, /Java Development Kit/); - assert.match(output, /Issues/); + expect(output).toMatch(new RegExp(`Titanium CLI\n\\s*CLI Version\\s*= ${pkgJson.version}`)); + expect(output).toMatch(/Titanium SDKs/); + expect(output).not.toMatch(/Java/); + expect(output).toMatch(/Issues/); - assert.strictEqual(exitCode, 0); + expect(exitCode).toBe(0); }), 60000 ); it( - 'should only show "jdk" info', + 'should only show "java" info', initCLI(async ({ run }) => { - const { exitCode, stdout } = await run(['info', '--types', 'jdk']); + const { exitCode, stdout } = await run(['info', '--types', 'java']); const output = stripColor(stdout); - assert.match(output, /Titanium Command-Line Interface/); - assert.doesNotMatch(output, /Operating System/); - assert.doesNotMatch( - output, + expect(output).toMatch(/Titanium Command-Line Interface/); + expect(output).not.toMatch(/Operating System/); + expect(output).not.toMatch( new RegExp(`Node.js\n\\s*Node.js Version\\s*= ${process.versions.node}`) ); - assert.doesNotMatch( - output, + expect(output).not.toMatch( new RegExp(`Titanium CLI\n\\s*CLI Version\\s*= ${pkgJson.version}`) ); - assert.doesNotMatch(output, /Titanium SDKs/); - assert.match(output, /Java/); - assert.match(output, /Issues/); + expect(output).not.toMatch(/Titanium SDKs/); + expect(output).toMatch(/Java/); + expect(output).toMatch(/Issues/); - assert.strictEqual(exitCode, 0); + expect(exitCode).toBe(0); }), 60000 ); diff --git a/test/commands/ti-module.test.js b/test/commands/ti-module.test.js index 9f947b618..36e82dc61 100644 --- a/test/commands/ti-module.test.js +++ b/test/commands/ti-module.test.js @@ -1,9 +1,8 @@ import { initCLI } from '../helpers/init-cli.js'; import { stripColor } from '../helpers/strip-color.js'; -import assert from 'node:assert'; import { join } from 'node:path'; -import { describe, it } from 'node:test'; import { fileURLToPath } from 'node:url'; +import { describe, expect, it } from 'vitest'; const fixturesDir = join(fileURLToPath(import.meta.url), '../fixtures'); @@ -14,13 +13,13 @@ describe('ti module', () => { const { exitCode, stdout } = await run(['module', '-h']); const output = stripColor(stdout); - assert.match(output, /Titanium Command-Line Interface/); - assert.match(output, /Usage: titanium module \[options\] \[command\]/); - assert.match(output, /Commands:/); - assert.match(output, /Module Options:/); - assert.match(output, /Global Options:/); + expect(output).toMatch(/Titanium Command-Line Interface/); + expect(output).toMatch(/Usage: titanium module \[options\] \[command\]/); + expect(output).toMatch(/Commands:/); + expect(output).toMatch(/Module Options:/); + expect(output).toMatch(/Global Options:/); - assert.strictEqual(exitCode, 0); + expect(exitCode).toBe(0); }) ); @@ -30,12 +29,12 @@ describe('ti module', () => { const { exitCode, stdout } = await run(['module', 'list', '-h']); const output = stripColor(stdout); - assert.match(output, /Titanium Command-Line Interface/); - assert.match(output, /Usage: titanium module list|ls/); - assert.match(output, /List Options:/); - assert.match(output, /Global Options:/); + expect(output).toMatch(/Titanium Command-Line Interface/); + expect(output).toMatch(/Usage: titanium module list|ls/); + expect(output).toMatch(/List Options:/); + expect(output).toMatch(/Global Options:/); - assert.strictEqual(exitCode, 0); + expect(exitCode).toBe(0); }) ); @@ -45,9 +44,9 @@ describe('ti module', () => { const { exitCode, stdout } = await run(['module']); const output = stripColor(stdout); - assert.match(output, /No modules found/); + expect(output).toMatch(/No modules found/); - assert.strictEqual(exitCode, 0); + expect(exitCode).toBe(0); }) ); @@ -57,8 +56,8 @@ describe('ti module', () => { const { exitCode, stdout } = await run(['module', '--json']); const json = JSON.parse(stdout); - assert.deepStrictEqual(json, {}); - assert.strictEqual(exitCode, 0); + expect(json).toEqual({}); + expect(exitCode).toBe(0); }) ); @@ -76,7 +75,7 @@ describe('ti module', () => { ]); const output = stripColor(stdout); - assert.match( + expect(output).toMatch( output, new RegExp(`com.test.module Android @@ -114,7 +113,7 @@ describe('ti module', () => { Titanium SDK = >=7.2.0`) ); - assert.strictEqual(exitCode, 0); + expect(exitCode).toBe(0); }) ); @@ -133,7 +132,7 @@ describe('ti module', () => { ]); const json = JSON.parse(stdout); - assert.deepStrictEqual(json, { + expect(json).toEqual({ 'com.test.module': { android: { '1.0.0': { @@ -217,7 +216,7 @@ describe('ti module', () => { }, }); - assert.strictEqual(exitCode, 0); + expect(exitCode).toBe(0); }) ); diff --git a/test/commands/ti-project.test.js b/test/commands/ti-project.test.js index 366a89945..c89f27f45 100644 --- a/test/commands/ti-project.test.js +++ b/test/commands/ti-project.test.js @@ -1,23 +1,22 @@ import { initMockSDKHome } from '../helpers/init-sdk-home.js'; import { stripColor } from '../helpers/strip-color.js'; -import assert from 'node:assert'; -import { describe, it } from 'node:test'; +import { describe, expect, it } from 'vitest'; describe('ti project', () => { it( 'should show help', - initMockSDKHome(async ({ run, _tmpSDKDir }) => { + initMockSDKHome(async ({ run }) => { const { exitCode, stdout } = await run(['project', '-h']); const output = stripColor(stdout); - assert.match(output, /Titanium Command-Line Interface/); - assert.match(output, /Usage: titanium project \[options\]/); - assert.match(output, /Get and set tiapp.xml settings./); - assert.match(output, /Project Arguments:/); - assert.match(output, /Project Options:/); - assert.match(output, /Global Options:/); + expect(output).toMatch(/Titanium Command-Line Interface/); + expect(output).toMatch(/Usage: titanium project \[options\]/); + expect(output).toMatch(/Get and set tiapp.xml settings./); + expect(output).toMatch(/Project Arguments:/); + expect(output).toMatch(/Project Options:/); + expect(output).toMatch(/Global Options:/); - assert.strictEqual(exitCode, 0); + expect(exitCode).toBe(0); }) ); }); diff --git a/test/commands/ti-sdk.test.js b/test/commands/ti-sdk.test.js index c94cda65b..d4692136a 100644 --- a/test/commands/ti-sdk.test.js +++ b/test/commands/ti-sdk.test.js @@ -2,11 +2,10 @@ import { initCLI } from '../helpers/init-cli.js'; import { initSDKHome, initMockSDKHome } from '../helpers/init-sdk-home.js'; import { stripColor } from '../helpers/strip-color.js'; import { exists } from 'node-titanium-sdk/util'; -import assert from 'node:assert'; import { rm } from 'node:fs/promises'; import { join } from 'node:path'; -import { describe, it } from 'node:test'; import { fileURLToPath, pathToFileURL } from 'node:url'; +import { describe, expect, it } from 'vitest'; const fixturesDir = join(fileURLToPath(import.meta.url), '../fixtures/sdk'); const sdkName = '12.2.0.GA'; @@ -26,13 +25,13 @@ describe('ti sdk', () => { const { exitCode, stdout } = await run(['sdk', '-h']); const output = stripColor(stdout); - assert.match(output, /Titanium Command-Line Interface/); - assert.match(output, /Usage: titanium sdk/); - assert.match(output, /Commands:/); - assert.match(output, /SDK Options:/); - assert.match(output, /Global Options:/); + expect(output).toMatch(/Titanium Command-Line Interface/); + expect(output).toMatch(/Usage: titanium sdk/); + expect(output).toMatch(/Commands:/); + expect(output).toMatch(/SDK Options:/); + expect(output).toMatch(/Global Options:/); - assert.strictEqual(exitCode, 0); + expect(exitCode).toBe(0); }) ); @@ -42,13 +41,13 @@ describe('ti sdk', () => { const { exitCode, stdout } = await run(['sdk', 'install', '-h']); const output = stripColor(stdout); - assert.match(output, /Titanium Command-Line Interface/); - assert.match(output, /Usage: titanium sdk install|i/); - assert.match(output, /Install Arguments:/); - assert.match(output, /Install Options:/); - assert.match(output, /Global Options:/); + expect(output).toMatch(/Titanium Command-Line Interface/); + expect(output).toMatch(/Usage: titanium sdk install|i/); + expect(output).toMatch(/Install Arguments:/); + expect(output).toMatch(/Install Options:/); + expect(output).toMatch(/Global Options:/); - assert.strictEqual(exitCode, 0); + expect(exitCode).toBe(0); }) ); @@ -58,12 +57,12 @@ describe('ti sdk', () => { const { exitCode, stdout } = await run(['sdk', 'list', '-h']); const output = stripColor(stdout); - assert.match(output, /Titanium Command-Line Interface/); - assert.match(output, /Usage: titanium sdk list|ls/); - assert.match(output, /List Options:/); - assert.match(output, /Global Options:/); + expect(output).toMatch(/Titanium Command-Line Interface/); + expect(output).toMatch(/Usage: titanium sdk list|ls/); + expect(output).toMatch(/List Options:/); + expect(output).toMatch(/Global Options:/); - assert.strictEqual(exitCode, 0); + expect(exitCode).toBe(0); }) ); @@ -73,13 +72,13 @@ describe('ti sdk', () => { const { exitCode, stdout } = await run(['sdk', 'uninstall', '-h']); const output = stripColor(stdout); - assert.match(output, /Titanium Command-Line Interface/); - assert.match(output, /Usage: titanium sdk uninstall|rm/); - assert.match(output, /Uninstall Arguments:/); - assert.match(output, /Uninstall Options:/); - assert.match(output, /Global Options:/); + expect(output).toMatch(/Titanium Command-Line Interface/); + expect(output).toMatch(/Usage: titanium sdk uninstall|rm/); + expect(output).toMatch(/Uninstall Arguments:/); + expect(output).toMatch(/Uninstall Options:/); + expect(output).toMatch(/Global Options:/); - assert.strictEqual(exitCode, 0); + expect(exitCode).toBe(0); }) ); }); @@ -94,18 +93,18 @@ describe('ti sdk', () => { // eslint-disable-next-line no-unused-vars let { exitCode, stdout, stderr } = await run(['sdk']); // no `ls` to test default subcommand let output = stripColor(stdout); - assert.match(output, /Titanium Command-Line Interface/); - assert.match( + expect(output).toMatch(/Titanium Command-Line Interface/); + expect(output).toMatch( output, new RegExp(`SDK Install Locations:\n[\\s\\S]*${tmpSDKDir.replace(/\\/g, '\\\\')}`) ); - assert.match(output, /No Titanium SDKs found/); + expect(output).toMatch(/No Titanium SDKs found/); // list SDKs as JSON (no SDKs installed) ({ exitCode, stdout } = await run(['sdk', 'ls', '--json'])); let json = JSON.parse(stdout); - assert(json.installLocations.includes(tmpSDKDir)); - assert.deepStrictEqual(json, { + expect(json.installLocations.includes(tmpSDKDir)).toBe(true); + expect(json).toEqual({ branch: {}, branches: { defaultBranch: 'main', @@ -117,7 +116,7 @@ describe('ti sdk', () => { releases: {}, sdks: {}, }); - assert.strictEqual(exitCode, 0); + expect(exitCode).toBe(0); // install an SDK ({ exitCode, stdout, stderr } = await run([ @@ -127,8 +126,8 @@ describe('ti sdk', () => { '--no-progress-bars', '--keep-files', ])); - assert.match(stdout, /successfully installed/); - assert.strictEqual(exitCode, 0); + expect(stdout).toMatch(/successfully installed/); + expect(exitCode).toBe(0); // find the downloaded file and move it to the tmp dir for subsequent tests const src = join(tmpHome, '.titanium', 'downloads', sdkFilename); @@ -141,23 +140,23 @@ describe('ti sdk', () => { // list SDKs ({ exitCode, stdout } = await run(['sdk', 'ls'])); output = stripColor(stdout); - assert.match(output, /Titanium Command-Line Interface/); - assert.match( + expect(output).toMatch(/Titanium Command-Line Interface/); + expect(output).toMatch( output, new RegExp(`SDK Install Locations:\n[\\s\\S]*${tmpSDKDir.replace(/\\/g, '\\\\')}`) ); - assert.match( + expect(output).toMatch( output, new RegExp( `Installed SDKs:\n\\s*${sdkName}\\s+${sdkVersion}\\s+${sdkPath.replace(/\\/g, '\\\\')}` ) ); - assert.strictEqual(exitCode, 0); + expect(exitCode).toBe(0); // list SDKs as JSON ({ exitCode, stdout } = await run(['sdk', 'ls', '--json'])); json = JSON.parse(stdout); - assert.deepStrictEqual(json, { + expect(json).toEqual({ branch: {}, branches: { defaultBranch: 'main', @@ -189,17 +188,17 @@ describe('ti sdk', () => { }, }, }); - assert.strictEqual(exitCode, 0); + expect(exitCode).toBe(0); // remove the SDK ({ exitCode, stdout } = await run(['sdk', 'uninstall', sdkName, '--force'])); - assert.match(stdout, /removed/); - assert.strictEqual(exitCode, 0); + expect(stdout).toMatch(/removed/); + expect(exitCode).toBe(0); // verify removed ({ exitCode, stdout } = await run(['sdk', 'ls', '--json'])); json = JSON.parse(stdout); - assert.deepStrictEqual(json, { + expect(json).toEqual({ branch: {}, branches: { defaultBranch: 'main', @@ -211,7 +210,7 @@ describe('ti sdk', () => { releases: {}, sdks: {}, }); - assert.strictEqual(exitCode, 0); + expect(exitCode).toBe(0); }), 240000 ); @@ -223,12 +222,12 @@ describe('ti sdk', () => { const sdkName = '0.0.0.GA'; const sdkPath = join(tmpSDKDir, 'mobilesdk', os, sdkName); let { exitCode, stdout } = await run(['sdk', 'install', sdkZipFile, '--no-progress-bars']); - assert.match(stdout, /successfully installed/); - assert.strictEqual(exitCode, 0); + expect(stdout).toMatch(/successfully installed/); + expect(exitCode).toBe(0); ({ exitCode, stdout } = await run(['sdk', 'ls', '--json'])); const json = JSON.parse(stdout); - assert.deepStrictEqual(json, { + expect(json).toEqual({ branch: {}, branches: { defaultBranch: 'main', @@ -260,7 +259,7 @@ describe('ti sdk', () => { }, }, }); - assert.strictEqual(exitCode, 0); + expect(exitCode).toBe(0); }), 120000 ); @@ -275,8 +274,8 @@ describe('ti sdk', () => { '--no-progress-bars', ]); const { exitCode, stderr } = result; - assert.match(stderr, /Specified file does not exist/); - assert.strictEqual(exitCode, 1); + expect(stderr).toMatch(/Specified file does not exist/); + expect(exitCode).toBe(1); }) ); @@ -289,8 +288,8 @@ describe('ti sdk', () => { join(fixturesDir, 'not_a_zip'), '--no-progress-bars', ]); - assert.match(stderr, /Specified file is not a zip file/); - assert.strictEqual(exitCode, 1); + expect(stderr).toMatch(/Specified file is not a zip file/); + expect(exitCode).toBe(1); }) ); @@ -298,8 +297,8 @@ describe('ti sdk', () => { 'should install an SDK from a URL', initSDKHome(async ({ _run }) => { // const { exitCode, stderr } = await run(['sdk', 'install', 'https://titaniumsdk.com/', '--no-progress-bars']); - // assert.match(stderr, /Specified file does not exist/); - // assert.strictEqual(exitCode, 1); + // expect(stderr).toMatch(/Specified file does not exist/); + // expect(exitCode).toBe(1); }) ); @@ -314,11 +313,11 @@ describe('ti sdk', () => { 'should error if SDK release not found', initSDKHome(async ({ run }) => { const { exitCode, stderr } = await run(['sdk', 'install', 'foo', '--no-progress-bars']); - assert.match( + expect(stderr).toMatch( stderr, /Unable to find any Titanium SDK releases or CI builds that match "foo"/ ); - assert.strictEqual(exitCode, 1); + expect(exitCode).toBe(1); }) ); }); @@ -330,57 +329,53 @@ describe('ti sdk', () => { // list branches let { exitCode, stdout } = await run(['sdk', 'list', '-b']); let output = stripColor(stdout); - assert.match(output, /Titanium Command-Line Interface/); - assert.match( - output, + expect(output).toMatch(/Titanium Command-Line Interface/); + expect(output).toMatch( new RegExp(`SDK Install Locations:\n[\\s\\S]*${tmpSDKDir.replace(/\\/g, '\\\\')}`) ); - assert.match(output, /Branches:\n\s*(main|master)/); + expect(output).toMatch(/Branches:\n\s*(main|master)/); // list stable releases ({ exitCode, stdout } = await run(['sdk', 'list', '-r'])); output = stripColor(stdout); - assert.match(output, /Titanium Command-Line Interface/); - assert.match( - output, + expect(output).toMatch(/Titanium Command-Line Interface/); + expect(output).toMatch( new RegExp(`SDK Install Locations:\n[\\s\\S]*${tmpSDKDir.replace(/\\/g, '\\\\')}`) ); - assert.match(output, /Releases:/); - assert.match(output, /12\.2\.0\.GA\s+9\/15\/23/); + expect(output).toMatch(/Releases:/); + expect(output).toMatch(/12\.2\.0\.GA\s+9\/15\/23/); // list stable and unstable releases ({ exitCode, stdout } = await run(['sdk', 'list', '-u'])); output = stripColor(stdout); - assert.match(output, /Titanium Command-Line Interface/); - assert.match( - output, + expect(output).toMatch(/Titanium Command-Line Interface/); + expect(output).toMatch( new RegExp(`SDK Install Locations:\n[\\s\\S]*${tmpSDKDir.replace(/\\/g, '\\\\')}`) ); - assert.match(output, /Releases:/); - assert.match(output, /12\.2\.0\.GA\s+9\/15\/23/); - assert.match(output, /12\.2\.0\.RC\s+8\/11\/23/); + expect(output).toMatch(/Releases:/); + expect(output).toMatch(/12\.2\.0\.GA\s+9\/15\/23/); + expect(output).toMatch(/12\.2\.0\.RC\s+8\/11\/23/); // list branch builds ({ exitCode, stdout } = await run(['sdk', 'list', '--branch', 'main'])); output = stripColor(stdout); - assert.match(output, /Titanium Command-Line Interface/); - assert.match( - output, + expect(output).toMatch(/Titanium Command-Line Interface/); + expect(output).toMatch( new RegExp(`SDK Install Locations:\n[\\s\\S]*${tmpSDKDir.replace(/\\/g, '\\\\')}`) ); - assert.match(output, /'main' Branch Builds:/); - // assert.match(output, /\d+\.\d+\.\d+\.v\d+\s+\d+\/\d+\/\d+\s+\d+(\.\d+)? .B \[unstable\]/); + expect(output).toMatch(/'main' Branch Builds:/); + // expect(output).toMatch(/\d+\.\d+\.\d+\.v\d+\s+\d+\/\d+\/\d+\s+\d+(\.\d+)? .B \[unstable\]/); // list branches, stable, and unstable releases as JSON ({ exitCode, stdout } = await run(['sdk', 'ls', '-bu', '--json'])); const json = JSON.parse(stdout); - assert( + expect( json.branches.branches.includes('main') || json.branches.branches.includes('master') - ); - assert(json.branches.branches.includes('12_6_X')); - assert(json.releases[sdkName]); + ).toBe(true); + expect(json.branches.branches).toContain('12_6_X'); + expect(json.releases[sdkName]).toBeDefined(); - assert.strictEqual(exitCode, 0); + expect(exitCode).toBe(0); }), 60000 ); @@ -390,12 +385,11 @@ describe('ti sdk', () => { initSDKHome(async ({ run, tmpSDKDir }) => { const { exitCode, stdout } = await run(['sdk', 'list']); const output = stripColor(stdout); - assert.match( - output, + expect(output).toMatch( new RegExp(`SDK Install Locations:\n[\\s\\S]*${tmpSDKDir.replace(/\\/g, '\\\\')}`) ); - assert.match(output, /No Titanium SDKs found/); - assert.strictEqual(exitCode, 0); + expect(output).toMatch(/No Titanium SDKs found/); + expect(exitCode).toBe(0); }), 60000 ); @@ -405,18 +399,18 @@ describe('ti sdk', () => { initMockSDKHome(async ({ run, tmpSDKDir }) => { const { exitCode, stdout } = await run(['sdk', 'list']); const output = stripColor(stdout); - assert.match( + expect(output).toMatch( output, new RegExp(`SDK Install Locations:\n[\\s\\S]*${tmpSDKDir.replace(/\\/g, '\\\\')}`) ); - assert.match(output, /Installed SDKs:/); - assert.match( + expect(output).toMatch(/Installed SDKs:/); + expect(output).toMatch( output, new RegExp( `0.0.0.GA\\s+0.0.0\\s+${join(tmpSDKDir, 'mobilesdk', os, '0.0.0.GA').replace(/\\/g, '\\\\')}` ) ); - assert.strictEqual(exitCode, 0); + expect(exitCode).toBe(0); }), 60000 ); @@ -429,10 +423,10 @@ describe('ti sdk', () => { const { exitCode, stdout } = await run(['sdk', 'select']); const output = stripColor(stdout); - assert.match(output, /Titanium Command-Line Interface/); - assert.match(output, /The "select" subcommand is no longer required./); + expect(output).toMatch(/Titanium Command-Line Interface/); + expect(output).toMatch(/The "select" subcommand is no longer required./); - assert.strictEqual(exitCode, 0); + expect(exitCode).toBe(0); }) ); }); diff --git a/test/commands/ti-setup.test.js b/test/commands/ti-setup.test.js index e22206496..0ceb57053 100644 --- a/test/commands/ti-setup.test.js +++ b/test/commands/ti-setup.test.js @@ -1,7 +1,6 @@ import { initCLI } from '../helpers/init-cli.js'; import { stripColor } from '../helpers/strip-color.js'; -import assert from 'node:assert'; -import { describe, it } from 'node:test'; +import { describe, expect, it } from 'vitest'; describe('ti setup', () => { it( @@ -10,13 +9,13 @@ describe('ti setup', () => { const { exitCode, stdout } = await run(['setup', '-h']); const output = stripColor(stdout); - assert.match(output, /Titanium Command-Line Interface/); - assert.match(output, /Usage: titanium setup \[options\] \[screen\]/); - assert.match(output, /Setup Arguments:/); - assert.match(output, /Setup Options:/); - assert.match(output, /Global Options:/); + expect(output).toMatch(/Titanium Command-Line Interface/); + expect(output).toMatch(/Usage: titanium setup \[options\] \[screen\]/); + expect(output).toMatch(/Setup Arguments:/); + expect(output).toMatch(/Setup Options:/); + expect(output).toMatch(/Global Options:/); - assert.strictEqual(exitCode, 0); + expect(exitCode).toBe(0); }) ); }); diff --git a/test/commands/ti.test.js b/test/commands/ti.test.js index db5eb2356..cefb25b55 100644 --- a/test/commands/ti.test.js +++ b/test/commands/ti.test.js @@ -1,10 +1,9 @@ import { initCLI } from '../helpers/init-cli.js'; import { stripColor } from '../helpers/strip-color.js'; -import assert from 'node:assert'; import { readFileSync } from 'node:fs'; import { dirname, join } from 'node:path'; -import { describe, it } from 'node:test'; import { fileURLToPath } from 'node:url'; +import { describe, expect, it } from 'vitest'; const __dirname = dirname(fileURLToPath(import.meta.url)); const pkgJson = JSON.parse(readFileSync(join(__dirname, '../../package.json'), 'utf8')); @@ -14,8 +13,8 @@ describe('ti', () => { 'should display the version using short flag', initCLI(async ({ run }) => { const { exitCode, stdout } = await run(['-v']); - assert.strictEqual(stdout, pkgJson.version); - assert.strictEqual(exitCode, 0); + expect(stdout).toBe(pkgJson.version); + expect(exitCode).toBe(0); }) ); @@ -23,8 +22,8 @@ describe('ti', () => { 'should display the version using long flag', initCLI(async ({ run }) => { const { exitCode, stdout } = await run(['--version']); - assert.strictEqual(stdout, pkgJson.version); - assert.strictEqual(exitCode, 0); + expect(stdout).toBe(pkgJson.version); + expect(exitCode).toBe(0); }) ); @@ -34,13 +33,13 @@ describe('ti', () => { const { exitCode, stdout } = await run(); const output = stripColor(stdout); - assert.match(output, /Titanium Command-Line Interface/); - assert.match(output, /Usage: titanium/); - assert.match(output, /Commands:/); - assert.match(output, /Global Options:/); - assert.match(output, /-h, --help/); + expect(output).toMatch(/Titanium Command-Line Interface/); + expect(output).toMatch(/Usage: titanium/); + expect(output).toMatch(/Commands:/); + expect(output).toMatch(/Global Options:/); + expect(output).toMatch(/-h, --help/); - assert.strictEqual(exitCode, 0); + expect(exitCode).toBe(0); }) ); }); diff --git a/test/util/arrayify.test.js b/test/util/arrayify.test.js index 75926a3c4..ddba4cef5 100644 --- a/test/util/arrayify.test.js +++ b/test/util/arrayify.test.js @@ -1,25 +1,24 @@ import { arrayify } from '../../src/util/arrayify.js'; -import assert from 'node:assert'; -import { describe, it } from 'node:test'; +import { describe, expect, it } from 'vitest'; describe('arrayify', () => { it('should init undefined array', () => { - assert.deepStrictEqual(arrayify(), []); + expect(arrayify()).toEqual([]); }); it('should arrayify a non-array', () => { - assert.deepStrictEqual(arrayify(1), [1]); - assert.deepStrictEqual(arrayify('a'), ['a']); - assert.deepStrictEqual(arrayify(true), [true]); - assert.deepStrictEqual(arrayify(false), [false]); + expect(arrayify(1)).toEqual([1]); + expect(arrayify('a')).toEqual(['a']); + expect(arrayify(true)).toEqual([true]); + expect(arrayify(false)).toEqual([false]); }); it('should arrayify a set', () => { - assert.deepStrictEqual(arrayify(new Set([1, 'a', true])), [1, 'a', true]); + expect(arrayify(new Set([1, 'a', true]))).toEqual([1, 'a', true]); }); it('should remove falsey values', () => { - assert.deepStrictEqual(arrayify([0, 1, null, undefined, '', 'a', true, false], true), [ + expect(arrayify([0, 1, null, undefined, '', 'a', true, false], true)).toEqual([ 0, 1, 'a', diff --git a/test/util/busyindicator.test.js b/test/util/busyindicator.test.js index de626c95a..33a26c080 100644 --- a/test/util/busyindicator.test.js +++ b/test/util/busyindicator.test.js @@ -1,8 +1,7 @@ import { BusyIndicator } from '../../src/util/busyindicator.js'; import { MockStream } from '../helpers/mock-stream.js'; -import assert from 'node:assert'; -import { describe, it } from 'node:test'; import { setTimeout } from 'node:timers/promises'; +import { describe, expect, it } from 'vitest'; describe('BusyIndicator', () => { it('should render a busy indicator', async () => { @@ -13,10 +12,10 @@ describe('BusyIndicator', () => { await setTimeout(200); busy.stop(); - assert.match(stream.buffer, / |\n \/\n -\n +\n/); + expect(stream.buffer).toMatch(/ |\n \/\n -\n +\n/); stream.buffer = 'foo'; await setTimeout(100); - assert.strictEqual(stream.buffer, 'foo'); + expect(stream.buffer).toBe('foo'); }); }); diff --git a/test/util/columns.test.js b/test/util/columns.test.js index 94552fb90..397e274d0 100644 --- a/test/util/columns.test.js +++ b/test/util/columns.test.js @@ -1,6 +1,5 @@ import { columns } from '../../src/util/columns.js'; -import assert from 'node:assert'; -import { describe, it } from 'node:test'; +import { describe, expect, it } from 'vitest'; const data = [ 'aaaa', @@ -33,16 +32,15 @@ const data = [ describe('columns', () => { it('should render columns with no wrapping', () => { - assert.strictEqual(columns(['foo', 'bar']), 'foo bar'); + expect(columns(['foo', 'bar'])).toBe('foo bar'); }); it('should render columns with margin', () => { - assert.strictEqual(columns(['foo', 'bar'], ' '), ' foo bar'); + expect(columns(['foo', 'bar'], ' ')).toBe(' foo bar'); }); it('should render lots of data in columns with wrapping', () => { - assert.strictEqual( - columns(data, ' ', 50), + expect(columns(data, ' ', 50)).toBe( [ ' aaaa hhhh oooo vvvv', ' bbbb iiii pppp wwww', @@ -56,8 +54,7 @@ describe('columns', () => { }); it('should render lots of data with huge width', () => { - assert.strictEqual( - columns(data, ' ', 1000), + expect(columns(data, ' ', 1000)).toBe( [ ' aaaa eeee iiii mmmm qqqq uuuu yyyy', ' bbbb ffff jjjj nnnn rrrr vvvv zzzz', @@ -68,8 +65,7 @@ describe('columns', () => { }); it('should render lots of data with zero width', () => { - assert.strictEqual( - columns(data, ' ', 0), + expect(columns(data, ' ', 0)).toBe( [ ' aaaa eeee iiii mmmm qqqq uuuu yyyy', ' bbbb ffff jjjj nnnn rrrr vvvv zzzz', diff --git a/test/util/detect.test.js b/test/util/detect.test.js index 640ea0a47..6098b75e4 100644 --- a/test/util/detect.test.js +++ b/test/util/detect.test.js @@ -1,9 +1,8 @@ import { detect } from '../../src/util/detect.js'; import { TiConfig } from '../../src/util/ticonfig.js'; -import assert from 'node:assert'; import { join } from 'node:path'; -import { describe, it } from 'node:test'; import { fileURLToPath } from 'node:url'; +import { describe, expect, it } from 'vitest'; const goodConfig = join(fileURLToPath(import.meta.url), '../fixtures/ticonfig/good.json'); @@ -13,10 +12,10 @@ describe('detect', () => { version: '1.2.3', }; const results = await detect(null, new TiConfig(goodConfig), mockCLI); - assert(Object.hasOwn(results, 'data')); - assert(Object.hasOwn(results, 'platformInfo')); - assert(Object.hasOwn(results.data.titaniumCLI, 'version')); - assert.strictEqual(results.data.titaniumCLI.version, '1.2.3'); + expect(Object.hasOwn(results, 'data')).toBe(true); + expect(Object.hasOwn(results, 'platformInfo')).toBe(true); + expect(Object.hasOwn(results.data.titaniumCLI, 'version')).toBe(true); + expect(results.data.titaniumCLI.version).toBe('1.2.3'); }, 60000); it('should detect just Titanium development environment', async () => { @@ -24,13 +23,13 @@ describe('detect', () => { version: '1.2.3', }; const results = await detect(null, new TiConfig(goodConfig), mockCLI, { titanium: true }); - assert(Object.hasOwn(results, 'data')); - assert(Object.hasOwn(results, 'platformInfo')); - assert(Object.hasOwn(results.data.titaniumCLI, 'version')); - assert.strictEqual(results.data.titaniumCLI.version, '1.2.3'); - assert.strictEqual(results.data.os, undefined); - assert.strictEqual(results.data.node, undefined); - assert.strictEqual(results.data.npm, undefined); - assert.strictEqual(results.data.jdk, undefined); + expect(Object.hasOwn(results, 'data')).toBe(true); + expect(Object.hasOwn(results, 'platformInfo')).toBe(true); + expect(Object.hasOwn(results.data.titaniumCLI, 'version')).toBe(true); + expect(results.data.titaniumCLI.version).toBe('1.2.3'); + expect(results.data.os).toBe(undefined); + expect(results.data.node).toBe(undefined); + expect(results.data.npm).toBe(undefined); + expect(results.data.jdk).toBe(undefined); }, 60000); }); diff --git a/test/util/logger.test.js b/test/util/logger.test.js index 362a893f6..8871b28d0 100644 --- a/test/util/logger.test.js +++ b/test/util/logger.test.js @@ -1,8 +1,7 @@ import { Logger } from '../../src/util/logger.js'; import { stripColor } from '../helpers/strip-color.js'; import { WritableStream } from 'memory-streams'; -import assert from 'node:assert'; -import { describe, it } from 'node:test'; +import { describe, expect, it } from 'vitest'; describe('Logger', () => { it('should log using all log levels', () => { @@ -22,13 +21,11 @@ describe('Logger', () => { logger.info('info test'); logger.warn('warn test'); - assert.strictEqual( - stripColor(stdout.toString()), + expect(stripColor(stdout.toString())).toBe( ['log test', 'log test', 'log test', '[INFO] info test', ''].join('\n') ); - assert.strictEqual( - stripColor(stderr.toString()), + expect(stripColor(stderr.toString())).toBe( [ '[TRACE] trace test', '[DEBUG] debug test', @@ -56,13 +53,9 @@ describe('Logger', () => { logger.info('info test'); logger.warn('warn test'); - assert.strictEqual( - stripColor(stdout.toString()), - ['log test', 'log test', 'log test', ''].join('\n') - ); + expect(stripColor(stdout.toString())).toBe(['log test', 'log test', 'log test', ''].join('\n')); - assert.strictEqual( - stripColor(stderr.toString()), + expect(stripColor(stderr.toString())).toBe( ['[ERROR] error test', '[WARN] warn test', ''].join('\n') ); }); @@ -79,19 +72,18 @@ describe('Logger', () => { logger.info('info test'); logger.warn('warn test'); - assert.strictEqual(stripColor(stdout.toString()), ['[INFO] info test', ''].join('\n')); + expect(stripColor(stdout.toString())).toBe(['[INFO] info test', ''].join('\n')); - assert.strictEqual(stripColor(stderr.toString()), ['[WARN] warn test', ''].join('\n')); + expect(stripColor(stderr.toString())).toBe(['[WARN] warn test', ''].join('\n')); logger.setLevel('warn'); logger.debug('debug test'); logger.info('info test'); logger.warn('warn test'); - assert.strictEqual(stripColor(stdout.toString()), ['[INFO] info test', ''].join('\n')); + expect(stripColor(stdout.toString())).toBe(['[INFO] info test', ''].join('\n')); - assert.strictEqual( - stripColor(stderr.toString()), + expect(stripColor(stderr.toString())).toBe( ['[WARN] warn test', '[WARN] warn test', ''].join('\n') ); @@ -100,13 +92,11 @@ describe('Logger', () => { logger.info('info test'); logger.warn('warn test'); - assert.strictEqual( - stripColor(stdout.toString()), + expect(stripColor(stdout.toString())).toBe( ['[INFO] info test', '[INFO] info test', ''].join('\n') ); - assert.strictEqual( - stripColor(stderr.toString()), + expect(stripColor(stderr.toString())).toBe( [ '[WARN] warn test', '[WARN] warn test', @@ -130,13 +120,13 @@ describe('Logger', () => { logger.info('info test'); logger.warn('warn test'); - assert.strictEqual(stripColor(stdout.toString()), ''); - assert.strictEqual(stripColor(stderr.toString()), ''); + expect(stripColor(stdout.toString())).toBe(''); + expect(stripColor(stderr.toString())).toBe(''); }); it('should get the levels', () => { const logger = new Logger(); - assert.deepStrictEqual(logger.getLevels(), ['trace', 'debug', 'info', 'warn', 'error']); + expect(logger.getLevels()).toEqual(['trace', 'debug', 'info', 'warn', 'error']); }); it('should display the banner', () => { @@ -157,23 +147,23 @@ describe('Logger', () => { ); logger.setBanner({ name: 'foo', copyright: 'bar', version: '1.2.3', sdkVersion: '4.5.6' }); - assert.match(stripColor(logger.getBanner()), expected); + expect(stripColor(logger.getBanner())).toMatch(expected); - assert.strictEqual(logger.bannerEnabled(), true); - assert.strictEqual(logger.skipBanner(), false); - assert.strictEqual(logger.bannerWasRendered(), false); - assert.strictEqual(emittedCount, 0); + expect(logger.bannerEnabled()).toBe(true); + expect(logger.skipBanner()).toBe(false); + expect(logger.bannerWasRendered()).toBe(false); + expect(emittedCount).toBe(0); logger.banner(); - assert.strictEqual(logger.bannerWasRendered(), true); - assert.strictEqual(emittedCount, 1); + expect(logger.bannerWasRendered()).toBe(true); + expect(emittedCount).toBe(1); - assert.match(stripColor(stdout.toString()), expected); + expect(stripColor(stdout.toString())).toMatch(expected); logger.banner(); - assert.strictEqual(emittedCount, 1); + expect(emittedCount).toBe(1); - assert.match(stripColor(stdout.toString()), expected); + expect(stripColor(stdout.toString())).toMatch(expected); }); it('should not render banner if disabled', () => { @@ -191,15 +181,15 @@ describe('Logger', () => { logger.setBanner({ name: 'foo', copyright: 'bar', version: '1.2.3', sdkVersion: '4.5.6' }); logger.bannerEnabled(false); - assert.strictEqual(logger.bannerEnabled(), false); - assert.strictEqual(logger.skipBanner(), false); - assert.strictEqual(logger.bannerWasRendered(), false); - assert.strictEqual(emittedCount, 0); + expect(logger.bannerEnabled()).toBe(false); + expect(logger.skipBanner()).toBe(false); + expect(logger.bannerWasRendered()).toBe(false); + expect(emittedCount).toBe(0); logger.banner(); - assert.strictEqual(logger.bannerWasRendered(), false); - assert.strictEqual(emittedCount, 0); - assert.strictEqual(stripColor(stdout.toString()), ''); + expect(logger.bannerWasRendered()).toBe(false); + expect(emittedCount).toBe(0); + expect(stripColor(stdout.toString())).toBe(''); }); it('should not render banner if skipped', () => { @@ -216,14 +206,14 @@ describe('Logger', () => { logger.setBanner({ name: 'foo', copyright: 'bar', version: '1.2.3', sdkVersion: '4.5.6' }); - assert.strictEqual(logger.skipBanner(), false); + expect(logger.skipBanner()).toBe(false); logger.skipBanner(true); - assert.strictEqual(logger.skipBanner(), true); + expect(logger.skipBanner()).toBe(true); logger.banner(); - assert.strictEqual(logger.bannerWasRendered(), false); - assert.strictEqual(emittedCount, 0); - assert.strictEqual(stripColor(stdout.toString()), ''); + expect(logger.bannerWasRendered()).toBe(false); + expect(emittedCount).toBe(0); + expect(stripColor(stdout.toString())).toBe(''); }); it('should log with timestamps', () => { @@ -234,11 +224,11 @@ describe('Logger', () => { stderr, }); - assert.strictEqual(logger.timestampEnabled(), false); - assert.strictEqual(logger.timestampEnabled(false), false); - assert.strictEqual(logger.timestampEnabled(true), true); + expect(logger.timestampEnabled()).toBe(false); + expect(logger.timestampEnabled(false)).toBe(false); + expect(logger.timestampEnabled(true)).toBe(true); logger.info('- [INFO] info test'); - assert.match(stripColor(stdout.toString()), /\[INFO\] info test/); + expect(stripColor(stdout.toString())).toMatch(/\[INFO\] info test/); }); }); diff --git a/test/util/progress.test.js b/test/util/progress.test.js index 7913481b5..7eb57b36c 100644 --- a/test/util/progress.test.js +++ b/test/util/progress.test.js @@ -1,7 +1,6 @@ import { ProgressBar } from '../../src/util/progress.js'; import { MockStream } from '../helpers/mock-stream.js'; -import assert from 'node:assert'; -import { describe, it } from 'node:test'; +import { describe, expect, it } from 'vitest'; describe('progress', () => { it('should render a progress bar', async () => { @@ -24,8 +23,7 @@ describe('progress', () => { }, 100); }); - assert.strictEqual( - stream.buffer, + expect(stream.buffer).toBe( ' 10% [====....................................] 1 of 10 (10%)\n' + ' 20% [========................................] 2 of 10 (20%)\n' + ' 30% [============............................] 3 of 10 (30%)\n' + @@ -59,8 +57,7 @@ describe('progress', () => { }, 100); }); - assert.strictEqual( - stream.buffer, + expect(stream.buffer).toBe( ' 10% [====....................................] 1 of 10 (10%)\n' + ' 20% [========................................] 2 of 10 (20%)\n' + ' 30% [============............................] 3 of 10 (30%)\n' + @@ -96,8 +93,7 @@ describe('progress', () => { }, 100); }); - assert.strictEqual( - stream.buffer, + expect(stream.buffer).toBe( ' 10% [====....................................] 0.1\n' + ' 20% [========................................] 0.2\n' + ' 30% [============............................] 0.3\n' + diff --git a/test/util/proxy.test.js b/test/util/proxy.test.js index dd701c939..6a1e4efde 100644 --- a/test/util/proxy.test.js +++ b/test/util/proxy.test.js @@ -1,6 +1,5 @@ import { detect } from '../../src/util/proxy.js'; -import assert from 'node:assert'; -import { afterEach, describe, it } from 'node:test'; +import { afterEach, describe, expect, it } from 'vitest'; describe('proxy', () => { afterEach(() => { @@ -11,26 +10,26 @@ describe('proxy', () => { it('should detect if proxy is present', async () => { let result = await detect(); if (typeof result === 'object') { - assert(Object.hasOwn(result, 'valid')); + expect(Object.hasOwn(result, 'valid')).toBe(true); } else { - assert.strictEqual(result, false); + expect(result).toBe(false); } process.env.http_proxy = 'https://user:pass@localhost:8888'; result = await detect(); if (typeof result === 'object') { - assert(Object.hasOwn(result, 'valid')); + expect(Object.hasOwn(result, 'valid')).toBe(true); } else { - assert.strictEqual(result, false); + expect(result).toBe(false); } delete process.env.http_proxy; process.env.https_proxy = 'https://user:pass@localhost:8888'; result = await detect(); if (typeof result === 'object') { - assert(Object.hasOwn(result, 'valid')); + expect(Object.hasOwn(result, 'valid')).toBe(true); } else { - assert.strictEqual(result, false); + expect(result).toBe(false); } delete process.env.https_proxy; }); diff --git a/test/util/ticonfig.test.js b/test/util/ticonfig.test.js index 9406c0139..39520e2cc 100644 --- a/test/util/ticonfig.test.js +++ b/test/util/ticonfig.test.js @@ -1,10 +1,9 @@ import { TiConfig } from '../../src/util/ticonfig.js'; -import assert from 'node:assert'; import { cp, mkdir, readFile, rmdir, unlink } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; -import { describe, it } from 'node:test'; import { fileURLToPath } from 'node:url'; +import { describe, expect, it } from 'vitest'; const fixturesDir = join(fileURLToPath(import.meta.url), '../fixtures/ticonfig'); @@ -14,43 +13,31 @@ describe('TiConfig', () => { }); it('should error load a bad config file', () => { - assert.throws( - () => { - new TiConfig(join(fixturesDir, 'bad.json')); - }, - { - name: 'Error', - message: /^Unable to parse config file/, - } - ); + expect(() => { + new TiConfig(join(fixturesDir, 'bad.json')); + }).toThrow(/Unable to parse config file/); }); it('should error if file does not exist', () => { - assert.throws( - () => { - new TiConfig(join(fixturesDir, 'does_not_exist')); - }, - { - name: 'Error', - message: /^Unable to open config file/, - } - ); + expect(() => { + new TiConfig(join(fixturesDir, 'does_not_exist')); + }).toThrow(/Unable to open config file/); }); it('should get values', () => { const cfg = new TiConfig(join(fixturesDir, 'good.json')); - assert.strictEqual(cfg.get('user.name'), 'Titanium'); - assert.strictEqual(cfg.get('does.not.exist'), undefined); - assert.strictEqual(cfg.get('does.not.exist', 'Foo'), 'Foo'); - assert.strictEqual(cfg.user.name, 'Titanium'); - assert.strictEqual(cfg.get().user.name, 'Titanium'); - assert.strictEqual(cfg.get('foo', 'bar'), 'bar'); - assert.strictEqual(cfg.get('cli.width', 80), 80); + expect(cfg.get('user.name')).toBe('Titanium'); + expect(cfg.get('does.not.exist')).toBe(undefined); + expect(cfg.get('does.not.exist', 'Foo')).toBe('Foo'); + expect(cfg.user.name).toBe('Titanium'); + expect(cfg.get().user.name).toBe('Titanium'); + expect(cfg.get('foo', 'bar')).toBe('bar'); + expect(cfg.get('cli.width', 80)).toBe(80); }); it('should get the config path', () => { const cfg = new TiConfig(join(fixturesDir, 'good.json')); - assert.match(cfg.getConfigPath(), /good\.json/); + expect(cfg.getConfigPath()).toMatch(/good\.json/); }); it('should apply values', () => { @@ -62,57 +49,57 @@ describe('TiConfig', () => { nil: 'null', empty: '', }); - assert.strictEqual(cfg.get('truthy'), true); - assert.strictEqual(cfg.get('falsey'), false); - assert.strictEqual(cfg.get('undef'), ''); - assert.strictEqual(cfg.get('nil'), null); - assert.strictEqual(cfg.get('empty'), ''); + expect(cfg.get('truthy')).toBe(true); + expect(cfg.get('falsey')).toBe(false); + expect(cfg.get('undef')).toBe(''); + expect(cfg.get('nil')).toBe(null); + expect(cfg.get('empty')).toBe(''); }); it('should set values', () => { const cfg = new TiConfig(join(fixturesDir, 'good.json')); cfg.set('foo', 'bar'); - assert.strictEqual(cfg.get('foo'), 'bar'); - assert.strictEqual(cfg.foo, 'bar'); + expect(cfg.get('foo')).toBe('bar'); + expect(cfg.foo).toBe('bar'); cfg.set('wiz.pow', 'baz'); - assert.deepStrictEqual(cfg.get('wiz.pow'), 'baz'); - assert.deepStrictEqual(cfg.get('wiz'), { pow: 'baz' }); - assert.deepStrictEqual(cfg.wiz, { pow: 'baz' }); + expect(cfg.get('wiz.pow')).toBe('baz'); + expect(cfg.get('wiz')).toEqual({ pow: 'baz' }); + expect(cfg.wiz).toEqual({ pow: 'baz' }); cfg.set('user.favorite.food', 'pizza'); - assert.strictEqual(cfg.user.favorite.food, 'pizza'); + expect(cfg.user.favorite.food).toBe('pizza'); cfg.set('bar'); - assert.strictEqual(cfg.get('bar'), ''); + expect(cfg.get('bar')).toBe(''); cfg.set('bar', 'null'); - assert.strictEqual(cfg.get('bar'), null); + expect(cfg.get('bar')).toBe(null); cfg.set('bar', null); - assert.strictEqual(cfg.get('bar'), null); + expect(cfg.get('bar')).toBe(null); cfg.set('bar', true); - assert.strictEqual(cfg.get('bar'), true); + expect(cfg.get('bar')).toBe(true); cfg.set('bar', 'true'); - assert.strictEqual(cfg.get('bar'), true); + expect(cfg.get('bar')).toBe(true); cfg.set('bar', false); - assert.strictEqual(cfg.get('bar'), false); + expect(cfg.get('bar')).toBe(false); cfg.set('bar', 'false'); - assert.strictEqual(cfg.get('bar'), false); + expect(cfg.get('bar')).toBe(false); cfg.set('bar', 123); - assert.strictEqual(cfg.get('bar'), 123); + expect(cfg.get('bar')).toBe(123); cfg.set('bar', 1.23); - assert.strictEqual(cfg.get('bar'), '1.23'); + expect(cfg.get('bar')).toBe('1.23'); cfg.set('bar', ''); - assert.strictEqual(cfg.get('bar'), ''); - assert.strictEqual(cfg.get('bar', 123), 123); + expect(cfg.get('bar')).toBe(''); + expect(cfg.get('bar', 123)).toBe(123); }); it('should save the config', async () => { @@ -126,10 +113,10 @@ describe('TiConfig', () => { cfg.save(); const json = JSON.parse(await readFile(tmpFile, 'utf-8')); - assert.strictEqual(json.user.name, 'Titanium'); - assert.strictEqual(json.foo, 'bar'); - assert.deepStrictEqual(json.wiz, { pow: 123 }); - assert.strictEqual(json.baz, undefined); + expect(json.user.name).toBe('Titanium'); + expect(json.foo).toBe('bar'); + expect(json.wiz).toEqual({ pow: 123 }); + expect(json.baz).toBe(undefined); } finally { await unlink(tmpFile); } @@ -144,15 +131,9 @@ describe('TiConfig', () => { const cfg = new TiConfig(join(fixturesDir, 'good.json')); cfg.setConfigPath(dir); - assert.throws( - () => { - cfg.save(); - }, - { - name: 'Error', - message: /Unable to write config file/, - } - ); + expect(() => { + cfg.save(); + }).toThrow(/Unable to write config file/); } finally { await rmdir(dir); } @@ -160,15 +141,9 @@ describe('TiConfig', () => { it('should access error saving the file', async () => { const cfg = new TiConfig(join(fixturesDir, 'good.json')); - assert.throws( - () => { - cfg.setConfigPath(join(tmpdir(), 'titanium-cli/foo/bar/config.json')); - cfg.save(); - }, - { - name: 'Error', - message: /Unable to write config file/, - } - ); + expect(() => { + cfg.setConfigPath(join(tmpdir(), 'titanium-cli/foo/bar/config.json')); + cfg.save(); + }).toThrow(/Unable to write config file/); }); }); diff --git a/test/util/tierror.test.js b/test/util/tierror.test.js index 4b55624e7..c8326a62d 100644 --- a/test/util/tierror.test.js +++ b/test/util/tierror.test.js @@ -1,21 +1,20 @@ import { TiError } from '../../src/util/tierror.js'; -import assert from 'node:assert'; -import { describe, it } from 'node:test'; +import { describe, expect, it } from 'vitest'; describe('TiError', () => { it('should support no meta info', () => { const e = new TiError('oh no'); - assert.strictEqual(e.toString(), 'Error: oh no'); + expect(e.toString()).toBe('Error: oh no'); }); it('should mix in meta info', () => { const e = new TiError('oh no', { reason: 'something' }); - assert.strictEqual(e.toString(), 'Error: oh no'); - assert.strictEqual(e.reason, 'something'); + expect(e.toString()).toBe('Error: oh no'); + expect(e.reason).toBe('something'); }); it('should also be an error', () => { const e = new TiError('oh no'); - assert(e instanceof Error); + expect(e instanceof Error).toBe(true); }); }); diff --git a/test/util/unique.test.js b/test/util/unique.test.js index 75e4f41dd..01cdf62c8 100644 --- a/test/util/unique.test.js +++ b/test/util/unique.test.js @@ -1,17 +1,15 @@ import { unique } from '../../src/util/unique.js'; -import assert from 'node:assert'; -import { describe, it } from 'node:test'; +import { describe, expect, it } from 'vitest'; describe('unique', () => { it('should return empty array if no elements', () => { - assert.deepStrictEqual(unique(), []); - assert.deepStrictEqual(unique([]), []); + expect(unique()).toEqual([]); + expect(unique([])).toEqual([]); }); it('should remove duplicates, null, and undefined values', () => { - assert.deepStrictEqual( - unique([1, '1', 'a', true, null, undefined, 1, '1', 'a', true, null, undefined]), - [1, '1', 'a', true] - ); + expect( + unique([1, '1', 'a', true, null, undefined, 1, '1', 'a', true, null, undefined]) + ).toEqual([1, '1', 'a', true]); }); }); diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 000000000..055e41bb2 --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,17 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + allowOnly: true, + coverage: { + include: ['src/**/*.js'], + reporter: ['html', 'lcov', 'text'], + }, + environment: 'node', + globals: false, + include: ['test/**/*.test.js'], + reporters: ['verbose'], + silent: false, + watch: false, + }, +}); From ab1959ee352b00b36057726cad0f42f2191941de Mon Sep 17 00:00:00 2001 From: Chris Barber Date: Wed, 11 Mar 2026 00:13:09 -0500 Subject: [PATCH 13/16] Fix tests --- test/commands/ti-config.test.js | 3 ++- test/commands/ti-sdk.test.js | 7 ++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/test/commands/ti-config.test.js b/test/commands/ti-config.test.js index 34c5e1fd8..e04694bbb 100644 --- a/test/commands/ti-config.test.js +++ b/test/commands/ti-config.test.js @@ -300,6 +300,7 @@ describe('ti config', () => { output = stripColor(stdout); expect(output).toMatch(/paths.modules saved/); expect(exitCode).toBe(0); - }) + }), + 10000 ); }); diff --git a/test/commands/ti-sdk.test.js b/test/commands/ti-sdk.test.js index d4692136a..22166f26d 100644 --- a/test/commands/ti-sdk.test.js +++ b/test/commands/ti-sdk.test.js @@ -126,7 +126,12 @@ describe('ti sdk', () => { '--no-progress-bars', '--keep-files', ])); - expect(stdout).toMatch(/successfully installed/); + try { + expect(stdout).toMatch(/successfully installed/); + } catch (error) { + console.error(stderr); + throw error; + } expect(exitCode).toBe(0); // find the downloaded file and move it to the tmp dir for subsequent tests From 7f97a4f0d47cded18f154646a61420b732c9f78a Mon Sep 17 00:00:00 2001 From: Chris Barber Date: Wed, 11 Mar 2026 00:19:32 -0500 Subject: [PATCH 14/16] Try to fix test --- test/commands/ti-sdk.test.js | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/test/commands/ti-sdk.test.js b/test/commands/ti-sdk.test.js index 22166f26d..78cb4fde2 100644 --- a/test/commands/ti-sdk.test.js +++ b/test/commands/ti-sdk.test.js @@ -92,10 +92,9 @@ describe('ti sdk', () => { // list SDKs (no SDKs installed) // eslint-disable-next-line no-unused-vars let { exitCode, stdout, stderr } = await run(['sdk']); // no `ls` to test default subcommand - let output = stripColor(stdout); + let output = stripColor(`${stdout}\n${stderr}`); expect(output).toMatch(/Titanium Command-Line Interface/); expect(output).toMatch( - output, new RegExp(`SDK Install Locations:\n[\\s\\S]*${tmpSDKDir.replace(/\\/g, '\\\\')}`) ); expect(output).toMatch(/No Titanium SDKs found/); @@ -126,12 +125,7 @@ describe('ti sdk', () => { '--no-progress-bars', '--keep-files', ])); - try { - expect(stdout).toMatch(/successfully installed/); - } catch (error) { - console.error(stderr); - throw error; - } + expect(`${stdout}\n${stderr}`).toMatch(/successfully installed/); expect(exitCode).toBe(0); // find the downloaded file and move it to the tmp dir for subsequent tests @@ -143,15 +137,13 @@ describe('ti sdk', () => { } // list SDKs - ({ exitCode, stdout } = await run(['sdk', 'ls'])); - output = stripColor(stdout); + ({ exitCode, stdout, stderr } = await run(['sdk', 'ls'])); + output = stripColor(`${stdout}\n${stderr}`); expect(output).toMatch(/Titanium Command-Line Interface/); expect(output).toMatch( - output, new RegExp(`SDK Install Locations:\n[\\s\\S]*${tmpSDKDir.replace(/\\/g, '\\\\')}`) ); expect(output).toMatch( - output, new RegExp( `Installed SDKs:\n\\s*${sdkName}\\s+${sdkVersion}\\s+${sdkPath.replace(/\\/g, '\\\\')}` ) From 6dd94765944d93b5c1b42d0809cd64996888cc62 Mon Sep 17 00:00:00 2001 From: Chris Barber Date: Wed, 11 Mar 2026 00:25:32 -0500 Subject: [PATCH 15/16] Try to fix sdk tests on windows --- src/commands/sdk.js | 25 ++++++++++++++++++++++--- test/commands/ti-sdk.test.js | 9 +++++++-- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/commands/sdk.js b/src/commands/sdk.js index 600a9cae8..4a0609436 100644 --- a/src/commands/sdk.js +++ b/src/commands/sdk.js @@ -19,7 +19,7 @@ import { version, } from 'node-titanium-sdk/util'; import { createWriteStream } from 'node:fs'; -import { mkdir, readdir, readFile, rename, rm, writeFile } from 'node:fs/promises'; +import { cp, mkdir, readdir, readFile, rename, rm, writeFile } from 'node:fs/promises'; import os from 'node:os'; import { basename, dirname, join } from 'node:path'; import { Transform } from 'node:stream'; @@ -433,7 +433,7 @@ SdkSubcommands.install = { await mkdir(dest, { recursive: true }); await rm(dest, { force: true, recursive: true }); await mkdir(dest, { recursive: true }); - await rename(src, dest); + await moveDir(src, dest); // step 5: install the modules @@ -479,7 +479,7 @@ SdkSubcommands.install = { trace(` ${cyan(name)}`); await rm(dest, { force: true, recursive: true }); await mkdir(dest, { recursive: true }); - await rename(src, dest); + await moveDir(src, dest); } } else { trace('SDK has new modules to install'); @@ -666,6 +666,25 @@ async function getInstallFile({ branch, config, logger, osName, showProgress, su return { downloadedFile, file }; } +/** + * Move a directory. Uses rename for performance; falls back to copy+remove on + * Windows where rename() fails with EPERM for directories with contents. + * @param {string} src - Source directory path + * @param {string} dest - Destination directory path + */ +async function moveDir(src, dest) { + try { + await rename(src, dest); + } catch (err) { + if (err.code === 'EPERM' || err.code === 'EXDEV') { + await cp(src, dest, { recursive: true }); + await rm(src, { force: true, recursive: true }); + } else { + throw err; + } + } +} + async function extractSDK({ debugLogger, file, diff --git a/test/commands/ti-sdk.test.js b/test/commands/ti-sdk.test.js index 78cb4fde2..646d727cc 100644 --- a/test/commands/ti-sdk.test.js +++ b/test/commands/ti-sdk.test.js @@ -218,8 +218,13 @@ describe('ti sdk', () => { const sdkZipFile = join(fixturesDir, 'mock-sdk.zip'); const sdkName = '0.0.0.GA'; const sdkPath = join(tmpSDKDir, 'mobilesdk', os, sdkName); - let { exitCode, stdout } = await run(['sdk', 'install', sdkZipFile, '--no-progress-bars']); - expect(stdout).toMatch(/successfully installed/); + let { exitCode, stdout, stderr } = await run([ + 'sdk', + 'install', + sdkZipFile, + '--no-progress-bars', + ]); + expect(`${stdout}\n${stderr}`).toMatch(/successfully installed/); expect(exitCode).toBe(0); ({ exitCode, stdout } = await run(['sdk', 'ls', '--json'])); From 2d91e9b992c0fa71d1ef9af450e303a4e3d11c75 Mon Sep 17 00:00:00 2001 From: Chris Barber Date: Wed, 11 Mar 2026 07:03:59 -0500 Subject: [PATCH 16/16] Remove test runner runner --- scripts/test.js | 57 ------------------------------------------------- 1 file changed, 57 deletions(-) delete mode 100644 scripts/test.js diff --git a/scripts/test.js b/scripts/test.js deleted file mode 100644 index 162985843..000000000 --- a/scripts/test.js +++ /dev/null @@ -1,57 +0,0 @@ -import { execa } from 'execa'; -import { glob } from 'glob'; -import { basename, join } from 'node:path'; - -let cover = false; -let only = false; - -const argv = process.argv - .slice(2) - .map((arg) => { - if (arg === '--coverage') { - cover = true; - } else if (arg === '--only') { - only = true; - } else { - return arg; - } - }) - .filter(Boolean); - -const args = []; - -if (cover) { - args.push(join('node_modules', 'c8', 'bin', 'c8.js'), process.execPath); -} - -let tests = await glob(['./test/**/*.test.js']); -tests.sort(); -if (argv.length) { - tests = tests.filter((file) => { - const filename = basename(file); - return argv.some((filter) => filename.includes(filter)); - }); -} - -if (!tests.length) { - console.error('No tests found'); - process.exit(1); -} - -args.push( - `--test${only ? '-only' : ''}`, - '--test-reporter=@reporters/github', - '--test-reporter-destination=stdout', - '--test-reporter=spec', - '--test-reporter-destination=stdout', - ...tests -); - -console.log(`\n> ${process.execPath} ${args.join(' ')}\n\n`); - -await execa(process.execPath, args, { - env: { - TI_CLI_SKIP_ENV_PATHS: 1, - }, - stdio: 'inherit', -});