Cache.js 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. "use strict";
  2. /**
  3. * @license
  4. * Copyright 2023 Google Inc.
  5. * SPDX-License-Identifier: Apache-2.0
  6. */
  7. var __importDefault = (this && this.__importDefault) || function (mod) {
  8. return (mod && mod.__esModule) ? mod : { "default": mod };
  9. };
  10. Object.defineProperty(exports, "__esModule", { value: true });
  11. exports.Cache = exports.InstalledBrowser = void 0;
  12. const fs_1 = __importDefault(require("fs"));
  13. const os_1 = __importDefault(require("os"));
  14. const path_1 = __importDefault(require("path"));
  15. const debug_1 = __importDefault(require("debug"));
  16. const browser_data_js_1 = require("./browser-data/browser-data.js");
  17. const detectPlatform_js_1 = require("./detectPlatform.js");
  18. const debugCache = (0, debug_1.default)('puppeteer:browsers:cache');
  19. /**
  20. * @public
  21. */
  22. class InstalledBrowser {
  23. browser;
  24. buildId;
  25. platform;
  26. executablePath;
  27. #cache;
  28. /**
  29. * @internal
  30. */
  31. constructor(cache, browser, buildId, platform) {
  32. this.#cache = cache;
  33. this.browser = browser;
  34. this.buildId = buildId;
  35. this.platform = platform;
  36. this.executablePath = cache.computeExecutablePath({
  37. browser,
  38. buildId,
  39. platform,
  40. });
  41. }
  42. /**
  43. * Path to the root of the installation folder. Use
  44. * {@link computeExecutablePath} to get the path to the executable binary.
  45. */
  46. get path() {
  47. return this.#cache.installationDir(this.browser, this.platform, this.buildId);
  48. }
  49. readMetadata() {
  50. return this.#cache.readMetadata(this.browser);
  51. }
  52. writeMetadata(metadata) {
  53. this.#cache.writeMetadata(this.browser, metadata);
  54. }
  55. }
  56. exports.InstalledBrowser = InstalledBrowser;
  57. /**
  58. * The cache used by Puppeteer relies on the following structure:
  59. *
  60. * - rootDir
  61. * -- <browser1> | browserRoot(browser1)
  62. * ---- <platform>-<buildId> | installationDir()
  63. * ------ the browser-platform-buildId
  64. * ------ specific structure.
  65. * -- <browser2> | browserRoot(browser2)
  66. * ---- <platform>-<buildId> | installationDir()
  67. * ------ the browser-platform-buildId
  68. * ------ specific structure.
  69. * @internal
  70. */
  71. class Cache {
  72. #rootDir;
  73. constructor(rootDir) {
  74. this.#rootDir = rootDir;
  75. }
  76. /**
  77. * @internal
  78. */
  79. get rootDir() {
  80. return this.#rootDir;
  81. }
  82. browserRoot(browser) {
  83. return path_1.default.join(this.#rootDir, browser);
  84. }
  85. metadataFile(browser) {
  86. return path_1.default.join(this.browserRoot(browser), '.metadata');
  87. }
  88. readMetadata(browser) {
  89. const metatadaPath = this.metadataFile(browser);
  90. if (!fs_1.default.existsSync(metatadaPath)) {
  91. return { aliases: {} };
  92. }
  93. // TODO: add type-safe parsing.
  94. const data = JSON.parse(fs_1.default.readFileSync(metatadaPath, 'utf8'));
  95. if (typeof data !== 'object') {
  96. throw new Error('.metadata is not an object');
  97. }
  98. return data;
  99. }
  100. writeMetadata(browser, metadata) {
  101. const metatadaPath = this.metadataFile(browser);
  102. fs_1.default.mkdirSync(path_1.default.dirname(metatadaPath), { recursive: true });
  103. fs_1.default.writeFileSync(metatadaPath, JSON.stringify(metadata, null, 2));
  104. }
  105. resolveAlias(browser, alias) {
  106. const metadata = this.readMetadata(browser);
  107. if (alias === 'latest') {
  108. return Object.values(metadata.aliases || {})
  109. .sort((0, browser_data_js_1.getVersionComparator)(browser))
  110. .at(-1);
  111. }
  112. return metadata.aliases[alias];
  113. }
  114. installationDir(browser, platform, buildId) {
  115. return path_1.default.join(this.browserRoot(browser), `${platform}-${buildId}`);
  116. }
  117. clear() {
  118. fs_1.default.rmSync(this.#rootDir, {
  119. force: true,
  120. recursive: true,
  121. maxRetries: 10,
  122. retryDelay: 500,
  123. });
  124. }
  125. uninstall(browser, platform, buildId) {
  126. const metadata = this.readMetadata(browser);
  127. for (const alias of Object.keys(metadata.aliases)) {
  128. if (metadata.aliases[alias] === buildId) {
  129. delete metadata.aliases[alias];
  130. }
  131. }
  132. fs_1.default.rmSync(this.installationDir(browser, platform, buildId), {
  133. force: true,
  134. recursive: true,
  135. maxRetries: 10,
  136. retryDelay: 500,
  137. });
  138. }
  139. getInstalledBrowsers() {
  140. if (!fs_1.default.existsSync(this.#rootDir)) {
  141. return [];
  142. }
  143. const types = fs_1.default.readdirSync(this.#rootDir);
  144. const browsers = types.filter((t) => {
  145. return Object.values(browser_data_js_1.Browser).includes(t);
  146. });
  147. return browsers.flatMap(browser => {
  148. const files = fs_1.default.readdirSync(this.browserRoot(browser));
  149. return files
  150. .map(file => {
  151. const result = parseFolderPath(path_1.default.join(this.browserRoot(browser), file));
  152. if (!result) {
  153. return null;
  154. }
  155. return new InstalledBrowser(this, browser, result.buildId, result.platform);
  156. })
  157. .filter((item) => {
  158. return item !== null;
  159. });
  160. });
  161. }
  162. computeExecutablePath(options) {
  163. options.platform ??= (0, detectPlatform_js_1.detectBrowserPlatform)();
  164. if (!options.platform) {
  165. throw new Error(`Cannot download a binary for the provided platform: ${os_1.default.platform()} (${os_1.default.arch()})`);
  166. }
  167. try {
  168. options.buildId =
  169. this.resolveAlias(options.browser, options.buildId) ?? options.buildId;
  170. }
  171. catch {
  172. debugCache('could not read .metadata file for the browser');
  173. }
  174. const installationDir = this.installationDir(options.browser, options.platform, options.buildId);
  175. return path_1.default.join(installationDir, browser_data_js_1.executablePathByBrowser[options.browser](options.platform, options.buildId));
  176. }
  177. }
  178. exports.Cache = Cache;
  179. function parseFolderPath(folderPath) {
  180. const name = path_1.default.basename(folderPath);
  181. const splits = name.split('-');
  182. if (splits.length !== 2) {
  183. return;
  184. }
  185. const [platform, buildId] = splits;
  186. if (!buildId || !platform) {
  187. return;
  188. }
  189. return { platform, buildId };
  190. }
  191. //# sourceMappingURL=Cache.js.map