index.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680
  1. /*
  2. Stencil Screenshot v4.20.0 | MIT Licensed | https://stenciljs.com
  3. */
  4. "use strict";
  5. var __create = Object.create;
  6. var __defProp = Object.defineProperty;
  7. var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
  8. var __getOwnPropNames = Object.getOwnPropertyNames;
  9. var __getProtoOf = Object.getPrototypeOf;
  10. var __hasOwnProp = Object.prototype.hasOwnProperty;
  11. var __export = (target, all) => {
  12. for (var name in all)
  13. __defProp(target, name, { get: all[name], enumerable: true });
  14. };
  15. var __copyProps = (to, from, except, desc) => {
  16. if (from && typeof from === "object" || typeof from === "function") {
  17. for (let key of __getOwnPropNames(from))
  18. if (!__hasOwnProp.call(to, key) && key !== except)
  19. __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  20. }
  21. return to;
  22. };
  23. var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
  24. // If the importer is in node compatibility mode or this is not an ESM
  25. // file that has been converted to a CommonJS file using a Babel-
  26. // compatible transform (i.e. "__esModule" has not been set), then set
  27. // "default" to the CommonJS "module.exports" for node compatibility.
  28. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
  29. mod
  30. ));
  31. var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
  32. // src/screenshot/index.ts
  33. var screenshot_exports = {};
  34. __export(screenshot_exports, {
  35. ScreenshotConnector: () => ScreenshotConnector,
  36. ScreenshotLocalConnector: () => ScreenshotLocalConnector
  37. });
  38. module.exports = __toCommonJS(screenshot_exports);
  39. // src/screenshot/connector-base.ts
  40. var import_os = require("os");
  41. var import_path2 = require("path");
  42. // src/screenshot/screenshot-fs.ts
  43. var import_fs = __toESM(require("fs"));
  44. var import_path = __toESM(require("path"));
  45. function fileExists(filePath) {
  46. return new Promise((resolve) => {
  47. import_fs.default.access(filePath, (err2) => resolve(!err2));
  48. });
  49. }
  50. function readFile(filePath) {
  51. return new Promise((resolve, reject) => {
  52. import_fs.default.readFile(filePath, "utf-8", (err2, data) => {
  53. if (err2) {
  54. reject(err2);
  55. } else {
  56. resolve(data);
  57. }
  58. });
  59. });
  60. }
  61. function readFileBuffer(filePath) {
  62. return new Promise((resolve, reject) => {
  63. import_fs.default.readFile(filePath, (err2, data) => {
  64. if (err2) {
  65. reject(err2);
  66. } else {
  67. resolve(data);
  68. }
  69. });
  70. });
  71. }
  72. function writeFile(filePath, data) {
  73. return new Promise((resolve, reject) => {
  74. import_fs.default.writeFile(filePath, data, (err2) => {
  75. if (err2) {
  76. reject(err2);
  77. } else {
  78. resolve();
  79. }
  80. });
  81. });
  82. }
  83. function mkDir(filePath) {
  84. return new Promise((resolve) => {
  85. import_fs.default.mkdir(filePath, () => {
  86. resolve();
  87. });
  88. });
  89. }
  90. function rmDir(filePath) {
  91. return new Promise((resolve) => {
  92. import_fs.default.rmdir(filePath, () => {
  93. resolve();
  94. });
  95. });
  96. }
  97. async function emptyDir(dir) {
  98. const files = await readDir(dir);
  99. const promises = files.map(async (fileName) => {
  100. const filePath = import_path.default.join(dir, fileName);
  101. const isDirFile = await isFile(filePath);
  102. if (isDirFile) {
  103. await unlink(filePath);
  104. }
  105. });
  106. await Promise.all(promises);
  107. }
  108. async function readDir(dir) {
  109. return new Promise((resolve) => {
  110. import_fs.default.readdir(dir, (err2, files) => {
  111. if (err2) {
  112. resolve([]);
  113. } else {
  114. resolve(files);
  115. }
  116. });
  117. });
  118. }
  119. async function isFile(itemPath) {
  120. return new Promise((resolve) => {
  121. import_fs.default.stat(itemPath, (err2, stat) => {
  122. if (err2) {
  123. resolve(false);
  124. } else {
  125. resolve(stat.isFile());
  126. }
  127. });
  128. });
  129. }
  130. async function unlink(filePath) {
  131. return new Promise((resolve) => {
  132. import_fs.default.unlink(filePath, () => {
  133. resolve();
  134. });
  135. });
  136. }
  137. // src/screenshot/connector-base.ts
  138. var ScreenshotConnector = class {
  139. constructor() {
  140. this.screenshotDirName = "screenshot";
  141. this.imagesDirName = "images";
  142. this.buildsDirName = "builds";
  143. this.masterBuildFileName = "master.json";
  144. this.screenshotCacheFileName = "screenshot-cache.json";
  145. }
  146. async initBuild(opts) {
  147. this.logger = opts.logger;
  148. this.buildId = opts.buildId;
  149. this.buildMessage = opts.buildMessage || "";
  150. this.buildAuthor = opts.buildAuthor;
  151. this.buildUrl = opts.buildUrl;
  152. this.previewUrl = opts.previewUrl;
  153. this.buildTimestamp = typeof opts.buildTimestamp === "number" ? opts.buildTimestamp : Date.now();
  154. this.cacheDir = opts.cacheDir;
  155. this.packageDir = opts.packageDir;
  156. this.rootDir = opts.rootDir;
  157. this.appNamespace = opts.appNamespace;
  158. this.waitBeforeScreenshot = opts.waitBeforeScreenshot;
  159. this.pixelmatchModulePath = opts.pixelmatchModulePath;
  160. if (!opts.logger) {
  161. throw new Error(`logger option required`);
  162. }
  163. if (typeof opts.buildId !== "string") {
  164. throw new Error(`buildId option required`);
  165. }
  166. if (typeof opts.cacheDir !== "string") {
  167. throw new Error(`cacheDir option required`);
  168. }
  169. if (typeof opts.packageDir !== "string") {
  170. throw new Error(`packageDir option required`);
  171. }
  172. if (typeof opts.rootDir !== "string") {
  173. throw new Error(`rootDir option required`);
  174. }
  175. this.updateMaster = !!opts.updateMaster;
  176. this.allowableMismatchedPixels = opts.allowableMismatchedPixels;
  177. this.allowableMismatchedRatio = opts.allowableMismatchedRatio;
  178. this.pixelmatchThreshold = opts.pixelmatchThreshold;
  179. this.logger.debug(`screenshot build: ${this.buildId}, ${this.buildMessage}, updateMaster: ${this.updateMaster}`);
  180. this.logger.debug(
  181. `screenshot, allowableMismatchedPixels: ${this.allowableMismatchedPixels}, allowableMismatchedRatio: ${this.allowableMismatchedRatio}, pixelmatchThreshold: ${this.pixelmatchThreshold}`
  182. );
  183. if (typeof opts.screenshotDirName === "string") {
  184. this.screenshotDirName = opts.screenshotDirName;
  185. }
  186. if (typeof opts.imagesDirName === "string") {
  187. this.imagesDirName = opts.imagesDirName;
  188. }
  189. if (typeof opts.buildsDirName === "string") {
  190. this.buildsDirName = opts.buildsDirName;
  191. }
  192. this.screenshotDir = (0, import_path2.join)(this.rootDir, this.screenshotDirName);
  193. this.imagesDir = (0, import_path2.join)(this.screenshotDir, this.imagesDirName);
  194. this.buildsDir = (0, import_path2.join)(this.screenshotDir, this.buildsDirName);
  195. this.masterBuildFilePath = (0, import_path2.join)(this.buildsDir, this.masterBuildFileName);
  196. this.screenshotCacheFilePath = (0, import_path2.join)(this.cacheDir, this.screenshotCacheFileName);
  197. this.currentBuildDir = (0, import_path2.join)((0, import_os.tmpdir)(), "screenshot-build-" + this.buildId);
  198. this.logger.debug(`screenshotDirPath: ${this.screenshotDir}`);
  199. this.logger.debug(`imagesDirPath: ${this.imagesDir}`);
  200. this.logger.debug(`buildsDirPath: ${this.buildsDir}`);
  201. this.logger.debug(`currentBuildDir: ${this.currentBuildDir}`);
  202. this.logger.debug(`cacheDir: ${this.cacheDir}`);
  203. await mkDir(this.screenshotDir);
  204. await Promise.all([
  205. mkDir(this.imagesDir),
  206. mkDir(this.buildsDir),
  207. mkDir(this.currentBuildDir),
  208. mkDir(this.cacheDir)
  209. ]);
  210. }
  211. async pullMasterBuild() {
  212. }
  213. async getMasterBuild() {
  214. try {
  215. const masterBuild = JSON.parse(await readFile(this.masterBuildFilePath));
  216. return masterBuild;
  217. } catch (e) {
  218. }
  219. return null;
  220. }
  221. async completeBuild(masterBuild) {
  222. const filePaths = (await readDir(this.currentBuildDir)).map((f) => (0, import_path2.join)(this.currentBuildDir, f)).filter((f) => f.endsWith(".json"));
  223. const screenshots = await Promise.all(filePaths.map(async (f) => JSON.parse(await readFile(f))));
  224. this.sortScreenshots(screenshots);
  225. if (!masterBuild) {
  226. masterBuild = {
  227. id: this.buildId,
  228. message: this.buildMessage,
  229. author: this.buildAuthor,
  230. url: this.buildUrl,
  231. previewUrl: this.previewUrl,
  232. appNamespace: this.appNamespace,
  233. timestamp: this.buildTimestamp,
  234. screenshots
  235. };
  236. }
  237. const results = {
  238. appNamespace: this.appNamespace,
  239. masterBuild,
  240. currentBuild: {
  241. id: this.buildId,
  242. message: this.buildMessage,
  243. author: this.buildAuthor,
  244. url: this.buildUrl,
  245. previewUrl: this.previewUrl,
  246. appNamespace: this.appNamespace,
  247. timestamp: this.buildTimestamp,
  248. screenshots
  249. },
  250. compare: {
  251. id: `${masterBuild.id}-${this.buildId}`,
  252. a: {
  253. id: masterBuild.id,
  254. message: masterBuild.message,
  255. author: masterBuild.author,
  256. url: masterBuild.url,
  257. previewUrl: masterBuild.previewUrl
  258. },
  259. b: {
  260. id: this.buildId,
  261. message: this.buildMessage,
  262. author: this.buildAuthor,
  263. url: this.buildUrl,
  264. previewUrl: this.previewUrl
  265. },
  266. url: null,
  267. appNamespace: this.appNamespace,
  268. timestamp: this.buildTimestamp,
  269. diffs: []
  270. }
  271. };
  272. results.currentBuild.screenshots.forEach((screenshot) => {
  273. screenshot.diff.device = screenshot.diff.device || screenshot.diff.userAgent;
  274. results.compare.diffs.push(screenshot.diff);
  275. delete screenshot.diff;
  276. });
  277. this.sortCompares(results.compare.diffs);
  278. await emptyDir(this.currentBuildDir);
  279. await rmDir(this.currentBuildDir);
  280. return results;
  281. }
  282. async publishBuild(results) {
  283. return results;
  284. }
  285. async generateJsonpDataUris(build) {
  286. if (build && Array.isArray(build.screenshots)) {
  287. for (let i = 0; i < build.screenshots.length; i++) {
  288. const screenshot = build.screenshots[i];
  289. const jsonpFileName = `screenshot_${screenshot.image}.js`;
  290. const jsonFilePath = (0, import_path2.join)(this.cacheDir, jsonpFileName);
  291. const jsonpExists = await fileExists(jsonFilePath);
  292. if (!jsonpExists) {
  293. const imageFilePath = (0, import_path2.join)(this.imagesDir, screenshot.image);
  294. const imageBuf = await readFileBuffer(imageFilePath);
  295. const jsonpContent = `loadScreenshot("${screenshot.image}","data:image/png;base64,${imageBuf.toString(
  296. "base64"
  297. )}");`;
  298. await writeFile(jsonFilePath, jsonpContent);
  299. }
  300. }
  301. }
  302. }
  303. async getScreenshotCache() {
  304. return null;
  305. }
  306. async updateScreenshotCache(screenshotCache, buildResults) {
  307. screenshotCache = screenshotCache || {};
  308. screenshotCache.timestamp = this.buildTimestamp;
  309. screenshotCache.lastBuildId = this.buildId;
  310. screenshotCache.size = 0;
  311. screenshotCache.items = screenshotCache.items || [];
  312. if (buildResults && buildResults.compare && Array.isArray(buildResults.compare.diffs)) {
  313. buildResults.compare.diffs.forEach((diff) => {
  314. if (typeof diff.cacheKey !== "string") {
  315. return;
  316. }
  317. if (diff.imageA === diff.imageB) {
  318. return;
  319. }
  320. const existingItem = screenshotCache.items.find((i) => i.key === diff.cacheKey);
  321. if (existingItem) {
  322. existingItem.ts = this.buildTimestamp;
  323. } else {
  324. screenshotCache.items.push({
  325. key: diff.cacheKey,
  326. ts: this.buildTimestamp,
  327. mp: diff.mismatchedPixels
  328. });
  329. }
  330. });
  331. }
  332. screenshotCache.items.sort((a, b) => {
  333. if (a.ts > b.ts) return -1;
  334. if (a.ts < b.ts) return 1;
  335. if (a.mp > b.mp) return -1;
  336. if (a.mp < b.mp) return 1;
  337. return 0;
  338. });
  339. screenshotCache.items = screenshotCache.items.slice(0, 1e3);
  340. screenshotCache.size = screenshotCache.items.length;
  341. return screenshotCache;
  342. }
  343. toJson(masterBuild, screenshotCache) {
  344. const masterScreenshots = {};
  345. if (masterBuild && Array.isArray(masterBuild.screenshots)) {
  346. masterBuild.screenshots.forEach((masterScreenshot) => {
  347. masterScreenshots[masterScreenshot.id] = masterScreenshot.image;
  348. });
  349. }
  350. const mismatchCache = {};
  351. if (screenshotCache && Array.isArray(screenshotCache.items)) {
  352. screenshotCache.items.forEach((cacheItem) => {
  353. mismatchCache[cacheItem.key] = cacheItem.mp;
  354. });
  355. }
  356. const screenshotBuild = {
  357. buildId: this.buildId,
  358. rootDir: this.rootDir,
  359. screenshotDir: this.screenshotDir,
  360. imagesDir: this.imagesDir,
  361. buildsDir: this.buildsDir,
  362. masterScreenshots,
  363. cache: mismatchCache,
  364. currentBuildDir: this.currentBuildDir,
  365. updateMaster: this.updateMaster,
  366. allowableMismatchedPixels: this.allowableMismatchedPixels,
  367. allowableMismatchedRatio: this.allowableMismatchedRatio,
  368. pixelmatchThreshold: this.pixelmatchThreshold,
  369. timeoutBeforeScreenshot: this.waitBeforeScreenshot,
  370. pixelmatchModulePath: this.pixelmatchModulePath
  371. };
  372. return JSON.stringify(screenshotBuild);
  373. }
  374. sortScreenshots(screenshots) {
  375. return screenshots.sort((a, b) => {
  376. if (a.desc && b.desc) {
  377. if (a.desc.toLowerCase() < b.desc.toLowerCase()) return -1;
  378. if (a.desc.toLowerCase() > b.desc.toLowerCase()) return 1;
  379. }
  380. if (a.device && b.device) {
  381. if (a.device.toLowerCase() < b.device.toLowerCase()) return -1;
  382. if (a.device.toLowerCase() > b.device.toLowerCase()) return 1;
  383. }
  384. if (a.userAgent && b.userAgent) {
  385. if (a.userAgent.toLowerCase() < b.userAgent.toLowerCase()) return -1;
  386. if (a.userAgent.toLowerCase() > b.userAgent.toLowerCase()) return 1;
  387. }
  388. if (a.width < b.width) return -1;
  389. if (a.width > b.width) return 1;
  390. if (a.height < b.height) return -1;
  391. if (a.height > b.height) return 1;
  392. if (a.id < b.id) return -1;
  393. if (a.id > b.id) return 1;
  394. return 0;
  395. });
  396. }
  397. sortCompares(compares) {
  398. return compares.sort((a, b) => {
  399. if (a.allowableMismatchedPixels > b.allowableMismatchedPixels) return -1;
  400. if (a.allowableMismatchedPixels < b.allowableMismatchedPixels) return 1;
  401. if (a.allowableMismatchedRatio > b.allowableMismatchedRatio) return -1;
  402. if (a.allowableMismatchedRatio < b.allowableMismatchedRatio) return 1;
  403. if (a.desc && b.desc) {
  404. if (a.desc.toLowerCase() < b.desc.toLowerCase()) return -1;
  405. if (a.desc.toLowerCase() > b.desc.toLowerCase()) return 1;
  406. }
  407. if (a.device && b.device) {
  408. if (a.device.toLowerCase() < b.device.toLowerCase()) return -1;
  409. if (a.device.toLowerCase() > b.device.toLowerCase()) return 1;
  410. }
  411. if (a.userAgent && b.userAgent) {
  412. if (a.userAgent.toLowerCase() < b.userAgent.toLowerCase()) return -1;
  413. if (a.userAgent.toLowerCase() > b.userAgent.toLowerCase()) return 1;
  414. }
  415. if (a.width < b.width) return -1;
  416. if (a.width > b.width) return 1;
  417. if (a.height < b.height) return -1;
  418. if (a.height > b.height) return 1;
  419. if (a.id < b.id) return -1;
  420. if (a.id > b.id) return 1;
  421. return 0;
  422. });
  423. }
  424. };
  425. // src/utils/path.ts
  426. var normalizePath = (path2, relativize = true) => {
  427. if (typeof path2 !== "string") {
  428. throw new Error(`invalid path to normalize`);
  429. }
  430. path2 = normalizeSlashes(path2.trim());
  431. const components = pathComponents(path2, getRootLength(path2));
  432. const reducedComponents = reducePathComponents(components);
  433. const rootPart = reducedComponents[0];
  434. const secondPart = reducedComponents[1];
  435. const normalized = rootPart + reducedComponents.slice(1).join("/");
  436. if (normalized === "") {
  437. return ".";
  438. }
  439. if (rootPart === "" && secondPart && path2.includes("/") && !secondPart.startsWith(".") && !secondPart.startsWith("@") && relativize) {
  440. return "./" + normalized;
  441. }
  442. return normalized;
  443. };
  444. var normalizeSlashes = (path2) => path2.replace(backslashRegExp, "/");
  445. var altDirectorySeparator = "\\";
  446. var urlSchemeSeparator = "://";
  447. var backslashRegExp = /\\/g;
  448. var reducePathComponents = (components) => {
  449. if (!Array.isArray(components) || components.length === 0) {
  450. return [];
  451. }
  452. const reduced = [components[0]];
  453. for (let i = 1; i < components.length; i++) {
  454. const component = components[i];
  455. if (!component) continue;
  456. if (component === ".") continue;
  457. if (component === "..") {
  458. if (reduced.length > 1) {
  459. if (reduced[reduced.length - 1] !== "..") {
  460. reduced.pop();
  461. continue;
  462. }
  463. } else if (reduced[0]) continue;
  464. }
  465. reduced.push(component);
  466. }
  467. return reduced;
  468. };
  469. var getRootLength = (path2) => {
  470. const rootLength = getEncodedRootLength(path2);
  471. return rootLength < 0 ? ~rootLength : rootLength;
  472. };
  473. var getEncodedRootLength = (path2) => {
  474. if (!path2) return 0;
  475. const ch0 = path2.charCodeAt(0);
  476. if (ch0 === 47 /* slash */ || ch0 === 92 /* backslash */) {
  477. if (path2.charCodeAt(1) !== ch0) return 1;
  478. const p1 = path2.indexOf(ch0 === 47 /* slash */ ? "/" : altDirectorySeparator, 2);
  479. if (p1 < 0) return path2.length;
  480. return p1 + 1;
  481. }
  482. if (isVolumeCharacter(ch0) && path2.charCodeAt(1) === 58 /* colon */) {
  483. const ch2 = path2.charCodeAt(2);
  484. if (ch2 === 47 /* slash */ || ch2 === 92 /* backslash */) return 3;
  485. if (path2.length === 2) return 2;
  486. }
  487. const schemeEnd = path2.indexOf(urlSchemeSeparator);
  488. if (schemeEnd !== -1) {
  489. const authorityStart = schemeEnd + urlSchemeSeparator.length;
  490. const authorityEnd = path2.indexOf("/", authorityStart);
  491. if (authorityEnd !== -1) {
  492. const scheme = path2.slice(0, schemeEnd);
  493. const authority = path2.slice(authorityStart, authorityEnd);
  494. if (scheme === "file" && (authority === "" || authority === "localhost") && isVolumeCharacter(path2.charCodeAt(authorityEnd + 1))) {
  495. const volumeSeparatorEnd = getFileUrlVolumeSeparatorEnd(path2, authorityEnd + 2);
  496. if (volumeSeparatorEnd !== -1) {
  497. if (path2.charCodeAt(volumeSeparatorEnd) === 47 /* slash */) {
  498. return ~(volumeSeparatorEnd + 1);
  499. }
  500. if (volumeSeparatorEnd === path2.length) {
  501. return ~volumeSeparatorEnd;
  502. }
  503. }
  504. }
  505. return ~(authorityEnd + 1);
  506. }
  507. return ~path2.length;
  508. }
  509. return 0;
  510. };
  511. var isVolumeCharacter = (charCode) => charCode >= 97 /* a */ && charCode <= 122 /* z */ || charCode >= 65 /* A */ && charCode <= 90 /* Z */;
  512. var getFileUrlVolumeSeparatorEnd = (url, start) => {
  513. const ch0 = url.charCodeAt(start);
  514. if (ch0 === 58 /* colon */) return start + 1;
  515. if (ch0 === 37 /* percent */ && url.charCodeAt(start + 1) === 51 /* _3 */) {
  516. const ch2 = url.charCodeAt(start + 2);
  517. if (ch2 === 97 /* a */ || ch2 === 65 /* A */) return start + 3;
  518. }
  519. return -1;
  520. };
  521. var pathComponents = (path2, rootLength) => {
  522. const root = path2.substring(0, rootLength);
  523. const rest = path2.substring(rootLength).split("/");
  524. const restLen = rest.length;
  525. if (restLen > 0 && !rest[restLen - 1]) {
  526. rest.pop();
  527. }
  528. return [root, ...rest];
  529. };
  530. // src/utils/result.ts
  531. var result_exports = {};
  532. __export(result_exports, {
  533. err: () => err,
  534. map: () => map,
  535. ok: () => ok,
  536. unwrap: () => unwrap,
  537. unwrapErr: () => unwrapErr
  538. });
  539. var ok = (value) => ({
  540. isOk: true,
  541. isErr: false,
  542. value
  543. });
  544. var err = (value) => ({
  545. isOk: false,
  546. isErr: true,
  547. value
  548. });
  549. function map(result, fn) {
  550. if (result.isOk) {
  551. const val = fn(result.value);
  552. if (val instanceof Promise) {
  553. return val.then((newVal) => ok(newVal));
  554. } else {
  555. return ok(val);
  556. }
  557. }
  558. if (result.isErr) {
  559. const value = result.value;
  560. return err(value);
  561. }
  562. throw "should never get here";
  563. }
  564. var unwrap = (result) => {
  565. if (result.isOk) {
  566. return result.value;
  567. } else {
  568. throw result.value;
  569. }
  570. };
  571. var unwrapErr = (result) => {
  572. if (result.isErr) {
  573. return result.value;
  574. } else {
  575. throw result.value;
  576. }
  577. };
  578. // src/screenshot/connector-local.ts
  579. var import_path4 = require("path");
  580. var ScreenshotLocalConnector = class extends ScreenshotConnector {
  581. async publishBuild(results) {
  582. if (this.updateMaster || !results.masterBuild) {
  583. results.masterBuild = {
  584. id: "master",
  585. message: "Master",
  586. appNamespace: this.appNamespace,
  587. timestamp: Date.now(),
  588. screenshots: []
  589. };
  590. }
  591. results.currentBuild.screenshots.forEach((currentScreenshot) => {
  592. const masterHasScreenshot = results.masterBuild.screenshots.some((masterScreenshot) => {
  593. return currentScreenshot.id === masterScreenshot.id;
  594. });
  595. if (!masterHasScreenshot) {
  596. results.masterBuild.screenshots.push(Object.assign({}, currentScreenshot));
  597. }
  598. });
  599. this.sortScreenshots(results.masterBuild.screenshots);
  600. await writeFile(this.masterBuildFilePath, JSON.stringify(results.masterBuild, null, 2));
  601. await this.generateJsonpDataUris(results.currentBuild);
  602. const compareAppSourceDir = (0, import_path4.join)(this.packageDir, "screenshot", "compare");
  603. const appSrcUrl = normalizePath((0, import_path4.relative)(this.screenshotDir, compareAppSourceDir));
  604. const imagesUrl = normalizePath((0, import_path4.relative)(this.screenshotDir, this.imagesDir));
  605. const jsonpUrl = normalizePath((0, import_path4.relative)(this.screenshotDir, this.cacheDir));
  606. const compareAppHtml = createLocalCompareApp(
  607. this.appNamespace,
  608. appSrcUrl,
  609. imagesUrl,
  610. jsonpUrl,
  611. results.masterBuild,
  612. results.currentBuild
  613. );
  614. const compareAppFileName = "compare.html";
  615. const compareAppFilePath = (0, import_path4.join)(this.screenshotDir, compareAppFileName);
  616. await writeFile(compareAppFilePath, compareAppHtml);
  617. const gitIgnorePath = (0, import_path4.join)(this.screenshotDir, ".gitignore");
  618. const gitIgnoreExists = await fileExists(gitIgnorePath);
  619. if (!gitIgnoreExists) {
  620. const content = [this.imagesDirName, this.buildsDirName, compareAppFileName];
  621. await writeFile(gitIgnorePath, content.join("\n"));
  622. }
  623. const url = new URL(`file://${compareAppFilePath}`);
  624. results.compare.url = url.href;
  625. return results;
  626. }
  627. async getScreenshotCache() {
  628. let screenshotCache = null;
  629. try {
  630. screenshotCache = JSON.parse(await readFile(this.screenshotCacheFilePath));
  631. } catch (e) {
  632. }
  633. return screenshotCache;
  634. }
  635. async updateScreenshotCache(cache, buildResults) {
  636. cache = await super.updateScreenshotCache(cache, buildResults);
  637. await writeFile(this.screenshotCacheFilePath, JSON.stringify(cache, null, 2));
  638. return cache;
  639. }
  640. };
  641. function createLocalCompareApp(namespace, appSrcUrl, imagesUrl, jsonpUrl, a, b) {
  642. return `<!doctype html>
  643. <html dir="ltr" lang="en">
  644. <head>
  645. <meta charset="utf-8">
  646. <title>Local ${namespace || ""} - Stencil Screenshot Visual Diff</title>
  647. <meta name="viewport" content="viewport-fit=cover, width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
  648. <meta http-equiv="x-ua-compatible" content="IE=Edge">
  649. <link href="${appSrcUrl}/build/app.css" rel="stylesheet">
  650. <script type="module" src="${appSrcUrl}/build/app.esm.js"></script>
  651. <script nomodule src="${appSrcUrl}/build/app.js"></script>
  652. <link rel="icon" type="image/x-icon" href="${appSrcUrl}/assets/favicon.ico">
  653. </head>
  654. <body>
  655. <script>
  656. (function() {
  657. var app = document.createElement('screenshot-compare');
  658. app.appSrcUrl = '${appSrcUrl}';
  659. app.imagesUrl = '${imagesUrl}/';
  660. app.jsonpUrl = '${jsonpUrl}/';
  661. app.a = ${JSON.stringify(a)};
  662. app.b = ${JSON.stringify(b)};
  663. document.body.appendChild(app);
  664. })();
  665. </script>
  666. </body>
  667. </html>`;
  668. }
  669. // Annotate the CommonJS export names for ESM import in node:
  670. 0 && (module.exports = {
  671. ScreenshotConnector,
  672. ScreenshotLocalConnector
  673. });