diff --git a/.travis.yml b/.travis.yml index 7a7e80dab91..5bb1a91dc8f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,7 +28,20 @@ jobs: - npm run tsc - travis_retry npm run unit - # Runs unit tests on Linux + Chromium + # Node <10.17's fs.promises module was experimental and doesn't behave as + # expected. This problem was fixed in Node 10.19, but we run the unit tests + # through on 10.15 to make sure we don't cause any regressions when using + # fs.promises. See https://github.com/puppeteer/puppeteer/issues/6548 for an + # example. + - node_js: '10.15.0' + name: 'Node 10.15 Unit tests: Linux/Chromium' + env: + - CHROMIUM=true + before_install: + - PUPPETEER_PRODUCT=firefox npm install + script: + - npm run unit + - node_js: '10.19.0' name: 'Unit tests [with coverage]: Linux/Chromium' env: diff --git a/src/common/DOMWorld.ts b/src/common/DOMWorld.ts index de09fe368a6..0e032be77c2 100644 --- a/src/common/DOMWorld.ts +++ b/src/common/DOMWorld.ts @@ -288,7 +288,7 @@ export class DOMWorld { 'Cannot pass a filepath to addScriptTag in the browser environment.' ); } - const fs = await import('fs'); + const fs = await helper.importFSModule(); let contents = await fs.promises.readFile(path, 'utf8'); contents += '//# sourceURL=' + path.replace(/\n/g, ''); const context = await this.executionContext(); @@ -370,7 +370,7 @@ export class DOMWorld { 'Cannot pass a filepath to addStyleTag in the browser environment.' ); } - const fs = await import('fs'); + const fs = await helper.importFSModule(); let contents = await fs.promises.readFile(path, 'utf8'); contents += '/*# sourceURL=' + path.replace(/\n/g, '') + '*/'; const context = await this.executionContext(); diff --git a/src/common/JSHandle.ts b/src/common/JSHandle.ts index 819b3958446..c0ee4070b9a 100644 --- a/src/common/JSHandle.ts +++ b/src/common/JSHandle.ts @@ -563,8 +563,7 @@ export class ElementHandle< // This import is only needed for `uploadFile`, so keep it scoped here to avoid paying // the cost unnecessarily. const path = await import('path'); - // eslint-disable-next-line @typescript-eslint/no-var-requires - const fs = await import('fs'); + const fs = await helper.importFSModule(); // Locate all files and confirm that they exist. const files = await Promise.all( filePaths.map(async (filePath) => { diff --git a/src/common/Page.ts b/src/common/Page.ts index e805e2e7aa7..7194649bef2 100644 --- a/src/common/Page.ts +++ b/src/common/Page.ts @@ -1709,7 +1709,7 @@ export class Page extends EventEmitter { 'Screenshots can only be written to a file path in a Node environment.' ); } - const fs = await import('fs'); + const fs = await helper.importFSModule(); if (options.path) await fs.promises.writeFile(options.path, buffer); return buffer; diff --git a/src/common/helper.ts b/src/common/helper.ts index dc6655d240b..d8ca9b4ef48 100644 --- a/src/common/helper.ts +++ b/src/common/helper.ts @@ -313,7 +313,7 @@ async function readProtocolStream( throw new Error('Cannot write to a path outside of Node.js environment.'); } - const fs = isNode ? await import('fs') : null; + const fs = isNode ? await importFSModule() : null; let eof = false; let fileHandle: import('fs').promises.FileHandle; @@ -344,6 +344,30 @@ async function readProtocolStream( } } +/** + * Loads the Node fs promises API. Needed because on Node 10.17 and below, + * fs.promises is experimental, and therefore not marked as enumerable. That + * means when TypeScript compiles an `import('fs')`, its helper doesn't spot the + * promises declaration and therefore on Node <10.17 you get an error as + * fs.promises is undefined in compiled TypeScript land. + * + * See https://github.com/puppeteer/puppeteer/issues/6548 for more details. + * + * Once Node 10 is no longer supported (April 2021) we can remove this and use + * `(await import('fs')).promises`. + */ +async function importFSModule(): Promise { + if (!isNode) { + throw new Error('Cannot load the fs module API outside of Node.'); + } + + const fs = await import('fs'); + if (fs.promises) { + return fs; + } + return fs.default; +} + export const helper = { evaluationString, pageBindingInitString, @@ -356,6 +380,7 @@ export const helper = { waitForEvent, isString, isNumber, + importFSModule, addEventListener, removeEventListeners, valueFromRemoteObject,