typedoc
Version:
Create api documentation for TypeScript projects.
238 lines (237 loc) • 9.57 kB
JavaScript
import { ok } from "assert";
import { ArrayType, ConditionalType, DeclarationReflection, DocumentReflection, IndexedAccessType, InferredType, IntersectionType, IntrinsicType, LiteralType, MappedType, NamedTupleMember, OptionalType, ParameterReflection, PredicateType, ProjectReflection, QueryType, ReferenceReflection, ReferenceType, Reflection, ReflectionKind, ReflectionType, RestType, SignatureReflection, TemplateLiteralType, TupleType, TypeOperatorType, TypeParameterReflection, UnionType, UnknownType, } from "../models/index.js";
import { insertPrioritySorted } from "../utils/array.js";
/**
* Deserializes TypeDoc's JSON output back to {@link Reflection} instances.
*
* @group Common
* @summary Deserializes TypeDoc's JSON output
*/
export class Deserializer {
application;
deferred = [];
deserializers = [];
activeReflection = [];
constructor(application) {
this.application = application;
}
get logger() {
return this.application.logger;
}
reflectionBuilders = {
declaration(parent, obj) {
return new DeclarationReflection(obj.name, obj.kind, parent);
},
document(parent, obj) {
return new DocumentReflection(obj.name, parent, [], {});
},
param(parent, obj) {
return new ParameterReflection(obj.name, obj.kind, parent);
},
project() {
throw new Error("Not supported, use Deserializer.reviveProject(s) instead.");
},
reference(parent, obj) {
// Ugly, but we don't have a reference yet!
return new ReferenceReflection(obj.name,
/* target */ parent, parent);
},
signature(parent, obj) {
return new SignatureReflection(obj.name, obj.kind, parent);
},
typeParam(parent, obj) {
return new TypeParameterReflection(obj.name, parent, void 0);
},
};
typeBuilders = {
array(obj, de) {
return new ArrayType(de.reviveType(obj.elementType));
},
conditional(obj, de) {
return new ConditionalType(de.reviveType(obj.checkType), de.reviveType(obj.extendsType), de.reviveType(obj.trueType), de.reviveType(obj.falseType));
},
indexedAccess(obj, de) {
return new IndexedAccessType(de.reviveType(obj.objectType), de.reviveType(obj.indexType));
},
inferred(obj, de) {
return new InferredType(obj.name, de.reviveType(obj.constraint));
},
intersection(obj, de) {
return new IntersectionType(obj.types.map((t) => de.reviveType(t)));
},
intrinsic(obj) {
return new IntrinsicType(obj.name);
},
literal(obj) {
if (obj.value && typeof obj.value === "object") {
return new LiteralType(BigInt(`${obj.value.negative ? "-" : ""}${obj.value.value}`));
}
return new LiteralType(obj.value);
},
mapped(obj, de) {
return new MappedType(obj.parameter, de.reviveType(obj.parameterType), de.reviveType(obj.templateType), obj.readonlyModifier, obj.optionalModifier, de.reviveType(obj.nameType));
},
optional(obj, de) {
return new OptionalType(de.reviveType(obj.elementType));
},
predicate(obj, de) {
return new PredicateType(obj.name, obj.asserts, de.reviveType(obj.targetType));
},
query(obj, de) {
return new QueryType(de.reviveType(obj.queryType));
},
reference(obj) {
// Correct reference will be restored in fromObject
return ReferenceType.createResolvedReference(obj.name, -2, null);
},
reflection(obj, de) {
return new ReflectionType(de.revive(obj.declaration, (o) => de.constructReflection(o)));
},
rest(obj, de) {
return new RestType(de.reviveType(obj.elementType));
},
templateLiteral(obj, de) {
return new TemplateLiteralType(obj.head, obj.tail.map(([t, s]) => [de.reviveType(t), s]));
},
tuple(obj, de) {
return new TupleType(obj.elements?.map((t) => de.reviveType(t)) || []);
},
namedTupleMember(obj, de) {
return new NamedTupleMember(obj.name, obj.isOptional, de.reviveType(obj.element));
},
typeOperator(obj, de) {
return new TypeOperatorType(de.reviveType(obj.target), obj.operator);
},
union(obj, de) {
return new UnionType(obj.types.map((t) => de.reviveType(t)));
},
unknown(obj) {
return new UnknownType(obj.name);
},
};
/**
* Only set when deserializing.
*/
projectRoot;
oldIdToNewId = {};
oldFileIdToNewFileId = {};
project;
addDeserializer(de) {
insertPrioritySorted(this.deserializers, de);
}
/**
* Revive a single project into the structure it was originally created with.
* This is generally not appropriate for merging multiple projects since projects may
* contain reflections in their root, not inside a module.
*/
reviveProject(name, projectObj, options) {
ok(this.deferred.length === 0, "Deserializer.defer was called when not deserializing");
const project = new ProjectReflection(name || projectObj.name, options.registry);
if (options.addProjectDocuments) {
this.application.converter.addProjectDocuments(project);
}
this.project = project;
this.projectRoot = options.projectRoot;
this.oldIdToNewId = { [projectObj.id]: project.id };
this.oldFileIdToNewFileId = {};
this.fromObject(project, projectObj);
const deferred = this.deferred;
this.deferred = [];
for (const def of deferred) {
def(project);
}
ok(this.deferred.length === 0, "Work may not be double deferred when deserializing.");
ok(this.activeReflection.length === 0, "Imbalanced reflection deserialization");
this.project = undefined;
this.projectRoot = undefined;
this.oldIdToNewId = {};
this.oldFileIdToNewFileId = {};
return project;
}
reviveProjects(name, projects, options) {
if (projects.length === 1 &&
!this.application.options.getValue("alwaysCreateEntryPointModule")) {
return this.reviveProject(name, projects[0], options);
}
const project = new ProjectReflection(name, options.registry);
if (options.addProjectDocuments) {
this.application.converter.addProjectDocuments(project);
}
this.project = project;
this.projectRoot = options.projectRoot;
for (const proj of projects) {
ok(this.deferred.length === 0, "Deserializer.defer was called when not deserializing");
const projModule = new DeclarationReflection(proj.name, ReflectionKind.Module, project);
project.registerReflection(projModule, undefined, undefined);
project.addChild(projModule);
this.oldIdToNewId = { [proj.id]: projModule.id };
this.oldFileIdToNewFileId = {};
this.fromObject(projModule, proj);
const deferred = this.deferred;
this.deferred = [];
for (const def of deferred) {
def(project);
}
ok(this.deferred.length === 0, "Work may not be double deferred when deserializing.");
ok(this.activeReflection.length === 0, "Imbalanced reflection deserialization");
}
this.oldIdToNewId = {};
this.oldFileIdToNewFileId = {};
this.project = undefined;
this.projectRoot = undefined;
return project;
}
revive(source, creator) {
if (source) {
const revived = creator(source);
this.fromObject(revived, source);
return revived;
}
}
reviveMany(sourceArray, creator) {
if (sourceArray) {
return sourceArray.map((item) => {
const revived = creator(item);
this.fromObject(revived, item);
return revived;
});
}
}
reviveType(obj) {
return this.revive(obj, (o) => this.constructType(o));
}
constructReflection(obj) {
ok(this.activeReflection.length > 0);
const result = this.reflectionBuilders[obj.variant](this.activeReflection[this.activeReflection.length - 1], obj);
this.oldIdToNewId[obj.id] = result.id;
this.project.registerReflection(result, undefined, undefined);
return result;
}
constructType(obj) {
const result = this.typeBuilders[obj.type](obj, this);
return result;
}
fromObject(receiver, obj) {
if (receiver instanceof Reflection) {
this.activeReflection.push(receiver);
}
receiver.fromObject(this, obj);
for (const de of this.deserializers) {
if (de.supports(receiver, obj)) {
de.fromObject(receiver, obj);
}
}
if (receiver instanceof Reflection) {
this.activeReflection.pop();
}
}
/**
* Defers work until the initial pass of serialization has been completed.
* This can be used to set up references which cannot be immediately restored.
*
* May only be called when deserializing.
*/
defer(cb) {
this.deferred.push(cb);
}
}