@graphile/simplify-inflection
Version:
Simplifies the graphile-build/graphile-build-pg inflection to trim the `ByFooIdAndBarId` from relations, etc
435 lines • 21 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PgSimplifyInflectionPlugin = exports.PgSimplifyInflectionPreset = void 0;
exports.arraysMatch = arraysMatch;
/**
* Returns true if array1 and array2 have the same length, and every pair of
* values within them pass the `comparator` check (which defaults to `===`).
*/
function arraysMatch(array1, array2, comparator = (v1, v2) => v1 === v2) {
if (array1 === array2)
return true;
const l = array1.length;
if (l !== array2.length) {
return false;
}
for (let i = 0; i < l; i++) {
if (!comparator(array1[i], array2[i])) {
return false;
}
}
return true;
}
function fixCapitalisedPlural(previous, _preset, str) {
const original = previous(str);
return original.replace(/[0-9]S(?=[A-Z]|$)/g, (match) => match.toLowerCase());
}
function fixChangePlural(previous, _options, str) {
const matches = str.match(/([A-Z]|_[a-z0-9])[a-z0-9]*_*$/);
const index = matches ? matches.index + matches[1].length - 1 : 0;
const suffixMatches = str.match(/_*$/);
const suffixIndex = suffixMatches.index;
const prefix = str.substring(0, index);
const word = str.substring(index, suffixIndex);
const suffix = str.substring(suffixIndex);
return `${prefix}${previous(word)}${suffix}`;
}
// Users can add 'listSuffix === "omit"`/`"include"` smart tags, this handles
// that.
let globalPgOmitListSuffix = null;
function overrideListSuffix(listSuffix, cb) {
if (listSuffix == null) {
return cb();
}
if (listSuffix !== "include" && listSuffix !== "omit") {
throw new Error(`Unrecognized value "${listSuffix}". If is set, it must be "omit" or "include".`);
}
const oldOverridePgOmitListSuffix = globalPgOmitListSuffix;
try {
globalPgOmitListSuffix = listSuffix === "omit";
return cb();
}
finally {
globalPgOmitListSuffix = oldOverridePgOmitListSuffix;
}
}
const PgSimplifyInflectionPlugin = {
name: "PgSimplifyInflectionPlugin",
version: "0.0.0",
inflection: {
add: {
distinctPluralize(_preset, str) {
const singular = this.singularize(str);
const plural = this.pluralize(singular);
if (singular !== plural) {
return plural;
}
if (plural.endsWith("ch") ||
plural.endsWith("s") ||
plural.endsWith("sh") ||
plural.endsWith("x") ||
plural.endsWith("z")) {
return plural + "es";
}
else if (plural.endsWith("y")) {
return plural.slice(0, -1) + "ies";
}
else {
return plural + "s";
}
},
_getBaseName(_preset, attributeName) {
const matches = attributeName.match(/^(.+?)(_row_id|_id|_uuid|_fk|_pk|RowId|Id|Uuid|UUID|Fk|Pk)$/);
if (matches) {
return matches[1];
}
return null;
},
_baseNameMatches(_preset, baseName, otherName) {
const singularizedName = this.singularize(otherName);
return this.camelCase(baseName) === this.camelCase(singularizedName);
},
/* This is a good method to override. */
_getOppositeBaseName(_preset, baseName) {
return ({
/*
* Changes to this list are breaking changes and will require a
* major version update, so we need to group as many together as
* possible! Rather than sending a PR, please look for an open
* issue called something like "Add more opposites" (if there isn't
* one then please open it) and add your suggestions to the GitHub
* comments.
*/
parent: "child",
child: "parent",
author: "authored",
editor: "edited",
reviewer: "reviewed",
}[baseName] || null);
},
_getBaseNameFromKeys(preset, detailedKeys) {
if (detailedKeys.length === 1) {
const key = detailedKeys[0];
const attributeName = this._attributeName({
...key,
skipRowId: true,
});
return this._getBaseName(attributeName);
}
if (preset.schema?.pgSimplifyMultikeyRelations) {
const attributeNames = detailedKeys.map((key) => this._attributeName({ ...key, skipRowId: true }));
const baseNames = attributeNames.map((attributeName) => this._getBaseName(attributeName));
// Check none are null
if (baseNames.every((n) => n)) {
return baseNames.join("-");
}
}
return null;
},
},
ignoreReplaceIfNotExists: ["deletedNodeId"],
replace: {
/*
* This solves the issue with `blah-table1s` becoming `blahTable1S`
* (i.e. the capital S at the end) or `table1-connection becoming `Table1SConnection`
*/
camelCase: fixCapitalisedPlural,
upperCamelCase: fixCapitalisedPlural,
/*
* Pluralize/singularize only supports single words, so only run
* on the final segment of a name.
*/
pluralize: fixChangePlural,
singularize: fixChangePlural,
// Fix a naming bug
deletedNodeId(_prev, preset, { resource }) {
return this.camelCase(`deleted-${this.singularize(this.tableType(resource.codec))}-${this.nodeIdFieldName()}`);
},
patchField(previous, options, fieldName) {
return options.schema?.pgSimplifyPatch ? "patch" : previous(fieldName);
},
connectionField(_prev, options, baseName) {
return (globalPgOmitListSuffix ?? options.schema?.pgOmitListSuffix)
? baseName + "Connection"
: baseName;
},
listField(_prev, options, baseName) {
return (globalPgOmitListSuffix ?? options.schema?.pgOmitListSuffix)
? baseName
: baseName + "List";
},
allRowsConnection(previous, options, resource) {
const listSuffix = resource.extensions?.tags?.listSuffix;
return overrideListSuffix(listSuffix, () => {
if (options.schema?.pgSimplifyAllRows) {
return this.connectionField(this.camelCase(`${this.distinctPluralize(this._singularizedResourceName(resource))}`));
}
else {
return previous(resource);
}
});
},
allRowsList(previous, options, resource) {
const listSuffix = resource.extensions?.tags?.listSuffix;
return overrideListSuffix(listSuffix, () => {
if (options.schema?.pgSimplifyAllRows) {
return this.listField(this.camelCase(this.distinctPluralize(this._singularizedResourceName(resource))));
}
else {
return previous(resource);
}
});
},
allInterfaceModeUnionRowsConnection(previous, options, codec) {
const listSuffix = codec.extensions?.tags?.listSuffix;
return overrideListSuffix(listSuffix, () => {
if (options.schema?.pgSimplifyAllRows) {
return this.connectionField(this.camelCase(`${this.distinctPluralize(this._singularizedCodecName(codec))}`));
}
else {
return previous(codec);
}
});
},
allInterfaceModeUnionRowsList(previous, options, codec) {
const listSuffix = codec.extensions?.tags?.listSuffix;
return overrideListSuffix(listSuffix, () => {
if (options.schema?.pgSimplifyAllRows) {
return this.listField(this.camelCase(this.distinctPluralize(this._singularizedCodecName(codec))));
}
else {
return previous(codec);
}
});
},
singleRelation(previous, _options, details) {
const { registry, codec, relationName } = details;
const relation = registry.pgRelations[codec.name]?.[relationName];
if (typeof relation.extensions?.tags?.fieldName === "string") {
return relation.extensions.tags.fieldName;
}
const detailedKeys = relation.localAttributes.map((attributeName) => ({
codec,
attributeName,
}));
const baseName = this._getBaseNameFromKeys(detailedKeys);
if (baseName) {
return this.camelCase(baseName);
}
const foreignPk = relation.remoteResource.uniques.find((u) => u.isPrimary);
if (foreignPk &&
arraysMatch(foreignPk.attributes, relation.remoteAttributes)) {
return this.camelCase(`${this._singularizedCodecName(relation.remoteResource.codec)}`);
}
return previous(details);
},
singleRelationBackwards(previous, _options, details) {
const { registry, codec, relationName } = details;
const relation = registry.pgRelations[codec.name]?.[relationName];
if (typeof relation.extensions?.tags?.foreignSingleFieldName === "string") {
return relation.extensions.tags.foreignSingleFieldName;
}
if (typeof relation.extensions?.tags?.foreignFieldName === "string") {
return relation.extensions.tags.foreignFieldName;
}
const detailedKeys = relation.remoteAttributes.map((attributeName) => ({
codec: relation.remoteResource.codec,
attributeName,
}));
const baseName = this._getBaseNameFromKeys(detailedKeys);
if (baseName) {
const oppositeBaseName = this._getOppositeBaseName(baseName);
if (oppositeBaseName) {
return this.camelCase(`${oppositeBaseName}-${this._singularizedCodecName(relation.remoteResource.codec)}`);
}
if (this._baseNameMatches(baseName, codec.name)) {
return this.camelCase(`${this._singularizedCodecName(relation.remoteResource.codec)}`);
}
}
const possibleResources = Object.values(registry.pgResources).filter((resource) => resource.codec === codec && !resource.parameters);
if (possibleResources.length > 1) {
throw new Error(`singleRelationBackwards inflector check failed: multiple table-like resources for codec '${codec.name}', so we cannot determine the primary key - please override this inflector for this table.`);
}
const uniques = (possibleResources[0]?.uniques ??
[]);
const pk = uniques.find((u) => u.isPrimary);
if (pk && arraysMatch(pk.attributes, relation.localAttributes)) {
return this.camelCase(`${this._singularizedCodecName(relation.remoteResource.codec)}`);
}
return previous(details);
},
_manyRelation(previous, _options, details) {
const { registry, codec, relationName } = details;
const relation = registry.pgRelations[codec.name]?.[relationName];
const baseOverride = relation.extensions?.tags.foreignFieldName;
if (typeof baseOverride === "string") {
return baseOverride;
}
const detailedKeys = relation.remoteAttributes.map((attributeName) => ({
codec: relation.remoteResource.codec,
attributeName,
}));
const baseName = this._getBaseNameFromKeys(detailedKeys);
if (baseName) {
const oppositeBaseName = this._getOppositeBaseName(baseName);
if (oppositeBaseName) {
return this.camelCase(`${oppositeBaseName}-${this.distinctPluralize(this._singularizedCodecName(relation.remoteResource.codec))}`);
}
if (this._baseNameMatches(baseName, codec.name)) {
return this.camelCase(`${this.distinctPluralize(this._singularizedCodecName(relation.remoteResource.codec))}`);
}
}
const pk = relation.remoteResource.uniques.find((u) => u.isPrimary);
if (pk && arraysMatch(pk.attributes, relation.remoteAttributes)) {
return this.camelCase(`${this.distinctPluralize(this._singularizedCodecName(relation.remoteResource.codec))}`);
}
return previous(details);
},
manyRelationConnection(previous, _options, details) {
const { registry, codec, relationName } = details;
const relation = registry.pgRelations[codec.name]?.[relationName];
const listSuffix = relation.extensions?.tags?.listSuffix ??
relation.remoteResource.extensions?.tags?.listSuffix;
return overrideListSuffix(listSuffix, () => previous(details));
},
manyRelationList(previous, _options, details) {
const { registry, codec, relationName } = details;
const relation = registry.pgRelations[codec.name]?.[relationName];
const listSuffix = relation.extensions?.tags?.listSuffix ??
relation.remoteResource.extensions?.tags?.listSuffix;
return overrideListSuffix(listSuffix, () => previous(details));
},
customQueryConnectionField(previous, _options, details) {
const listSuffix = details.resource.extensions?.tags?.listSuffix;
return overrideListSuffix(listSuffix, () => previous(details));
},
customQueryListField(previous, _options, details) {
const listSuffix = details.resource.extensions?.tags?.listSuffix;
return overrideListSuffix(listSuffix, () => previous(details));
},
computedAttributeConnectionField(previous, _options, details) {
const listSuffix = details.resource.extensions?.tags?.listSuffix;
return overrideListSuffix(listSuffix, () => previous(details));
},
computedAttributeListField(previous, _options, details) {
const listSuffix = details.resource.extensions?.tags?.listSuffix;
return overrideListSuffix(listSuffix, () => previous(details));
},
nodeById(previous, options, typeName) {
if (options.schema?.pgShortPk) {
return this.camelCase(`${typeName}-by-${this.nodeIdFieldName()}`);
}
else {
return previous(typeName);
}
},
rowByUnique(previous, options, details) {
const { unique, resource } = details;
if (typeof unique.extensions?.tags?.fieldName === "string") {
return unique.extensions?.tags?.fieldName;
}
if (options.schema?.pgShortPk && unique.isPrimary) {
// Primary key, shorten!
return this.camelCase(this._singularizedCodecName(resource.codec));
}
else {
return previous(details);
}
},
updateByKeysField(previous, options, details) {
const { resource, unique } = details;
if (typeof unique.extensions?.tags.updateFieldName === "string") {
return unique.extensions.tags.updateFieldName;
}
if (options.schema?.pgShortPk && unique.isPrimary) {
return this.camelCase(`update-${this._singularizedCodecName(resource.codec)}`);
}
else {
return previous(details);
}
},
deleteByKeysField(previous, options, details) {
const { resource, unique } = details;
if (typeof unique.extensions?.tags.deleteFieldName === "string") {
return unique.extensions.tags.deleteFieldName;
}
if (options.schema?.pgShortPk && unique.isPrimary) {
// Primary key, shorten!
return this.camelCase(`delete-${this._singularizedCodecName(resource.codec)}`);
}
else {
return previous(details);
}
},
updateByKeysInputType(previous, options, details) {
const { resource, unique } = details;
if (unique.extensions?.tags.updateFieldName) {
return this.upperCamelCase(`${unique.extensions.tags.updateFieldName}-input`);
}
if (options.schema?.pgShortPk && unique.isPrimary) {
// Primary key, shorten!
return this.upperCamelCase(`update-${this._singularizedCodecName(resource.codec)}-input`);
}
else {
return previous(details);
}
},
deleteByKeysInputType(previous, options, details) {
const { resource, unique } = details;
if (unique.extensions?.tags.deleteFieldName) {
return this.upperCamelCase(`${unique.extensions.tags.deleteFieldName}-input`);
}
if (options.schema?.pgShortPk && unique.isPrimary) {
// Primary key, shorten!
return this.upperCamelCase(`delete-${this._singularizedCodecName(resource.codec)}-input`);
}
else {
return previous(details);
}
},
updateNodeField(previous, options, details) {
if (options.schema?.pgShortPk) {
return this.camelCase(`update-${this._singularizedCodecName(details.resource.codec)}-by-${this.nodeIdFieldName()}`);
}
else {
return previous(details);
}
},
deleteNodeField(previous, options, details) {
if (options.schema?.pgShortPk) {
return this.camelCase(`delete-${this._singularizedCodecName(details.resource.codec)}-by-${this.nodeIdFieldName()}`);
}
else {
return previous(details);
}
},
updateNodeInputType(previous, options, details) {
if (options.schema?.pgShortPk) {
return this.upperCamelCase(`update-${this._singularizedCodecName(details.resource.codec)}-by-${this.nodeIdFieldName()}-input`);
}
else {
return previous(details);
}
},
deleteNodeInputType(previous, options, details) {
if (options.schema?.pgShortPk) {
return this.upperCamelCase(`delete-${this._singularizedCodecName(details.resource.codec)}-by-${this.nodeIdFieldName()}-input`);
}
else {
return previous(details);
}
},
},
},
};
exports.PgSimplifyInflectionPlugin = PgSimplifyInflectionPlugin;
exports.PgSimplifyInflectionPreset = {
plugins: [PgSimplifyInflectionPlugin],
schema: {
pgSimplifyPatch: true,
pgSimplifyAllRows: true,
pgShortPk: true,
pgSimplifyMultikeyRelations: true,
},
};
//# sourceMappingURL=index.js.map