Files
Mert Can Altin 60cb176373 fix(cli): call defaultRegistryDirectory() when resolving the cache file (#454)
`registry.defaultRegistryDirectory` became a function upstream in
microsoft/playwright#41942 and arrived here with the roll to
1.63.0-alpha-2026-08-05. `cacheFile()` still used it as a string, so
`path.join()` threw, `readCache()`/`writeCache()` swallowed the
TypeError, and the update check was never cached: every single CLI
invocation fetched the npm registry and re-ran the installed-skill
check.

Locally that is ~480ms per command instead of ~125ms.

Tests always set PLAYWRIGHT_CLI_INSTALLATION_FOR_TEST, so the default
branch was never exercised. Add a regression test that points HOME at a
temp directory and asserts the cache file is written.

Also hoist `cacheFile()` out of the try blocks so only I/O and parse
failures are swallowed there, instead of masking a path-computation bug
as "no cache".
2026-08-24 16:15:19 -06:00

116 lines
3.2 KiB
JavaScript
Executable File

#!/usr/bin/env node
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// @ts-check
const fs = require('fs');
const path = require('path');
const { program } = require('playwright-core/lib/tools/cli-client/program');
const coreBundle = require('playwright-core/lib/coreBundle');
const { tools, registry } = coreBundle;
const { checkInstalledSkills, frame } = require('./skillCheck');
const packageJson = require('./package.json');
const ONE_DAY_MS = 24 * 60 * 60 * 1000;
main();
async function main() {
await checkForUpdates().catch(() => {});
program({ embedderVersion: packageJson.version });
}
async function checkForUpdates() {
if (process.env.NO_UPDATE_NOTIFIER || process.env.CI)
return;
const cache = readCache();
const stale = !cache || (Date.now() - cache.lastCheck) > ONE_DAY_MS;
if (!stale)
return;
writeCache({ lastCheck: Date.now() });
const command = process.argv.slice(2).find(arg => !arg.startsWith('-'));
if (command !== 'install')
checkInstalledSkills();
const latest = await fetchLatestVersion();
if (latest && tools.compareSemver(latest, packageJson.version) > 0)
printNotice(packageJson.version, latest);
}
async function fetchLatestVersion() {
try {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 1500);
try {
const res = await fetch(`https://registry.npmjs.org/${packageJson.name}/latest`, { signal: controller.signal });
if (!res.ok)
return undefined;
const json = await res.json();
return typeof json.version === 'string' ? json.version : undefined;
} finally {
clearTimeout(timeout);
}
} catch {
return undefined;
}
}
/**
*
* @param {string} current
* @param {string} latest
*/
function printNotice(current, latest) {
process.stderr.write('\n' + frame([
`Update available for ${packageJson.name}: ${current}${latest}`,
`Run \`npm install -g ${packageJson.name}@latest\` (global) or`,
`\`npm install --save-dev ${packageJson.name}@latest\` (local) to update.`,
]) + '\n');
}
function cacheFile() {
const dir = process.env.PLAYWRIGHT_CLI_INSTALLATION_FOR_TEST || registry.defaultRegistryDirectory();
return path.join(dir, 'cli-update-check.json');
}
function readCache() {
const file = cacheFile();
try {
const data = JSON.parse(fs.readFileSync(file, 'utf8'));
if (typeof data.lastCheck === 'number')
return data;
} catch {
}
return undefined;
}
/**
* @param {*} data
*/
function writeCache(data) {
const file = cacheFile();
try {
fs.mkdirSync(path.dirname(file), { recursive: true });
fs.writeFileSync(file, JSON.stringify(data));
} catch {
}
}