mirror of
https://github.com/microsoft/playwright.git
synced 2024-12-12 11:50:22 +03:00
9e41518c92
Currently, metadata does only contain the stack trace, and we send it from the JS client.
249 lines
8.8 KiB
JavaScript
Executable File
249 lines
8.8 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 (type === 'json')
|
|
return { ts: 'any', scheme: 'tAny', 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, onlyOptional) {
|
|
const ts = [];
|
|
const scheme = [];
|
|
for (const [name, value] of Object.entries(props)) {
|
|
const inner = inlineType(value, indent);
|
|
if (onlyOptional && !inner.optional)
|
|
continue;
|
|
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, onlyOptional = false) {
|
|
if (!Object.entries(props).length)
|
|
return { ts: `{}`, scheme: `tObject({})` };
|
|
const inner = properties(props, indent + ' ', onlyOptional);
|
|
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, tAny, 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', 'protocol', '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`;
|
|
const optionsName = `${channelName}${titleCase(methodName)}Options`;
|
|
ts_types.set(paramsName, parameters.ts);
|
|
ts_types.set(optionsName, objectType(method.parameters || {}, '', true).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}, metadata?: Metadata): 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;
|
|
}
|
|
`);
|
|
|
|
let hasChanges = false;
|
|
|
|
function writeFile(filePath, content) {
|
|
const existing = fs.readFileSync(filePath, 'utf8');
|
|
if (existing === content)
|
|
return;
|
|
hasChanges = true;
|
|
const root = path.join(__dirname, '..');
|
|
console.log(`Writing //${path.relative(root, filePath)}`);
|
|
fs.writeFileSync(filePath, content, 'utf8');
|
|
}
|
|
|
|
writeFile(path.join(__dirname, '..', 'src', 'protocol', 'channels.ts'), channels_ts.join('\n'));
|
|
writeFile(path.join(__dirname, '..', 'src', 'protocol', 'validator.ts'), validator_ts.join('\n'));
|
|
process.exit(hasChanges ? 1 : 0);
|