Skip to content

Commit

Permalink
feat(pptr): switched over puppeteer-core to avoid chromium install
Browse files Browse the repository at this point in the history
Switched over using puppeteer-core to avoid chromium download on every install. With
chrome-launcher, lib will first try to locate any Chromium instance that is installed on users
system, and only if not found, it will installed preferred chromium revision that is declared on
puppeteer-core pkg.

fix #50
  • Loading branch information
onderceylan committed Oct 21, 2019
1 parent 9311962 commit 11045a1
Show file tree
Hide file tree
Showing 7 changed files with 232 additions and 22 deletions.
3 changes: 2 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
"rules": {
"no-prototype-builtins": "off",
"import/no-extraneous-dependencies": "off",
"import/prefer-default-export": "off"
"import/prefer-default-export": "off",
"global-require": "off"
},
"settings": {
"import/resolver": {
Expand Down
7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,13 @@
"dependencies": {
"chalk": "2.4.2",
"cheerio": "1.0.0-rc.3",
"chrome-launcher": "0.11.2",
"lodash.isequal": "4.5.0",
"lodash.uniqwith": "4.5.0",
"meow": "5.0.0",
"mime-types": "2.1.24",
"puppeteer": "1.19.0",
"progress": "2.0.3",
"puppeteer-core": "1.20.0",
"slash": "3.0.0",
"tslib": "1.10.0"
},
Expand All @@ -67,7 +69,8 @@
"@types/lodash.uniqwith": "^4.5.6",
"@types/meow": "^5.0.0",
"@types/mime-types": "^2.1.0",
"@types/puppeteer": "^1.19.1",
"@types/progress": "^2.0.3",
"@types/puppeteer-core": "^1.9.0",
"@typescript-eslint/eslint-plugin": "^2.2.0",
"@typescript-eslint/parser": "^2.2.0",
"cz-conventional-changelog": "^3.0.2",
Expand Down
1 change: 1 addition & 0 deletions src/config/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ export default {
'--disable-default-apps',
'--mute-audio',
'--no-first-run',
'--headless',
],
EMULATED_USER_AGENT:
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3641.0 Safari/537.36',
Expand Down
123 changes: 123 additions & 0 deletions src/helpers/browser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import puppeteer, {
Browser,
LaunchOptions,
RevisionInfo,
} from 'puppeteer-core';
import {
launch,
LaunchedChrome,
Options as ChromeLauncherOptions,
} from 'chrome-launcher';
import { get } from 'http';
import constants from '../config/constants';
import installer from './installer';

interface BrowserVersionInfo {
Browser: string;
webSocketDebuggerUrl: string;
'Protocol-Version': string;
'User-Agent': string;
'V8-Version': string;
'Webkit-Version': string;
}

const isPreferredBrowserRevisionInstalled = (): boolean => {
const revisionInfo = installer.getPreferredBrowserRevisionInfo();
return revisionInfo.local;
};

const getLocalRevisionList = (): Promise<string[]> => {
return installer.getBrowserFetcher().localRevisions();
};

const getLocalRevisionInfo = async (): Promise<RevisionInfo | undefined> => {
if (isPreferredBrowserRevisionInstalled()) {
return installer.getPreferredBrowserRevisionInfo();
}

const localRevisions = await getLocalRevisionList();

if (localRevisions.length > 0) {
const lastRevision = localRevisions.pop() as string;
return installer.getBrowserFetcher().revisionInfo(lastRevision);
}

return undefined;
};

const getLocalBrowserInstance = async (
launchArgs?: LaunchOptions,
): Promise<Browser> => {
let revisionInfo: RevisionInfo;
const localRevisionInfo = await getLocalRevisionInfo();

if (localRevisionInfo) {
revisionInfo = localRevisionInfo;
} else {
revisionInfo = await installer.installPreferredBrowserRevision();
}

return puppeteer.launch({
...launchArgs,
executablePath: revisionInfo.executablePath,
});
};

const launchSystemBrowser = async (): Promise<LaunchedChrome> => {
const launchOptions: ChromeLauncherOptions = {
chromeFlags: constants.PUPPETEER_LAUNCH_ARGS,
logLevel: 'silent',
};

return launch(launchOptions);
};

const getLaunchedChromeVersionInfo = (
chrome: LaunchedChrome,
): Promise<BrowserVersionInfo> => {
return new Promise((resolve, reject) => {
get(`http://localhost:${chrome.port}/json/version`, res => {
let data = '';
res.setEncoding('utf8');

res.on('data', chunk => {
data += chunk;
});

res.on('end', () => {
resolve(JSON.parse(data));
});
}).on('error', err => reject(err));
});
};

const getSystemBrowserInstance = async (
chrome: LaunchedChrome,
launchArgs?: LaunchOptions,
): Promise<Browser> => {
const chromeVersionInfo = await getLaunchedChromeVersionInfo(chrome);

const browser = await puppeteer.connect({
...launchArgs,
browserWSEndpoint: chromeVersionInfo.webSocketDebuggerUrl,
});

browser.on('disconnected', () => chrome.kill());

return browser;
};

const getBrowserInstance = async (
launchArgs?: LaunchOptions,
): Promise<Browser> => {
const chrome = await launchSystemBrowser();
if (chrome && chrome.port > 0) {
return getSystemBrowserInstance(chrome, launchArgs);
}

return getLocalBrowserInstance(launchArgs);
};

export default {
getBrowserInstance,
};
87 changes: 87 additions & 0 deletions src/helpers/installer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import puppeteer, { BrowserFetcher, RevisionInfo } from 'puppeteer-core';
import ProgressBar from 'progress';
import preLogger from './logger';

let browserFetcher: BrowserFetcher;

const getBrowserFetcher = (): BrowserFetcher => {
if (browserFetcher) {
return browserFetcher;
}

browserFetcher = puppeteer.createBrowserFetcher();
return browserFetcher;
};

const getPreferredBrowserRevisionInfo = (): RevisionInfo => {
const revision =
process.env.PUPPETEER_CHROMIUM_REVISION ||
process.env.npm_config_puppeteer_chromium_revision ||
process.env.npm_package_config_puppeteer_chromium_revision ||
require('puppeteer-core/package.json').puppeteer.chromium_revision;

return getBrowserFetcher().revisionInfo(revision);
};

const { revision } = getPreferredBrowserRevisionInfo();
const revisionInfo = getBrowserFetcher().revisionInfo(revision);
const logger = preLogger('installer');

const toMegabytes = (bytes: number): string => {
const mb = bytes / 1024 / 1024;
return `${Math.round(mb * 10) / 10} Mb`;
};

const cleanUpOldRevisions = (revisions: string[]): Promise<void[]> => {
const localRevisions = revisions.filter(rev => revision !== rev);
// Remove previous chromium revisions.
const cleanupOldVersions = localRevisions.map((rev: string) =>
getBrowserFetcher().remove(rev),
);
return Promise.all([...cleanupOldVersions]);
};

let progressBar: ProgressBar;
let lastDownloadedBytes = 0;
const onProgress = (downloadedBytes: number, totalBytes: number): void => {
if (!progressBar) {
progressBar = new ProgressBar(
`Downloading Chromium r${revision} - ${toMegabytes(
totalBytes,
)} [:bar] :percent :etas `,
{
complete: '=',
incomplete: ' ',
width: 20,
total: totalBytes,
},
);
}
const delta = downloadedBytes - lastDownloadedBytes;
lastDownloadedBytes = downloadedBytes;
progressBar.tick(delta);
};

const installPreferredBrowserRevision = async (): Promise<RevisionInfo> => {
logger.warn(
`Chromium is not found on your system, gonna have to download r${revision} for you once`,
);

const installedRevision = await getBrowserFetcher().download(
revision,
onProgress,
);
logger.log(`Chromium downloaded to ${revisionInfo.folderPath}`);

await getBrowserFetcher()
.localRevisions()
.then(cleanUpOldRevisions);

return installedRevision;
};

export default {
getBrowserFetcher,
installPreferredBrowserRevision,
getPreferredBrowserRevisionInfo,
};
31 changes: 13 additions & 18 deletions src/puppets.ts → src/helpers/puppets.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import puppeteer, { Browser } from 'puppeteer';
import constants from './config/constants';
import url from './helpers/url';
import file from './helpers/file';
import images from './helpers/images';
import preLogger from './helpers/logger';
import { Options } from './models/options';
import { Browser } from 'puppeteer-core';
import constants from '../config/constants';
import url from './url';
import file from './file';
import images from './images';
import browserHelper from './browser';
import preLogger from './logger';
import { Options } from '../models/options';
import {
DeviceFactorSpec,
Dimension,
LaunchScreenSpec,
SplashScreenSpec,
} from './models/spec';
import { Image, SavedImage } from './models/image';
} from '../models/spec';
import { Image, SavedImage } from '../models/image';

const getAppleSplashScreenData = async (
browser: Browser,
Expand Down Expand Up @@ -200,12 +201,8 @@ const getSplashScreenMetaData = async (
'Initialising puppeteer to load latest splash screen metadata',
'🤖',
);
const browser = await puppeteer.launch({
headless: true,
args: constants.PUPPETEER_LAUNCH_ARGS,
timeout: 1000,
});

const browser = await browserHelper.getBrowserInstance({ timeout: 1000 });
let splashScreenUniformMetaData;

try {
Expand All @@ -224,7 +221,7 @@ const getSplashScreenMetaData = async (
`Failed to fetch latest specs from Apple Human Interface guidelines - using static fallback data`,
);
} finally {
browser.close();
await browser.close();
}

return splashScreenUniformMetaData;
Expand Down Expand Up @@ -253,14 +250,12 @@ const saveImages = async (

return Promise.all(
imageList.map(async ({ name, width, height, scaleFactor, orientation }) => {
const browser = await puppeteer.launch({
headless: true,
const browser = await browserHelper.getBrowserInstance({
defaultViewport: {
width,
height,
},
timeout: constants.BROWSER_SHELL_TIMEOUT,
args: constants.PUPPETEER_LAUNCH_ARGS,
});

const { type, quality } = options;
Expand Down
2 changes: 1 addition & 1 deletion src/main.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import pwa from './helpers/pwa';
import puppets from './puppets';
import puppets from './helpers/puppets';
import flags from './helpers/flags';
import preLogger from './helpers/logger';
import { CLIOptions, Options } from './models/options';
Expand Down

0 comments on commit 11045a1

Please sign in to comment.