Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 33 additions & 50 deletions utils/doclint/generateDotnetApi.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ const optionTypes = new Map();
const customTypeNames = new Map([
['domcontentloaded', 'DOMContentLoaded'],
['networkidle', 'NetworkIdle'],
['File', 'FilePayload'],
]);

const outputDir = process.argv[2] || path.join(__dirname, 'generate_types', 'csharp');
Expand Down Expand Up @@ -394,8 +393,28 @@ function generateNameDefault(member, name, t, parent) {
if (attemptedName.endsWith('s')
&& !["properties", "httpcredentials"].includes(attemptedName.toLowerCase()))
attemptedName = attemptedName.substring(0, attemptedName.length - 1);
if (customTypeNames.get(attemptedName))
attemptedName = customTypeNames.get(attemptedName);

// For some of these we don't want to generate generic types.
// For some others we simply did not have the code that was deduping the names.
if (attemptedName === 'BoundingBox')
attemptedName = `${parent.name}BoundingBoxResult`;
if (attemptedName === 'BrowserContextCookie')
attemptedName = 'BrowserContextCookiesResult';
if (attemptedName === 'File')
attemptedName = `FilePayload`;
if (attemptedName === 'Size')
attemptedName = 'RequestSizesResult';
if (attemptedName === 'ViewportSize' && parent.name === 'Page')
attemptedName = 'PageViewportSizeResult';
if (attemptedName === 'SecurityDetail')
attemptedName = 'ResponseSecurityDetailsResult';
if (attemptedName === 'ServerAddr')
attemptedName = 'ResponseServerAddrResult';
if (attemptedName === 'Timing')
attemptedName = 'RequestTimingResult';
if (attemptedName === 'HeadersArray')
attemptedName = 'Header';

let probableType = modelTypes.get(attemptedName);
if ((probableType && typesDiffer(t, probableType))
|| (["Value"].includes(attemptedName))) {
Expand Down Expand Up @@ -457,18 +476,6 @@ function renderMethod(member, parent, name, options, out) {
if (options.trimRunAndPrefix)
name = name.substring('RunAnd'.length);

/**
* @param {Documentation.Type} type
* @returns
*/
function resolveType(type) {
return translateType(type, parent, (t) => {
let newName = `${parent.name}${toMemberName(member)}Result`;
documentedResults.set(newName, `Result of calling <see cref="I${toTitleCase(parent.name)}.${toMemberName(member, true)}"/>.`);
return newName;
});
}

/** @type {Map<string, string[]>} */
const paramDocs = new Map();
const addParamsDoc = (paramName, docs) => {
Expand All @@ -479,32 +486,8 @@ function renderMethod(member, parent, name, options, out) {
paramDocs.set(paramName, docs);
};

/** @type {string} */
let type = null;
// need to check the original one
if (member.type.name === 'Object' || member.type.name === 'Array') {
let innerType = member.type;
let isArray = false;
if (innerType.name === 'Array') {
// we want to influence the name, but also change the object type
innerType = member.type.templates[0];
isArray = true;
}

if (innerType.expression === '[Object]<[string], [string]>') {
// do nothing, because this is handled down the road
} else if (!isArray && !innerType.properties) {
type = `dynamic`;
} else {
type = classNameMap.get(innerType.name);
if (!type)
type = resolveType(innerType);
if (isArray)
type = `IReadOnlyList<${type}>`;
}
}
let type = translateType(member.type, parent, t => generateNameDefault(member, name, t, parent), false, true);

type = type || resolveType(member.type);
// TODO: this is something that will probably go into the docs
// translate simple getters into read-only properties, and simple
// set-only methods to settable properties
Expand Down Expand Up @@ -737,14 +720,14 @@ function renderMethod(member, parent, name, options, out) {
* @param {boolean=} optional
* @returns {string}
*/
function translateType(type, parent, generateNameCallback = t => t.name, optional = false) {
function translateType(type, parent, generateNameCallback = t => t.name, optional = false, isReturnType = false) {
// a few special cases we can fix automatically
if (type.expression === '[null]|[Error]')
return 'void';

if (type.union) {
if (type.union[0].name === 'null' && type.union.length === 2)
return translateType(type.union[1], parent, generateNameCallback, true);
return translateType(type.union[1], parent, generateNameCallback, true, isReturnType);

if (type.expression === '[string]|[Buffer]')
return `byte[]`; // TODO: make sure we implement extension methods for this!
Expand All @@ -769,17 +752,17 @@ function translateType(type, parent, generateNameCallback = t => t.name, optiona
if (type.templates.length != 1)
throw new Error(`Array (${type.name} from ${parent.name}) has more than 1 dimension. Panic.`);

let innerType = translateType(type.templates[0], parent, generateNameCallback);
return `IEnumerable<${innerType}>`;
let innerType = translateType(type.templates[0], parent, generateNameCallback, false, isReturnType);
return isReturnType ? `IReadOnlyList<${innerType}>` : `IEnumerable<${innerType}>`;
}

if (type.name === 'Object') {
// take care of some common cases
// TODO: this can be genericized
if (type.templates && type.templates.length == 2) {
// get the inner types of both templates, and if they're strings, it's a keyvaluepair string, string,
let keyType = translateType(type.templates[0], parent, generateNameCallback);
let valueType = translateType(type.templates[1], parent, generateNameCallback);
let keyType = translateType(type.templates[0], parent, generateNameCallback, false, isReturnType);
let valueType = translateType(type.templates[1], parent, generateNameCallback, false, isReturnType);
if (parent.name === 'Request' || parent.name === 'Response')
return `Dictionary<${keyType}, ${valueType}>`;
return `IEnumerable<KeyValuePair<${keyType}, ${valueType}>>`;
Expand All @@ -803,8 +786,8 @@ function translateType(type, parent, generateNameCallback = t => t.name, optiona
if (type.name === 'Map') {
if (type.templates && type.templates.length == 2) {
// we map to a dictionary
let keyType = translateType(type.templates[0], parent, generateNameCallback);
let valueType = translateType(type.templates[1], parent, generateNameCallback);
let keyType = translateType(type.templates[0], parent, generateNameCallback, false, isReturnType);
let valueType = translateType(type.templates[1], parent, generateNameCallback, false, isReturnType);
return `Dictionary<${keyType}, ${valueType}>`;
} else {
throw 'Map has invalid number of templates.';
Expand All @@ -817,7 +800,7 @@ function translateType(type, parent, generateNameCallback = t => t.name, optiona

let argsList = '';
if (type.args) {
let translatedCallbackArguments = type.args.map(t => translateType(t, parent, generateNameCallback));
let translatedCallbackArguments = type.args.map(t => translateType(t, parent, generateNameCallback, false, isReturnType));
if (translatedCallbackArguments.includes(null))
throw new Error('There was an argument we could not parse. Aborting.');

Expand All @@ -828,7 +811,7 @@ function translateType(type, parent, generateNameCallback = t => t.name, optiona
// this is an Action
return `Action<${argsList}>`;
} else {
let returnType = translateType(type.returnType, parent, generateNameCallback);
let returnType = translateType(type.returnType, parent, generateNameCallback, false, isReturnType);
if (returnType == null)
throw new Error('Unexpected null as return type.');

Expand Down