mirror of
				https://github.com/microsoft/playwright.git
				synced 2025-06-26 21:40:17 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			230 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			230 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
		
			Executable File
		
	
	
	
	
| #!/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 yaml = require('yaml');
 | |
| 
 | |
| const channels = new Set();
 | |
| 
 | |
| function raise(item) {
 | |
|   throw new Error('Invalid item: ' + JSON.stringify(item, null, 2));
 | |
| }
 | |
| 
 | |
| function titleCase(name) {
 | |
|   return name[0].toUpperCase() + name.substring(1);
 | |
| }
 | |
| 
 | |
| function inlineType(type, indent, wrapEnums = false) {
 | |
|   if (typeof type === 'string') {
 | |
|     const optional = type.endsWith('?');
 | |
|     if (optional)
 | |
|       type = type.substring(0, type.length - 1);
 | |
|     if (type === 'binary')
 | |
|       return { ts: 'Binary', scheme: 'tBinary', optional };
 | |
|     if (['string', 'boolean', 'number', 'undefined'].includes(type))
 | |
|       return { ts: type, scheme: `t${titleCase(type)}`, optional };
 | |
|     if (channels.has(type))
 | |
|       return { ts: `${type}Channel`, scheme: `tChannel('${type}')` , optional };
 | |
|     if (type === 'Channel')
 | |
|       return { ts: `Channel`, scheme: `tChannel('*')`, optional };
 | |
|     return { ts: type, scheme: `tType('${type}')`, optional };
 | |
|   }
 | |
|   if (type.type.startsWith('array')) {
 | |
|     const optional = type.type.endsWith('?');
 | |
|     const inner = inlineType(type.items, indent, true);
 | |
|     return { ts: `${inner.ts}[]`, scheme: `tArray(${inner.scheme})`, optional };
 | |
|   }
 | |
|   if (type.type.startsWith('enum')) {
 | |
|     const optional = type.type.endsWith('?');
 | |
|     const ts = type.literals.map(literal => `'${literal}'`).join(' | ');
 | |
|     return {
 | |
|       ts: wrapEnums ? `(${ts})` : ts,
 | |
|       scheme: `tEnum([${type.literals.map(literal => `'${literal}'`).join(', ')}])`,
 | |
|       optional
 | |
|     };
 | |
|   }
 | |
|   if (type.type.startsWith('object')) {
 | |
|     const optional = type.type.endsWith('?');
 | |
|     const inner = properties(type.properties, indent + '  ');
 | |
|     return {
 | |
|       ts: `{\n${inner.ts}\n${indent}}`,
 | |
|       scheme: `tObject({\n${inner.scheme}\n${indent}})`,
 | |
|       optional
 | |
|     };
 | |
|   }
 | |
|   raise(type);
 | |
| }
 | |
| 
 | |
| function properties(props, indent) {
 | |
|   const ts = [];
 | |
|   const scheme = [];
 | |
|   for (const [name, value] of Object.entries(props)) {
 | |
|     const inner = inlineType(value, indent);
 | |
|     ts.push(`${indent}${name}${inner.optional ? '?' : ''}: ${inner.ts},`);
 | |
|     const wrapped = inner.optional ? `tOptional(${inner.scheme})` : inner.scheme;
 | |
|     scheme.push(`${indent}${name}: ${wrapped},`);
 | |
|   }
 | |
|   return { ts: ts.join('\n'), scheme: scheme.join('\n') };
 | |
| }
 | |
| 
 | |
| function objectType(props, indent) {
 | |
|   if (!Object.entries(props).length)
 | |
|     return { ts: `{}`, scheme: `tObject({})` };
 | |
|   const inner = properties(props, indent + '  ');
 | |
|   return { ts: `{\n${inner.ts}\n${indent}}`, scheme: `tObject({\n${inner.scheme}\n${indent}})` };
 | |
| }
 | |
| 
 | |
| const channels_ts = [
 | |
| `/**
 | |
|  * 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.
 | |
|  */
 | |
| 
 | |
| // This file is generated by ${path.basename(__filename)}, do not edit manually.
 | |
| 
 | |
| import { EventEmitter } from 'events';
 | |
| 
 | |
| export type Binary = string;
 | |
| 
 | |
| export interface Channel extends EventEmitter {
 | |
| }
 | |
| `];
 | |
| 
 | |
| const validator_ts = [
 | |
| `/**
 | |
|  * 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.
 | |
|  */
 | |
| 
 | |
| // This file is generated by ${path.basename(__filename)}, do not edit manually.
 | |
| 
 | |
| import { Validator, ValidationError, tOptional, tObject, tBoolean, tNumber, tString, tEnum, tArray, tBinary } from './validatorPrimitives';
 | |
| export { Validator, ValidationError } from './validatorPrimitives';
 | |
| 
 | |
| type Scheme = { [key: string]: Validator };
 | |
| 
 | |
| export function createScheme(tChannel: (name: string) => Validator): Scheme {
 | |
|   const scheme: Scheme = {};
 | |
| 
 | |
|   const tType = (name: string): Validator => {
 | |
|     return (arg: any, path: string) => {
 | |
|       const v = scheme[name];
 | |
|       if (!v)
 | |
|         throw new ValidationError(path + ': unknown type "' + name + '"');
 | |
|       return v(arg, path);
 | |
|     };
 | |
|   };
 | |
| `];
 | |
| 
 | |
| const yml = fs.readFileSync(path.join(__dirname, '..', 'src', 'rpc', 'protocol.yml'), 'utf-8');
 | |
| const protocol = yaml.parse(yml);
 | |
| 
 | |
| function addScheme(name, s) {
 | |
|   const lines = `scheme.${name} = ${s};`.split('\n');
 | |
|   validator_ts.push(...lines.map(line => '  ' + line));
 | |
| }
 | |
| 
 | |
| const inherits = new Map();
 | |
| for (const [name, value] of Object.entries(protocol)) {
 | |
|   if (value.type === 'interface') {
 | |
|     channels.add(name);
 | |
|     if (value.extends)
 | |
|       inherits.set(name, value.extends);
 | |
|   }
 | |
| }
 | |
| 
 | |
| for (const [name, item] of Object.entries(protocol)) {
 | |
|   if (item.type === 'interface') {
 | |
|     const channelName = name;
 | |
|     channels_ts.push(`// ----------- ${channelName} -----------`);
 | |
|     const init = objectType(item.initializer || {}, '');
 | |
|     const initializerName = channelName + 'Initializer';
 | |
|     channels_ts.push(`export type ${initializerName} = ${init.ts};`);
 | |
| 
 | |
|     channels_ts.push(`export interface ${channelName}Channel extends ${(item.extends || '') + 'Channel'} {`);
 | |
|     const ts_types = new Map();
 | |
| 
 | |
|     for (let [eventName, event] of Object.entries(item.events || {})) {
 | |
|       if (event === null)
 | |
|         event = {};
 | |
|       const parameters = objectType(event.parameters || {}, '');
 | |
|       const paramsName = `${channelName}${titleCase(eventName)}Event`;
 | |
|       ts_types.set(paramsName, parameters.ts);
 | |
|       channels_ts.push(`  on(event: '${eventName}', callback: (params: ${paramsName}) => void): this;`);
 | |
|     }
 | |
| 
 | |
|     for (let [methodName, method] of Object.entries(item.commands || {})) {
 | |
|       if (method === null)
 | |
|         method = {};
 | |
|       const parameters = objectType(method.parameters || {}, '');
 | |
|       const paramsName = `${channelName}${titleCase(methodName)}Params`;
 | |
|       ts_types.set(paramsName, parameters.ts);
 | |
|       addScheme(paramsName, method.parameters ? parameters.scheme : `tOptional(tObject({}))`);
 | |
|       for (const key of inherits.keys()) {
 | |
|         if (inherits.get(key) === channelName)
 | |
|           addScheme(`${key}${titleCase(methodName)}Params`, `tType('${paramsName}')`);
 | |
|       }
 | |
| 
 | |
|       const resultName = `${channelName}${titleCase(methodName)}Result`;
 | |
|       const returns = objectType(method.returns || {}, '');
 | |
|       ts_types.set(resultName, method.returns ? returns.ts : 'void');
 | |
| 
 | |
|       channels_ts.push(`  ${methodName}(params${method.parameters ? '' : '?'}: ${paramsName}): Promise<${resultName}>;`);
 | |
|     }
 | |
| 
 | |
|     channels_ts.push(`}`);
 | |
|     for (const [typeName, typeValue] of ts_types)
 | |
|       channels_ts.push(`export type ${typeName} = ${typeValue};`);
 | |
|   } else if (item.type === 'object') {
 | |
|     const inner = objectType(item.properties, '');
 | |
|     channels_ts.push(`export type ${name} = ${inner.ts};`);
 | |
|     addScheme(name, inner.scheme);
 | |
|   }
 | |
|   channels_ts.push(``);
 | |
| }
 | |
| 
 | |
| validator_ts.push(`
 | |
|   return scheme;
 | |
| }
 | |
| `);
 | |
| 
 | |
| fs.writeFileSync(path.join(__dirname, '..', 'src', 'rpc', 'channels.ts'), channels_ts.join('\n'), 'utf-8');
 | |
| fs.writeFileSync(path.join(__dirname, '..', 'src', 'rpc', 'validator.ts'), validator_ts.join('\n'), 'utf-8');
 | 
