From 6a93cb9749450703ff52b187cec4a2f4d15bfaad Mon Sep 17 00:00:00 2001 From: Joel Einbinder Date: Fri, 28 Aug 2020 17:53:03 -0700 Subject: [PATCH] fix(types): don't show types that we don't export (#3185) Today we have a bunch of types used by the d.ts file that are not exported. We don't want to export them because it would greatly increase our semver API surface area, so this patch inlines them. Now users will not see names of types they can't import. --- utils/generate_types/index.js | 70 +++++++++++++-------- utils/generate_types/overrides.d.ts | 97 ++++++++++++++++++++--------- utils/generate_types/test/test.ts | 6 ++ 3 files changed, 120 insertions(+), 53 deletions(-) diff --git a/utils/generate_types/index.js b/utils/generate_types/index.js index f6779ce06b..56c81b2032 100644 --- a/utils/generate_types/index.js +++ b/utils/generate_types/index.js @@ -69,7 +69,7 @@ let documentation; ${overrides} ${classes.map(classDesc => classToString(classDesc)).join('\n')} -${objectDefinitionsToString()} +${objectDefinitionsToString(overrides)} ${generateDevicesTypes()} `; for (const [key, value] of Object.entries(exported)) @@ -80,14 +80,20 @@ ${generateDevicesTypes()} process.exit(1); }); -function objectDefinitionsToString() { +/** + * @param {string} overriddes + */ +function objectDefinitionsToString(overriddes) { let definition; const parts = []; + const internalWords = new Set(overriddes.split(/[^\w$]/g)); while ((definition = objectDefinitions.pop())) { const {name, properties} = definition; - parts.push(`${exported[name] ? 'export ' : ''}interface ${name} {`); - parts.push(properties.map(member => `${memberJSDOC(member, ' ')}${nameForProperty(member)}${argsFromMember(member, name)}: ${typeToString(member.type, name, member.name)};`).join('\n\n')); - parts.push('}\n'); + const shouldExport = !!exported[name]; + const usedInternally = internalWords.has(name); + if (!usedInternally && !shouldExport) + continue; + parts.push(`${shouldExport ? 'export ' : ''}interface ${name} ${stringifyObjectType(properties, name, '')}\n`) } return parts.join('\n'); } @@ -141,9 +147,9 @@ function createEventDescriptions(classDesc) { return []; const descriptions = []; for (const [eventName, value] of classDesc.events) { - const type = typeToString(value && value.type, classDesc.name, eventName, 'payload'); + const type = stringifyComplexType(value && value.type, '', classDesc.name, eventName, 'payload'); const argName = argNameForType(type); - const params = argName ? `${argName} : ${type}` : ''; + const params = argName ? `${argName}: ${type}` : ''; descriptions.push({ type, params, @@ -183,8 +189,8 @@ function classBody(classDesc) { return parts.join('\n'); } const jsdoc = memberJSDOC(member, ' '); - const args = argsFromMember(member, classDesc.name); - const type = typeToString(member.type, classDesc.name, member.name); + const args = argsFromMember(member, ' ', classDesc.name); + const type = stringifyComplexType(member.type, ' ', classDesc.name, member.name); // do this late, because we still want object definitions for overridden types if (!hasOwnMethod(classDesc, member.name)) return ''; @@ -229,21 +235,32 @@ function writeComment(comment, indent = '') { /** * @param {Documentation.Type} type */ -function typeToString(type, ...namespace) { +function stringifyComplexType(type, indent, ...namespace) { if (!type) return 'void'; - // Accessibility.snapshot has a recursive data structure, so special case it here. - if (namespace[0] === 'AccessibilitySnapshot' && namespace[1] === 'children') - return 'Array'; - let typeString = stringifyType(parseType(type.name)); + let typeString = stringifySimpleType(parseType(type.name)); if (type.properties.length && typeString.indexOf('Object') !== -1) { const name = namespace.map(n => n[0].toUpperCase() + n.substring(1)).join(''); - typeString = typeString.replace('Object', name); + const shouldExport = exported[name]; objectDefinitions.push({name, properties: type.properties}); + if (shouldExport) { + typeString = typeString.replace(/Object/g, name); + } else { + const objType = stringifyObjectType(type.properties, name, indent); + typeString = typeString.replace(/Object/g, objType); + } } return typeString; } +function stringifyObjectType(properties, name, indent = '') { + const parts = []; + parts.push(`{`); + parts.push(properties.map(member => `${memberJSDOC(member, indent + ' ')}${nameForProperty(member)}${argsFromMember(member, indent + ' ', name)}: ${stringifyComplexType(member.type, indent + ' ', name, member.name)};`).join('\n\n')); + parts.push(indent + '}'); + return parts.join('\n'); +} + /** * @param {string} type */ @@ -308,15 +325,15 @@ function parseType(type) { /** * @return {string} */ -function stringifyType(parsedType) { +function stringifySimpleType(parsedType) { if (!parsedType) return 'void'; if (parsedType.name === 'Object' && parsedType.template) { - const keyType = stringifyType({ + const keyType = stringifySimpleType({ ...parsedType.template, next: null }); - const valueType = stringifyType(parsedType.template.next); + const valueType = stringifySimpleType(parsedType.template.next); return `{ [key: ${keyType}]: ${valueType}; }`; } let out = parsedType.name; @@ -327,20 +344,23 @@ function stringifyType(parsedType) { const arg = args; args = args.next; arg.next = null; - stringArgs.push(stringifyType(arg)); + stringArgs.push({ + type: stringifySimpleType(arg), + name: arg.name.toLowerCase() + }); } - out = `((${stringArgs.map((type, index) => `arg${index} : ${type}`).join(', ')}) => ${stringifyType(parsedType.retType)})`; + out = `((${stringArgs.map(({name, type}) => `${name}: ${type}`).join(', ')}) => ${stringifySimpleType(parsedType.retType)})`; } else if (parsedType.name === 'function') { out = 'Function'; } if (parsedType.nullable) out = 'null|' + out; if (parsedType.template) - out += '<' + stringifyType(parsedType.template) + '>'; + out += '<' + stringifySimpleType(parsedType.template) + '>'; if (parsedType.pipe) - out += '|' + stringifyType(parsedType.pipe); + out += '|' + stringifySimpleType(parsedType.pipe); if (parsedType.next) - out += ', ' + stringifyType(parsedType.next); + out += ', ' + stringifySimpleType(parsedType.next); return out.trim(); } @@ -359,10 +379,10 @@ function matchingBracket(str, open, close) { /** * @param {Documentation.Member} member */ -function argsFromMember(member, ...namespace) { +function argsFromMember(member, indent, ...namespace) { if (member.kind === 'property') return ''; - return '(' + member.argsArray.map(arg => `${nameForProperty(arg)}: ${typeToString(arg.type, ...namespace, member.name, arg.name)}`).join(', ') + ')'; + return '(' + member.argsArray.map(arg => `${nameForProperty(arg)}: ${stringifyComplexType(arg.type, indent, ...namespace, member.name, arg.name)}`).join(', ') + ')'; } /** * @param {Documentation.Member} member diff --git a/utils/generate_types/overrides.d.ts b/utils/generate_types/overrides.d.ts index 490a9c8445..9b76dba3bf 100644 --- a/utils/generate_types/overrides.d.ts +++ b/utils/generate_types/overrides.d.ts @@ -43,8 +43,6 @@ type PageFunction = string | ((arg: Unboxed) => R | Promise); type PageFunctionOn = string | ((on: On, arg2: Unboxed) => R | Promise); type SmartHandle = T extends Node ? ElementHandle : JSHandle; type ElementHandleForTag = ElementHandle; -type HTMLOrSVGElement = SVGElement | HTMLElement; -type HTMLOrSVGElementHandle = ElementHandle; type PageWaitForSelectorOptionsNotHidden = PageWaitForSelectorOptions & { state: 'visible'|'attached'; @@ -61,28 +59,28 @@ export interface Page { evaluateHandle(pageFunction: PageFunction, arg?: any): Promise>; $(selector: K): Promise | null>; - $(selector: string): Promise; + $(selector: string): Promise | null>; $$(selector: K): Promise[]>; - $$(selector: string): Promise; + $$(selector: string): Promise[]>; $eval(selector: K, pageFunction: PageFunctionOn, arg: Arg): Promise; - $eval(selector: string, pageFunction: PageFunctionOn, arg: Arg): Promise; + $eval(selector: string, pageFunction: PageFunctionOn, arg: Arg): Promise; $eval(selector: K, pageFunction: PageFunctionOn, arg?: any): Promise; - $eval(selector: string, pageFunction: PageFunctionOn, arg?: any): Promise; + $eval(selector: string, pageFunction: PageFunctionOn, arg?: any): Promise; $$eval(selector: K, pageFunction: PageFunctionOn, arg: Arg): Promise; - $$eval(selector: string, pageFunction: PageFunctionOn, arg: Arg): Promise; + $$eval(selector: string, pageFunction: PageFunctionOn, arg: Arg): Promise; $$eval(selector: K, pageFunction: PageFunctionOn, arg?: any): Promise; - $$eval(selector: string, pageFunction: PageFunctionOn, arg?: any): Promise; + $$eval(selector: string, pageFunction: PageFunctionOn, arg?: any): Promise; waitForFunction(pageFunction: PageFunction, arg: Arg, options?: PageWaitForFunctionOptions): Promise>; waitForFunction(pageFunction: PageFunction, arg?: any, options?: PageWaitForFunctionOptions): Promise>; waitForSelector(selector: K, options?: PageWaitForSelectorOptionsNotHidden): Promise>; - waitForSelector(selector: string, options?: PageWaitForSelectorOptionsNotHidden): Promise; + waitForSelector(selector: string, options?: PageWaitForSelectorOptionsNotHidden): Promise>; waitForSelector(selector: K, options: PageWaitForSelectorOptions): Promise | null>; - waitForSelector(selector: string, options: PageWaitForSelectorOptions): Promise; + waitForSelector(selector: string, options: PageWaitForSelectorOptions): Promise>; } export interface Frame { @@ -93,28 +91,28 @@ export interface Frame { evaluateHandle(pageFunction: PageFunction, arg?: any): Promise>; $(selector: K): Promise | null>; - $(selector: string): Promise; + $(selector: string): Promise | null>; $$(selector: K): Promise[]>; - $$(selector: string): Promise; + $$(selector: string): Promise[]>; $eval(selector: K, pageFunction: PageFunctionOn, arg: Arg): Promise; - $eval(selector: string, pageFunction: PageFunctionOn, arg: Arg): Promise; + $eval(selector: string, pageFunction: PageFunctionOn, arg: Arg): Promise; $eval(selector: K, pageFunction: PageFunctionOn, arg?: any): Promise; - $eval(selector: string, pageFunction: PageFunctionOn, arg?: any): Promise; + $eval(selector: string, pageFunction: PageFunctionOn, arg?: any): Promise; $$eval(selector: K, pageFunction: PageFunctionOn, arg: Arg): Promise; - $$eval(selector: string, pageFunction: PageFunctionOn, arg: Arg): Promise; + $$eval(selector: string, pageFunction: PageFunctionOn, arg: Arg): Promise; $$eval(selector: K, pageFunction: PageFunctionOn, arg?: any): Promise; - $$eval(selector: string, pageFunction: PageFunctionOn, arg?: any): Promise; + $$eval(selector: string, pageFunction: PageFunctionOn, arg?: any): Promise; waitForFunction(pageFunction: PageFunction, arg: Arg, options?: PageWaitForFunctionOptions): Promise>; waitForFunction(pageFunction: PageFunction, arg?: any, options?: PageWaitForFunctionOptions): Promise>; waitForSelector(selector: K, options?: PageWaitForSelectorOptionsNotHidden): Promise>; - waitForSelector(selector: string, options?: PageWaitForSelectorOptionsNotHidden): Promise; + waitForSelector(selector: string, options?: PageWaitForSelectorOptionsNotHidden): Promise>; waitForSelector(selector: K, options: PageWaitForSelectorOptions): Promise | null>; - waitForSelector(selector: string, options: PageWaitForSelectorOptions): Promise; + waitForSelector(selector: string, options: PageWaitForSelectorOptions): Promise>; } export interface Worker { @@ -138,25 +136,25 @@ export interface JSHandle { export interface ElementHandle extends JSHandle { $(selector: K): Promise | null>; - $(selector: string): Promise; + $(selector: string): Promise | null>; $$(selector: K): Promise[]>; - $$(selector: string): Promise; + $$(selector: string): Promise[]>; $eval(selector: K, pageFunction: PageFunctionOn, arg: Arg): Promise; - $eval(selector: string, pageFunction: PageFunctionOn, arg: Arg): Promise; + $eval(selector: string, pageFunction: PageFunctionOn, arg: Arg): Promise; $eval(selector: K, pageFunction: PageFunctionOn, arg?: any): Promise; - $eval(selector: string, pageFunction: PageFunctionOn, arg?: any): Promise; + $eval(selector: string, pageFunction: PageFunctionOn, arg?: any): Promise; $$eval(selector: K, pageFunction: PageFunctionOn, arg: Arg): Promise; - $$eval(selector: string, pageFunction: PageFunctionOn, arg: Arg): Promise; + $$eval(selector: string, pageFunction: PageFunctionOn, arg: Arg): Promise; $$eval(selector: K, pageFunction: PageFunctionOn, arg?: any): Promise; - $$eval(selector: string, pageFunction: PageFunctionOn, arg?: any): Promise; + $$eval(selector: string, pageFunction: PageFunctionOn, arg?: any): Promise; waitForSelector(selector: K, options?: ElementHandleWaitForSelectorOptionsNotHidden): Promise>; - waitForSelector(selector: string, options?: ElementHandleWaitForSelectorOptionsNotHidden): Promise; + waitForSelector(selector: string, options?: ElementHandleWaitForSelectorOptionsNotHidden): Promise>; waitForSelector(selector: K, options: ElementHandleWaitForSelectorOptions): Promise | null>; - waitForSelector(selector: string, options: ElementHandleWaitForSelectorOptions): Promise; + waitForSelector(selector: string, options: ElementHandleWaitForSelectorOptions): Promise>; } export interface BrowserType { @@ -165,7 +163,7 @@ export interface BrowserType { export interface ChromiumBrowser extends Browser { contexts(): Array; - newContext(options?: BrowserNewContextOptions): Promise; + newContext(options?: BrowserContextOptions): Promise; } export interface CDPSession { @@ -181,7 +179,7 @@ export interface CDPSession { } type DeviceDescriptor = { - viewport: BrowserNewContextOptionsViewport; + viewport: ViewportSize; userAgent: string; deviceScaleFactor: number; isMobile: boolean; @@ -194,6 +192,49 @@ class TimeoutError extends Error {} } +export interface Accessibility { + snapshot(options?: { + /** + * Prune uninteresting nodes from the tree. Defaults to `true`. + */ + interestingOnly?: boolean; + + /** + * The root DOM element for the snapshot. Defaults to the whole page. + */ + root?: ElementHandle; + }): Promise; +} + +type AccessibilityNode = { + role: string; + name: string; + value?: string|number; + description?: string; + keyshortcuts?: string; + roledescription?: string; + valuetext?: string; + disabled?: boolean; + expanded?: boolean; + focused?: boolean; + modal?: boolean; + multiline?: boolean; + multiselectable?: boolean; + readonly?: boolean; + required?: boolean; + selected?: boolean; + checked?: boolean|"mixed"; + pressed?: boolean|"mixed"; + level?: number; + valuemin?: number; + valuemax?: number; + autocomplete?: string; + haspopup?: string; + invalid?: string; + orientation?: string; + children?: AccessibilityNode[]; +} + export const selectors: Selectors; export const devices: Devices & DeviceDescriptor[]; diff --git a/utils/generate_types/test/test.ts b/utils/generate_types/test/test.ts index 9168b88d06..a461437e15 100644 --- a/utils/generate_types/test/test.ts +++ b/utils/generate_types/test/test.ts @@ -189,6 +189,12 @@ playwright.chromium.launch().then(async browser => { const inputElement = (await page.$('input[type=submit]'))!; await inputElement.click(); + + await inputElement.setInputFiles([{ + name: 'yo', + mimeType: 'text/plain', + buffer: Buffer.from('yo') + }]) }); // Example with launch options