@typespec/compiler
Version:
TypeSpec Compiler Preview
128 lines • 4.89 kB
JavaScript
import { reportDiagnostic } from "../core/messages.js";
import { parseMimeType } from "../core/mime-type.js";
import { DuplicateTracker, useStateMap } from "../utils/index.js";
import { createStateSymbol } from "./utils.js";
const [getEncodedNamesMap, setEncodedNamesMap, getEncodedNamesStateMap] = useStateMap(createStateSymbol("encodedName"));
export function $encodedName(context, target, mimeType, name) {
let existing = getEncodedNamesMap(context.program, target);
if (existing === undefined) {
existing = new Map();
setEncodedNamesMap(context.program, target, existing);
}
const mimeTypeObj = parseMimeType(mimeType);
if (mimeTypeObj === undefined) {
reportDiagnostic(context.program, {
code: "invalid-mime-type",
format: { mimeType },
target: context.getArgumentTarget(0),
});
}
else if (mimeTypeObj.suffix) {
reportDiagnostic(context.program, {
code: "no-mime-type-suffix",
format: { mimeType, suffix: mimeTypeObj.suffix },
target: context.getArgumentTarget(0),
});
}
existing.set(mimeType, name);
}
function getEncodedName(program, target, mimeType) {
const mimeTypeObj = parseMimeType(mimeType);
if (mimeTypeObj === undefined) {
return undefined;
}
const resolvedMimeType = mimeTypeObj?.suffix
? `${mimeTypeObj.type}/${mimeTypeObj.suffix}`
: mimeType;
return getEncodedNamesMap(program, target)?.get(resolvedMimeType);
}
/**
* Resolve the encoded name for the given type when serialized to the given mime type.
* If a specific value was provided by `@encodedName` decorator for that mime type it will return that otherwise it will return the name of the type.
*
* @example
*
* For the given
* ```tsp
* model Certificate {
* @encodedName("application/json", "exp")
* @encodedName("application/xml", "expiry")
* expireAt: utcDateTime;
*
* }
* ```
*
* ```ts
* resolveEncodedName(program, type, "application/json") // exp
* resolveEncodedName(program, type, "application/merge-patch+json") // exp
* resolveEncodedName(program, type, "application/xml") // expireAt
* resolveEncodedName(program, type, "application/yaml") // expiry
* ```
*/
export function resolveEncodedName(program, target, mimeType) {
return getEncodedName(program, target, mimeType) ?? target.name;
}
/**
* Validate encoded names for conflicts. Validate the encoded names doesn't encode to an existing property name and that 2 encoded names don't map to the same name for the same mime type.
* @internal
*/
export function validateEncodedNamesConflicts(program) {
const duplicateTrackers = new Map();
function getOrCreateDuplicateTracker(type, mimeType) {
let perMimeTypes = duplicateTrackers.get(type);
if (perMimeTypes === undefined) {
perMimeTypes = new Map();
duplicateTrackers.set(type, perMimeTypes);
}
let tracker = perMimeTypes.get(mimeType);
if (tracker === undefined) {
tracker = new DuplicateTracker();
perMimeTypes.set(mimeType, tracker);
}
return tracker;
}
for (const [target, map] of getEncodedNamesStateMap(program).entries()) {
const scope = getScope(target);
if (scope === undefined) {
return;
}
for (const [mimeType, name] of map.entries()) {
const duplicateTracker = getOrCreateDuplicateTracker(scope.parent, mimeType);
duplicateTracker.track(name, target);
if (scope.members.has(name)) {
reportDiagnostic(program, {
code: "encoded-name-conflict",
format: { name, mimeType },
target: target,
});
}
}
}
for (const perMimeTypes of duplicateTrackers.values()) {
for (const [mimeType, tracker] of perMimeTypes.entries()) {
for (const [duplicateName, items] of tracker.entries()) {
for (const item of items) {
reportDiagnostic(program, {
code: "encoded-name-conflict",
messageId: "duplicate",
format: { name: duplicateName, mimeType },
target: item,
});
}
}
}
}
}
function getScope(type) {
switch (type.kind) {
case "ModelProperty":
return type.model && { parent: type.model, members: type.model.properties };
case "EnumMember":
return { parent: type.enum, members: type.enum.members };
case "UnionVariant":
return { parent: type.union, members: type.union.variants };
default:
return undefined;
}
}
//# sourceMappingURL=encoded-names.js.map