#!/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. */ const fs = require('fs'); const path = require('path'); const rmSync = require('rimraf').sync; const ncp = require('ncp'); const {spawnSync} = require('child_process'); const util = require('util'); const writeFileAsync = util.promisify(fs.writeFile.bind(fs)); const cpAsync = util.promisify(ncp); const SCRIPT_NAME = path.basename(__filename); const ROOT_PATH = path.join(__dirname, '..'); const PLAYWRIGHT_CORE_FILES = ['lib', 'types', 'NOTICE', 'LICENSE', '.npmignore']; const PACKAGES = { 'playwright': { description: 'A high-level API to automate web browsers', browsers: ['chromium', 'firefox', 'webkit'], // We copy README.md additionally for Playwright so that it looks nice on NPM. files: [...PLAYWRIGHT_CORE_FILES, 'README.md'], }, 'playwright-core': { description: 'A high-level API to automate web browsers', browsers: [], files: PLAYWRIGHT_CORE_FILES, }, 'playwright-webkit': { description: 'A high-level API to automate WebKit', browsers: ['webkit'], files: PLAYWRIGHT_CORE_FILES, }, 'playwright-firefox': { description: 'A high-level API to automate Firefox', browsers: ['firefox'], files: PLAYWRIGHT_CORE_FILES, }, 'playwright-chromium': { description: 'A high-level API to automate Chromium', browsers: ['chromium'], files: PLAYWRIGHT_CORE_FILES, }, 'playwright-electron': { version: '0.4.0', // Manually manage playwright-electron version. description: 'A high-level API to automate Electron', browsers: [], files: PLAYWRIGHT_CORE_FILES, }, }; const cleanupPaths = []; // 1. Parse CLI arguments const args = process.argv.slice(2); if (args.some(arg => arg === '--help')) { console.log(usage()); process.exit(1); } else if (args.length < 1) { console.log(`Please specify package name, e.g. 'playwright' or 'playwright-chromium'.`); console.log(`Try running ${SCRIPT_NAME} --help`); process.exit(1); } else if (args.length < 2) { console.log(`Please specify output path`); console.log(`Try running ${SCRIPT_NAME} --help`); process.exit(1); } // 2. Setup cleanup if needed if (!args.some(arg => arg === '--no-cleanup')) { process.on('exit', () => { cleanupPaths.forEach(cleanupPath => rmSync(cleanupPath, {})); }); process.on('SIGINT', () => process.exit(2)); process.on('SIGHUP', () => process.exit(3)); process.on('SIGTERM', () => process.exit(4)); process.on('uncaughtException', error => { console.error(error); process.exit(5); }); process.on('unhandledRejection', error => { console.error(error); process.exit(6); }); } const packageName = args[0]; const outputPath = path.resolve(args[1]); const packagePath = path.join(__dirname, packageName); const package = PACKAGES[packageName]; if (!package) { console.log(`ERROR: unknown package ${packageName}`); process.exit(1); } (async () => { // 3. Copy package files. for (const file of package.files) await copyToPackage(file); // 4. Generate package.json const pwInternalJSON = require(path.join(ROOT_PATH, 'package.json')); await writeToPackage('package.json', JSON.stringify({ name: packageName, version: package.version || pwInternalJSON.version, description: package.description, repository: pwInternalJSON.repository, engines: pwInternalJSON.engines, homepage: pwInternalJSON.homepage, main: 'index.js', scripts: { install: 'node install.js', }, author: pwInternalJSON.author, license: pwInternalJSON.license, dependencies: pwInternalJSON.dependencies }, null, 2)); // 5. Generate browsers.json const browsersJSON = require(path.join(ROOT_PATH, 'browsers.json')); browsersJSON.browsers = browsersJSON.browsers.filter(browser => package.browsers.includes(browser.name)); await writeToPackage('browsers.json', JSON.stringify(browsersJSON, null, 2)); // 6. Run npm pack const {stdout, stderr, status} = spawnSync('npm', ['pack'], {cwd: packagePath, encoding: 'utf8'}); if (status !== 0) { console.log(`ERROR: "npm pack" failed`); console.log(stderr); process.exit(1); } const tgzName = stdout.trim(); // 7. Move result to the outputPath fs.renameSync(path.join(packagePath, tgzName), outputPath); console.log(outputPath); })(); async function writeToPackage(fileName, content) { const toPath = path.join(packagePath, fileName); cleanupPaths.push(toPath); console.error(`- generating: //${path.relative(ROOT_PATH, toPath)}`); await writeFileAsync(toPath, content); } async function copyToPackage(fileOrDirectoryName) { const fromPath = path.join(ROOT_PATH, fileOrDirectoryName); const toPath = path.join(packagePath, fileOrDirectoryName); cleanupPaths.push(toPath); console.error(`- copying: //${path.relative(ROOT_PATH, fromPath)} -> //${path.relative(ROOT_PATH, toPath)}`); await cpAsync(fromPath, toPath); } function usage() { return ` usage: ${SCRIPT_NAME} [--no-cleanup] Creates a .tgz of the package and saves it at the given output path --no-cleanup skip cleaning up generated files from package directory Example: ${SCRIPT_NAME} playwright ./playwright.tgz `; }