diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cb55069b09c37..1a5cea3272a02 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -68,10 +68,15 @@ jobs: run: npm run docs - name: Check if autogenerated docs differ run: | - if [[ $(git diff) ]]; then - echo "Please update the documentation by running 'npm run docs'" + diff_file=$(mktemp doc_diff_XXXXXX) + git diff --color > $diff_file + if [[ -s $diff_file ]]; then + echo "Please update the documentation by running 'npm run docs'. The following was the diff" + cat $diff_file + rm $diff_file exit 1 fi + rm $diff_file - name: Check if docs need to be deployed id: needs_deploying run: | @@ -165,7 +170,7 @@ jobs: if: ${{ matrix.spec.name == 'Linux' }} run: sudo apt-get install xvfb - name: Build - run: npm run build + run: npm run build:ci - name: Test types run: npm run test:types # On Linux we run all Chrome tests without retries and Firefox tests with retries. @@ -215,7 +220,7 @@ jobs: ls .local-chromium - name: Build run: | - npm run build + npm run build:ci docker/pack.sh - name: Build docker image working-directory: ./docker diff --git a/package-lock.json b/package-lock.json index dad25ec36b9e1..4ce10af21fd0d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,6 +30,7 @@ "@microsoft/api-extractor-model": "7.23.0", "@types/debug": "4.1.7", "@types/diff": "5.0.2", + "@types/glob": "7.2.0", "@types/mime": "3.0.1", "@types/mocha": "9.1.1", "@types/node": "18.7.1", @@ -61,6 +62,7 @@ "eslint-plugin-unused-imports": "2.0.0", "esprima": "4.0.1", "expect": "25.2.7", + "glob": "8.0.3", "gts": "4.0.0", "husky": "8.0.1", "jpeg-js": "0.4.4", @@ -4088,19 +4090,19 @@ } }, "node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", + "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", + "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "minimatch": "^5.0.1", + "once": "^1.3.0" }, "engines": { - "node": "*" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -4118,6 +4120,27 @@ "node": ">= 6" } }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", + "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/global-dirs": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", @@ -5474,6 +5497,48 @@ "node": ">=0.3.1" } }, + "node_modules/mocha/node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/glob/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/mocha/node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/mocha/node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -6614,6 +6679,25 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/run-async": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", @@ -7087,6 +7171,26 @@ "node": ">=8" } }, + "node_modules/test-exclude/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/text-diff": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/text-diff/-/text-diff-1.0.1.tgz", @@ -10655,16 +10759,36 @@ } }, "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", + "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", + "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", + "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + } } }, "glob-parent": { @@ -11670,6 +11794,41 @@ "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", "dev": true }, + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "dependencies": { + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } + } + }, "js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -12501,6 +12660,21 @@ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "requires": { "glob": "^7.1.3" + }, + "dependencies": { + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } } }, "run-async": { @@ -12859,6 +13033,22 @@ "@istanbuljs/schema": "^0.1.2", "glob": "^7.1.4", "minimatch": "^3.0.4" + }, + "dependencies": { + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } } }, "text-diff": { diff --git a/package.json b/package.json index 40485e310b2b2..c127d9d8bbba3 100644 --- a/package.json +++ b/package.json @@ -42,24 +42,24 @@ "lint:eslint": "([ \"$CI\" = true ] && eslint --ext js --ext ts --quiet -f codeframe . || eslint --ext js --ext ts .)", "install": "node install.js", "generate:sources": "tsx utils/generate_sources.ts", - "generate:types": "node utils/export_all.js && api-extractor run --local --verbose && eslint --ext ts --no-ignore --no-eslintrc -c .eslintrc.types.cjs --fix lib/types.d.ts", - "generate:markdown": "tsx utils/generate_docs.ts && prettier --ignore-path none --write docs", - "generate:esm-package-json": "echo '{\"type\": \"module\"}' > lib/esm/package.json", + "generate:artifacts": "tsx utils/generate_artifacts.ts", + "generate:markdown": "tsx utils/generate_docs.ts", "format": "run-s format:*", "format:prettier": "prettier --write .", "format:eslint": "eslint --ext js --ext ts --fix .", "docs": "run-s build generate:markdown", - "debug": "npm run build && mocha --inspect-brk", + "debug": "npm run build:test && mocha --inspect-brk", "commitlint": "commitlint --from=HEAD~1", "clean": "rimraf lib && rimraf test/build", "check": "run-p check:*", "check:protocol-revision": "tsx scripts/ensure-correct-devtools-protocol-package", "check:pinned-deps": "tsx scripts/ensure-pinned-deps", - "build": "run-s generate:sources build:tsc generate:types generate:esm-package-json", - "build:tsc": "tsc --version && run-p build:tsc:*", - "build:tsc:esm": "tsc -b src/tsconfig.esm.json", - "build:tsc:cjs": "tsc -b src/tsconfig.cjs.json", - "build:tsc:test": "tsc -b test" + "build": "npm run build:lib", + "build:test": "run-s generate:sources build:tsc:test", + "build:ci": "run-s build:test generate:artifacts", + "build:lib": "run-s generate:sources build:tsc:lib generate:artifacts", + "build:tsc:test": "tsc -b test", + "build:tsc:lib": "tsc -b tsconfig.lib.json" }, "files": [ "lib", @@ -90,6 +90,7 @@ "@microsoft/api-extractor-model": "7.23.0", "@types/debug": "4.1.7", "@types/diff": "5.0.2", + "@types/glob": "7.2.0", "@types/mime": "3.0.1", "@types/mocha": "9.1.1", "@types/node": "18.7.1", @@ -121,6 +122,7 @@ "eslint-plugin-unused-imports": "2.0.0", "esprima": "4.0.1", "expect": "25.2.7", + "glob": "8.0.3", "gts": "4.0.0", "husky": "8.0.1", "jpeg-js": "0.4.4", diff --git a/src/tsconfig.cjs.json b/src/tsconfig.cjs.json index e7c4de56698d3..c92ca3f87cc8a 100644 --- a/src/tsconfig.cjs.json +++ b/src/tsconfig.cjs.json @@ -8,5 +8,6 @@ "references": [ {"path": "../vendor/tsconfig.cjs.json"}, {"path": "../compat/cjs/tsconfig.json"} - ] + ], + "exclude": ["injected/injected.ts"] } diff --git a/src/types.ts b/src/types.ts index 8b6622e4e6399..42978312e78d3 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,13 +1,5 @@ -// AUTOGENERATED - Use `utils/export_all.js` to regenerate. +// AUTOGENERATED - Use `npm run generate:sources` to regenerate. -export * from './compat.d.js'; -export * from './constants.js'; -export * from './environment.js'; -export * from './initializePuppeteer.js'; -export * from './puppeteer.js'; -export * from './revisions.js'; - -// Exports from `common` export * from './common/Accessibility.js'; export * from './common/AriaQueryHandler.js'; export * from './common/Browser.js'; @@ -26,6 +18,7 @@ export * from './common/EmulationManager.js'; export * from './common/Errors.js'; export * from './common/EventEmitter.js'; export * from './common/ExecutionContext.js'; +export * from './common/fetch.js'; export * from './common/FileChooser.js'; export * from './common/FirefoxTargetManager.js'; export * from './common/Frame.js'; @@ -39,8 +32,8 @@ export * from './common/LifecycleWatcher.js'; export * from './common/NetworkConditions.js'; export * from './common/NetworkEventManager.js'; export * from './common/NetworkManager.js'; -export * from './common/PDFOptions.js'; export * from './common/Page.js'; +export * from './common/PDFOptions.js'; export * from './common/Product.js'; export * from './common/Puppeteer.js'; export * from './common/PuppeteerViewport.js'; @@ -51,25 +44,30 @@ export * from './common/TargetManager.js'; export * from './common/TaskQueue.js'; export * from './common/TimeoutSettings.js'; export * from './common/Tracing.js'; -export * from './common/USKeyboardLayout.js'; -export * from './common/WebWorker.js'; -export * from './common/fetch.js'; export * from './common/types.js'; +export * from './common/USKeyboardLayout.js'; export * from './common/util.js'; - -// Exports from `node` +export * from './common/WebWorker.js'; +export * from './compat.d.js'; +export * from './constants.js'; +export * from './environment.js'; +export * from './generated/injected.js'; +export * from './generated/version.js'; +export * from './initializePuppeteer.js'; export * from './node/BrowserFetcher.js'; export * from './node/BrowserRunner.js'; export * from './node/ChromeLauncher.js'; export * from './node/FirefoxLauncher.js'; +export * from './node/install.js'; export * from './node/LaunchOptions.js'; export * from './node/NodeWebSocketTransport.js'; export * from './node/PipeTransport.js'; export * from './node/ProductLauncher.js'; export * from './node/Puppeteer.js'; -export * from './node/install.js'; export * from './node/util.js'; - -// Exports from `generated` -export * from './generated/injected.js'; -export * from './generated/version.js'; +export * from './puppeteer.js'; +export * from './revisions.js'; +export * from './util/assert.js'; +export * from './util/DeferredPromise.js'; +export * from './util/ErrorLike.js'; +export * from './util/getPackageDirectory.js'; diff --git a/src/util/getPackageDirectory.ts b/src/util/getPackageDirectory.ts index 94c49535aa79d..d0c5fc4cad2a1 100644 --- a/src/util/getPackageDirectory.ts +++ b/src/util/getPackageDirectory.ts @@ -1,6 +1,9 @@ import {existsSync} from 'fs'; import {dirname, join, parse} from 'path'; +/** + * @internal + */ export const getPackageDirectory = (from: string): string => { let found = existsSync(join(from, 'package.json')); const root = parse(from).root; diff --git a/tsconfig.lib.json b/tsconfig.lib.json new file mode 100644 index 0000000000000..6f668b4e54b07 --- /dev/null +++ b/tsconfig.lib.json @@ -0,0 +1,16 @@ +/** + * This configuration only exists for the API Extractor tool and for VSCode to use. It is NOT the tsconfig used for compilation. + * For CJS builds, `tsconfig.cjs.json` is used, and for ESM, it's `tsconfig.esm.json`. + * See the details in CONTRIBUTING.md that describes our TypeScript setup. + */ +{ + "extends": "./tsconfig.base.json", + "compilerOptions": { + "composite": true + }, + "references": [ + {"path": "src/tsconfig.esm.json"}, + {"path": "src/tsconfig.cjs.json"} + ], + "exclude": ["**/*"] +} diff --git a/utils/export_all.js b/utils/export_all.js deleted file mode 100644 index 2cbef44f91291..0000000000000 --- a/utils/export_all.js +++ /dev/null @@ -1,30 +0,0 @@ -const {readdirSync, writeFileSync} = require('fs'); -const {join, basename} = require('path'); - -const EXCLUDE_FILES = ['puppeteer-core.ts']; - -let typesTs = '// AUTOGENERATED - Use `utils/export_all.js` to regenerate.\n'; - -typesTs += `\n`; -for (const file of readdirSync(join(__dirname, `../src`)).filter(filename => { - return ( - filename.endsWith('ts') && - !filename.startsWith('types') && - !EXCLUDE_FILES.includes(filename) - ); -})) { - typesTs += `export * from './${basename(file, '.ts')}.js';\n`; -} - -for (const folder of ['common', 'node', 'generated']) { - typesTs += `\n// Exports from \`${folder}\`\n`; - for (const file of readdirSync(join(__dirname, `../src/${folder}`)).filter( - filename => { - return filename.endsWith('ts') && !EXCLUDE_FILES.includes(filename); - } - )) { - typesTs += `export * from './${folder}/${basename(file, '.ts')}.js';\n`; - } -} - -writeFileSync(join(__dirname, '../src/types.ts'), typesTs); diff --git a/utils/generate_artifacts.ts b/utils/generate_artifacts.ts new file mode 100644 index 0000000000000..ab29964bb237e --- /dev/null +++ b/utils/generate_artifacts.ts @@ -0,0 +1,28 @@ +#!/usr/bin/env node +import {writeFile} from 'fs/promises'; +import {job} from './internal/job.js'; +import {spawnAndLog} from './internal/util.js'; + +(async () => { + job('', async ({outputs}) => { + await writeFile(outputs[0]!, '{"type": "module"}'); + }) + .outputs(['lib/esm/package.json']) + .build(); + + job('', async ({outputs}) => { + spawnAndLog('api-extractor', 'run', '--local'); + spawnAndLog( + 'eslint', + '--ext=ts', + '--no-ignore', + '--no-eslintrc', + '-c=.eslintrc.types.cjs', + '--fix', + outputs[0]! + ); + }) + .inputs(['lib/esm/puppeteer/types.d.ts']) + .outputs(['lib/types.d.ts', 'puppeteer.api.json']) + .build(); +})(); diff --git a/utils/generate_docs.ts b/utils/generate_docs.ts index a3cd93e46a47d..35d2c8a57a085 100644 --- a/utils/generate_docs.ts +++ b/utils/generate_docs.ts @@ -14,11 +14,11 @@ * limitations under the License. */ -import {readFile, writeFile} from 'fs/promises'; -import rimraf from 'rimraf'; +import {readFile, rm, writeFile} from 'fs/promises'; import semver from 'semver'; import {generateDocs} from './internal/custom_markdown_action.js'; import {job} from './internal/job.js'; +import {spawnAndLog} from './internal/util.js'; function getOffsetAndLimit( sectionName: string, @@ -46,7 +46,7 @@ function spliceIntoSection( } (async () => { - job('', async ({inputs, outputs}) => { + const job1 = job('', async ({inputs, outputs}) => { const content = await readFile(inputs[0]!, 'utf-8'); const sectionContent = ` --- @@ -60,8 +60,8 @@ sidebar_position: 1 .build(); // Chrome Versions - job('', async ({inputs, outputs}) => { - let content = await readFile(outputs[0]!, {encoding: 'utf8'}); + const job2 = job('', async ({inputs, outputs}) => { + let content = await readFile(inputs[2]!, {encoding: 'utf8'}); const {versionsPerRelease} = await import(inputs[0]!); const versionsArchived = JSON.parse(await readFile(inputs[1]!, 'utf8')); @@ -95,14 +95,21 @@ sidebar_position: 1 await writeFile(outputs[0]!, content); }) - .inputs(['versions.js', 'website/versionsArchived.json']) + .inputs([ + 'versions.js', + 'website/versionsArchived.json', + 'docs/chromium-support.md', + ]) .outputs(['docs/chromium-support.md']) .build(); + await Promise.all([job1, job2]); + // Generate documentation job('', async ({inputs, outputs}) => { - rimraf.sync(outputs[0]!); + await rm(outputs[0]!, {recursive: true, force: true}); generateDocs(inputs[0]!, outputs[0]!); + spawnAndLog('prettier', '--ignore-path', 'none', '--write', 'docs'); }) .inputs(['docs/puppeteer.api.json']) .outputs(['docs/api']) diff --git a/utils/generate_sources.ts b/utils/generate_sources.ts index 1dc919b822847..d828a9943e651 100644 --- a/utils/generate_sources.ts +++ b/utils/generate_sources.ts @@ -1,10 +1,13 @@ #!/usr/bin/env node +import {createHash} from 'crypto'; import esbuild from 'esbuild'; -import {mkdir, mkdtemp, readFile, writeFile} from 'fs/promises'; +import {mkdir, mkdtemp, readFile, rm, writeFile} from 'fs/promises'; +import {sync as glob} from 'glob'; import path from 'path'; -import rimraf from 'rimraf'; import {job} from './internal/job.js'; +const INCLUDED_FOLDERS = ['common', 'node', 'generated', 'util']; + (async () => { await job('', async ({outputs}) => { await Promise.all( @@ -45,12 +48,35 @@ import {job} from './internal/job.js'; JSON.stringify(content) ); await writeFile(outputs[0]!, scriptContent); - rimraf.sync(tmp); + await rm(tmp, {recursive: true, force: true}); }) - .inputs(['src/templates/injected.ts.tmpl', 'src/injected/**.ts']) + .inputs(['src/templates/injected.ts.tmpl', 'src/injected/**/*.ts']) .outputs(['src/generated/injected.ts']) .build(); + const sources = glob( + `src/{@(${INCLUDED_FOLDERS.join('|')})/*.ts,!(types|puppeteer-core).ts}` + ); + await job('', async ({outputs}) => { + let types = + '// AUTOGENERATED - Use `npm run generate:sources` to regenerate.\n\n'; + for (const input of sources.map(source => { + return `.${source.slice(3)}`; + })) { + types += `export * from '${input.replace('.ts', '.js')}';\n`; + } + await writeFile(outputs[0]!, types); + }) + .value( + sources + .reduce((hmac, value) => { + return hmac.update(value); + }, createHash('sha256')) + .digest('hex') + ) + .outputs(['src/types.ts']) + .build(); + job('', async ({inputs, outputs}) => { const version = JSON.parse(await readFile(inputs[0]!, 'utf8')).version; await writeFile( diff --git a/utils/internal/job.ts b/utils/internal/job.ts index 49226a505fa71..2e331a75523ce 100644 --- a/utils/internal/job.ts +++ b/utils/internal/job.ts @@ -1,7 +1,13 @@ -import {Stats} from 'fs'; -import {stat} from 'fs/promises'; +import {createHash} from 'crypto'; +import {existsSync, Stats} from 'fs'; +import {mkdir, readFile, stat, writeFile} from 'fs/promises'; import {glob} from 'glob'; -import path from 'path'; +import {tmpdir} from 'os'; +import {dirname, join, resolve} from 'path'; +import {chdir} from 'process'; + +const packageRoot = resolve(join(__dirname, '..', '..')); +chdir(packageRoot); interface JobContext { name: string; @@ -14,61 +20,120 @@ class JobBuilder { #outputs: string[] = []; #callback: (ctx: JobContext) => Promise; #name: string; + #value = ''; + #force = false; constructor(name: string, callback: (ctx: JobContext) => Promise) { this.#name = name; this.#callback = callback; } + get jobHash(): string { + return createHash('sha256').update(this.#name).digest('hex'); + } + + force() { + this.#force = true; + return this; + } + + value(value: string) { + this.#value = value; + return this; + } + inputs(inputs: string[]): JobBuilder { this.#inputs = inputs.flatMap(value => { - value = path.resolve(__dirname, '..', '..', value); - const paths = glob.sync(value); - return paths.length ? paths : [value]; + if (glob.hasMagic(value)) { + return glob.sync(value).map(value => { + // Glob doesn't support `\` on Windows, so we join here. + return join(packageRoot, value); + }); + } + return join(packageRoot, value); }); return this; } outputs(outputs: string[]): JobBuilder { if (!this.#name) { - this.#name = outputs[0]!; + this.#name = outputs.join(' and '); } this.#outputs = outputs.map(value => { - return path.resolve(__dirname, '..', '..', value); + return join(packageRoot, value); }); return this; } async build(): Promise { console.log(`Running job ${this.#name}...`); + // For debugging. + if (this.#force) { + return this.#run(); + } + // In case we deleted an output file on purpose. + if (!this.getOutputStats()) { + return this.#run(); + } + // Run if the job has a value, but it changes. + if (this.#value) { + if (!(await this.isValueDifferent())) { + return; + } + return this.#run(); + } + // Always run when there is no output. + if (!this.#outputs.length) { + return this.#run(); + } + // Make-like comparator. + if (!(await this.areInputsNewer())) { + return; + } + return this.#run(); + } - let shouldRun = true; + async isValueDifferent(): Promise { + const file = join(tmpdir(), `puppeteer/${this.jobHash}.txt`); + await mkdir(dirname(file), {recursive: true}); + if (!existsSync(file)) { + await writeFile(file, this.#value); + return true; + } + return this.#value !== (await readFile(file, 'utf8')); + } - const inputStats = await Promise.all( - this.#inputs.map(input => { - return stat(input); - }) - ); - let outputStats: Stats[]; + #outputStats?: Stats[]; + async getOutputStats(): Promise { + if (this.#outputStats) { + return this.#outputStats; + } try { - outputStats = await Promise.all( + this.#outputStats = await Promise.all( this.#outputs.map(output => { return stat(output); }) ); - - if ( - outputStats.reduce(reduceMinTime, Infinity) > - inputStats.reduce(reduceMaxTime, 0) - ) { - shouldRun = false; - } } catch {} + return this.#outputStats; + } - if (shouldRun) { - this.#run(); + async areInputsNewer(): Promise { + const inputStats = await Promise.all( + this.#inputs.map(input => { + return stat(input); + }) + ); + const outputStats = await this.getOutputStats(); + if ( + outputStats && + outputStats.reduce(reduceMinTime, Infinity) > + inputStats.reduce(reduceMaxTime, 0) + ) { + return false; } + return true; } #run(): Promise { diff --git a/utils/internal/util.ts b/utils/internal/util.ts new file mode 100644 index 0000000000000..4ebbe8b86b9d0 --- /dev/null +++ b/utils/internal/util.ts @@ -0,0 +1,14 @@ +import {spawnSync} from 'child_process'; + +export const spawnAndLog = (...args: string[]): void => { + const {stdout, stderr} = spawnSync(args[0]!, args.slice(1), { + encoding: 'utf-8', + shell: true, + }); + if (stdout) { + console.log(stdout); + } + if (stderr) { + console.error(stderr); + } +};