Much stricter ts

This commit is contained in:
DrMint 2023-08-14 22:09:29 +02:00
parent f56ba4675f
commit f28a928442
44 changed files with 656 additions and 355 deletions

View File

@ -0,0 +1,9 @@
import { Access } from "payload/config";
import { RecordersRoles } from "../../constants";
import { Recorder } from "../../types/collections";
import { isDefined, isUndefined } from "../../utils/asserts";
export const mustBeAdmin: Access<unknown, Recorder> = ({ req: { user } }): boolean => {
if (isUndefined(user)) return false;
return isDefined(user.role) && user.role.includes(RecordersRoles.Admin);
};

View File

@ -1,11 +1,10 @@
import { Access } from "payload/config";
import { Recorder } from "../../types/collections";
import { RecordersRoles } from "../../constants";
import { Recorder } from "../../types/collections";
import { isUndefined } from "../../utils/asserts";
export const mustBeAdminOrSelf: Access = ({ req }) => {
const user = req.user as Recorder | undefined;
export const mustBeAdminOrSelf: Access<unknown, Recorder> = ({ req: { user } }) => {
if (isUndefined(user)) return false;
if (user.role.includes(RecordersRoles.Admin)) return true;
if (user.role?.includes(RecordersRoles.Admin)) return true;
return { id: { equals: user.id } };
};

View File

@ -1,8 +1,8 @@
import { Access } from "payload/config";
import { Recorder } from "../../types/collections";
import { isUndefined } from "../../utils/asserts";
import { isDefined, isUndefined } from "../../utils/asserts";
export const mustHaveAtLeastOneRole = ({ req }): boolean => {
const user = req.user as Recorder | undefined;
export const mustHaveAtLeastOneRole: Access<unknown, Recorder> = ({ req: { user } }): boolean => {
if (isUndefined(user)) return false;
return user.role.length > 0;
return isDefined(user.role) && user.role.length > 0;
};

View File

@ -0,0 +1,9 @@
import { FieldAccess } from "payload/types";
import { RecordersRoles } from "../../constants";
import { Recorder } from "../../types/collections";
import { isDefined, isUndefined } from "../../utils/asserts";
export const mustBeAdmin: FieldAccess<any, any, Recorder> = ({ req: { user } }): boolean => {
if (isUndefined(user)) return false;
return isDefined(user.role) && user.role.includes(RecordersRoles.Admin);
};

View File

@ -1,9 +0,0 @@
import { Recorder } from "../types/collections";
import { RecordersRoles } from "../constants";
import { isUndefined } from "../utils/asserts";
export const mustBeAdmin = ({ req }): boolean => {
const user = req.user as Recorder | undefined;
if (isUndefined(user)) return false;
return user.role.includes(RecordersRoles.Admin);
};

View File

@ -1,5 +1,5 @@
import { CollectionConfig } from "payload/types";
import { mustBeAdmin } from "../../accesses/mustBeAdmin";
import { mustBeAdmin } from "../../accesses/collections/mustBeAdmin";
import { CollectionGroups, Collections } from "../../constants";
import { backPropagationField } from "../../fields/backPropagationField/backPropagationField";
import { slugField } from "../../fields/slugField/slugField";

View File

@ -1,8 +1,17 @@
import { Collections } from "../../../constants";
import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint";
import { ChronologyEra } from "../../../types/collections";
import { StrapiLanguage } from "../../../types/strapi";
import { isUndefined } from "../../../utils/asserts";
export const importFromStrapi = createStrapiImportEndpoint<ChronologyEra>({
type StrapiChronologyEra = {
slug: string;
starting_year: number;
ending_year: number;
title: { title: string; language: StrapiLanguage; description?: string }[];
};
export const importFromStrapi = createStrapiImportEndpoint<ChronologyEra, StrapiChronologyEra>({
strapi: {
collection: "chronology-eras",
params: {
@ -15,11 +24,15 @@ export const importFromStrapi = createStrapiImportEndpoint<ChronologyEra>({
slug,
startingYear: starting_year,
endingYear: ending_year,
translations: titles.map(({ language, title, description }) => ({
language: language.data.attributes.code,
translations: titles.map(({ language, title, description }) => {
if (isUndefined(language.data))
throw new Error("Language is undefined for one of the translations");
return {
language: language.data?.attributes.code,
title,
description,
})),
};
}),
}),
},
});

View File

@ -1,9 +1,14 @@
import { CollectionBeforeValidateHook } from "payload/types";
import { ChronologyEra } from "../../../types/collections";
import { isUndefined } from "../../../utils/asserts";
export const beforeValidateEndingGreaterThanStarting: CollectionBeforeValidateHook<
ChronologyEra
> = async ({ data: { startingYear, endingYear } }) => {
> = async ({ data }) => {
if (isUndefined(data)) throw new Error("The data is undefined");
const { startingYear, endingYear } = data;
if (isUndefined(endingYear)) throw new Error("Ending year is undefined");
if (isUndefined(startingYear)) throw new Error("Starting year is undefined");
if (endingYear < startingYear) {
throw new Error("The ending year cannot be before the starting year.");
}

View File

@ -2,11 +2,16 @@ import payload from "payload";
import { CollectionBeforeValidateHook } from "payload/types";
import { Collections } from "../../../constants";
import { ChronologyEra } from "../../../types/collections";
import { hasIntersection } from "../../../utils/asserts";
import { hasIntersection, isUndefined } from "../../../utils/asserts";
export const beforeValidateNoIntersection: CollectionBeforeValidateHook<ChronologyEra> = async ({
data: { startingYear, endingYear },
data,
}) => {
if (isUndefined(data)) throw new Error("The data is undefined");
const { startingYear, endingYear } = data;
if (isUndefined(endingYear)) throw new Error("Ending year is undefined");
if (isUndefined(startingYear)) throw new Error("Starting year is undefined");
const otherEras = await payload.find({
collection: Collections.ChronologyEras,
limit: 100,

View File

@ -72,6 +72,7 @@ export const ChronologyItems: CollectionConfig = buildVersionedCollectionConfig(
if (!DateTime.fromObject({ year, month, day }).isValid) {
return `The given date (${stringDate}) is not a valid date.`;
}
return true;
},
fields: [
{

View File

@ -1,8 +1,24 @@
import { Collections } from "../../../constants";
import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint";
import { ChronologyItem } from "../../../types/collections";
import { StrapiLanguage } from "../../../types/strapi";
import { isUndefined } from "../../../utils/asserts";
export const importFromStrapi = createStrapiImportEndpoint<ChronologyItem>({
type StrapiChronologyItem = {
year: number;
month?: number;
day?: number;
events: {
translations: {
title?: string;
description?: string;
note?: string;
language: StrapiLanguage;
}[];
}[];
};
export const importFromStrapi = createStrapiImportEndpoint<ChronologyItem, StrapiChronologyItem>({
strapi: {
collection: "chronology-items",
params: {
@ -14,7 +30,10 @@ export const importFromStrapi = createStrapiImportEndpoint<ChronologyItem>({
convert: ({ year, month, day, events }, user) => ({
date: { year, month, day },
events: events.map((event) => ({
translations: event.translations.map(({ title, description, note, language }) => ({
translations: event.translations.map(({ title, description, note, language }) => {
if (isUndefined(language.data))
throw new Error("A language is required for a chronology item event translation");
return {
title,
description,
note,
@ -23,7 +42,8 @@ export const importFromStrapi = createStrapiImportEndpoint<ChronologyItem>({
...(language.data.attributes.code === "en"
? { transcribers: [user.id] }
: { translators: [user.id] }),
})),
};
}),
})),
}),
},

View File

@ -6,8 +6,12 @@ export const beforeValidatePopulateNameField: FieldHook<
ChronologyItem,
ChronologyItem["name"],
ChronologyItem
> = ({ data: { date } }) => {
if (isUndefined(date?.year)) return "????-??-??";
> = ({ data }) => {
if (isUndefined(data)) {
return "????-??-??";
}
const { date } = data;
if (isUndefined(date) || isUndefined(date?.year)) return "????-??-??";
const { year, month, day } = date;
let result = String(year).padStart(5, " ");
if (isDefined(month)) {

View File

@ -1,5 +1,5 @@
import { text } from "payload/dist/fields/validations";
import { mustBeAdmin } from "../../accesses/mustBeAdmin";
import { mustBeAdmin } from "../../accesses/collections/mustBeAdmin";
import { CollectionGroups, Collections } from "../../constants";
import { buildCollectionConfig } from "../../utils/collectionConfig";
import { importFromStrapi } from "./endpoints/importFromStrapi";

View File

@ -2,7 +2,12 @@ import { Collections } from "../../../constants";
import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint";
import { Language } from "../../../types/collections";
export const importFromStrapi = createStrapiImportEndpoint<Language>({
type StrapiLanguage = {
code: string;
name: string;
};
export const importFromStrapi = createStrapiImportEndpoint<Language, StrapiLanguage>({
strapi: {
collection: "currencies",
params: {},

View File

@ -1,11 +1,11 @@
import payload from "payload";
import { mustBeAdmin } from "../../accesses/mustBeAdmin";
import { mustBeAdmin } from "../../accesses/collections/mustBeAdmin";
import { QuickFilters } from "../../components/QuickFilters";
import { CollectionGroups, Collections, KeysTypes, LanguageCodes } from "../../constants";
import { translatedFields } from "../../fields/translatedFields/translatedFields";
import { beforeDuplicateAddCopyTo } from "../../hooks/beforeDuplicateAddCopyTo";
import { Key } from "../../types/collections";
import { isDefined } from "../../utils/asserts";
import { isDefined, isUndefined } from "../../utils/asserts";
import { buildCollectionConfig } from "../../utils/collectionConfig";
import { importFromStrapi } from "./endpoints/importFromStrapi";
@ -58,15 +58,15 @@ export const Keys = buildCollectionConfig({
},
hooks: {
beforeValidate: [
async ({ data: { name, type } }) => {
async ({ data }) => {
if (isUndefined(data)) return;
const { name, type } = data;
const result = await payload.find({
collection: Collections.Keys,
where: { name: { equals: name }, type: { equals: type } },
});
if (result.docs.length > 0) {
throw new Error(
`A Key of type "${KeysTypes[type]}" already exists with the name "${name}"`
);
throw new Error(`A Key of type "${type}" already exists with the name "${name}"`);
}
},
],

View File

@ -1,14 +1,14 @@
import payload from "payload";
import { CollectionConfig } from "payload/types";
import { Collections } from "../../../constants";
import {
getAllStrapiEntries,
importStrapiEntries,
} from "../../../endpoints/createStrapiImportEndpoint";
import { Key } from "../../../types/collections";
import { isDefined } from "../../../utils/asserts";
import { CollectionEndpoint, PayloadCreateData } from "../../../types/payload";
import { StrapiLanguage } from "../../../types/strapi";
import { isDefined, isUndefined } from "../../../utils/asserts";
import { formatToCamelCase } from "../../../utils/string";
import { PayloadCreateData } from "../../../utils/types";
const importStrapiWordings: typeof importStrapiEntries = async ({
payload: payloadParams,
@ -30,7 +30,7 @@ const importStrapiWordings: typeof importStrapiEntries = async ({
.filter(({ name }) => isDefined(name) && name !== ""),
}));
const errors = [];
const errors: string[] = [];
await Promise.all(
entries.map(async (entry) => {
@ -42,15 +42,17 @@ const importStrapiWordings: typeof importStrapiEntries = async ({
});
} catch (e) {
console.warn(e);
if (typeof e === "object" && isDefined(e) && "name" in e) {
errors.push(`${e.name} with ${entry.name}`);
}
}
})
);
return { count: entries.length, errors };
};
export const importFromStrapi: CollectionConfig["endpoints"][number] = {
export const importFromStrapi: CollectionEndpoint = {
method: "get",
path: "/strapi",
handler: async (req, res) => {
@ -64,7 +66,15 @@ export const importFromStrapi: CollectionConfig["endpoints"][number] = {
});
}
const { count: categoriesCount, errors: categoriesErrors } = await importStrapiEntries<Key>({
type StrapiCategories = {
slug: string;
titles: { title?: string; short?: string; language: StrapiLanguage }[];
};
const { count: categoriesCount, errors: categoriesErrors } = await importStrapiEntries<
Key,
StrapiCategories
>({
strapi: {
collection: "categories",
params: { populate: { titles: { populate: "language" } } },
@ -74,18 +84,31 @@ export const importFromStrapi: CollectionConfig["endpoints"][number] = {
convert: ({ slug, titles }) => ({
name: slug,
type: "Categories",
translations: titles.map(({ title, short, language }) => ({
translations: titles.map(({ title, short, language }) => {
if (isUndefined(language.data))
throw new Error("A language is required for a Keys title translation");
if (isUndefined(title))
throw new Error("A title is required for a Keys title translation");
return {
name: title,
short,
language: language.data.attributes.code,
})),
};
}),
}),
},
user: req.user,
});
const { count: contentTypesCount, errors: contentTypesErrors } = await importStrapiEntries<Key>(
{
type StrapiContentType = {
slug: string;
titles: { title: string; language: StrapiLanguage }[];
};
const { count: contentTypesCount, errors: contentTypesErrors } = await importStrapiEntries<
Key,
StrapiContentType
>({
strapi: {
collection: "content-types",
params: { populate: { titles: { populate: "language" } } },
@ -95,18 +118,28 @@ export const importFromStrapi: CollectionConfig["endpoints"][number] = {
convert: ({ slug, titles }) => ({
name: slug,
type: "Contents",
translations: titles.map(({ title, language }) => ({
translations: titles.map(({ title, language }) => {
if (isUndefined(language.data))
throw new Error("A language is required for a Keys title translation");
return {
name: title,
language: language.data.attributes.code,
})),
};
}),
}),
},
user: req.user,
}
);
});
const { count: gamePlatformsCount, errors: gamePlatformsErrors } =
await importStrapiEntries<Key>({
type StrapiGamePlatform = {
slug: string;
titles: { title?: string; short?: string; language: StrapiLanguage }[];
};
const { count: gamePlatformsCount, errors: gamePlatformsErrors } = await importStrapiEntries<
Key,
StrapiGamePlatform
>({
strapi: {
collection: "game-platforms",
params: { populate: { titles: { populate: "language" } } },
@ -116,17 +149,31 @@ export const importFromStrapi: CollectionConfig["endpoints"][number] = {
convert: ({ slug, titles }) => ({
name: slug,
type: "GamePlatforms",
translations: titles.map(({ title, short, language }) => ({
translations: titles.map(({ title, short, language }) => {
if (isUndefined(language.data))
throw new Error("A language is required for a Keys title translation");
if (isUndefined(title))
throw new Error("A title is required for a Keys title translation");
return {
name: title,
short,
language: language.data.attributes.code,
})),
};
}),
}),
},
user: req.user,
});
const { count: libraryCount, errors: libraryErrors } = await importStrapiEntries<Key>({
type StrapiMetadataTypes = {
slug: string;
titles: { title: string; language: StrapiLanguage }[];
};
const { count: libraryCount, errors: libraryErrors } = await importStrapiEntries<
Key,
StrapiMetadataTypes
>({
strapi: {
collection: "metadata-types",
params: { populate: { titles: { populate: "language" } } },
@ -136,17 +183,28 @@ export const importFromStrapi: CollectionConfig["endpoints"][number] = {
convert: ({ slug, titles }) => ({
name: slug,
type: "Library",
translations: titles.map(({ title, language }) => ({
translations: titles.map(({ title, language }) => {
if (isUndefined(language.data))
throw new Error("A language is required for a Keys title translation");
return {
name: title,
language: language.data.attributes.code,
})),
};
}),
}),
},
user: req.user,
});
const { count: libraryAudioCount, errors: libraryAudioErrors } = await importStrapiEntries<Key>(
{
type StrapiAudioSubtypes = {
slug: string;
titles: { title: string; language: StrapiLanguage }[];
};
const { count: libraryAudioCount, errors: libraryAudioErrors } = await importStrapiEntries<
Key,
StrapiAudioSubtypes
>({
strapi: {
collection: "audio-subtypes",
params: { populate: { titles: { populate: "language" } } },
@ -156,18 +214,28 @@ export const importFromStrapi: CollectionConfig["endpoints"][number] = {
convert: ({ slug, titles }) => ({
name: slug,
type: "LibraryAudio",
translations: titles.map(({ title, language }) => ({
translations: titles.map(({ title, language }) => {
if (isUndefined(language.data))
throw new Error("A language is required for a Keys title translation");
return {
name: title,
language: language.data.attributes.code,
})),
};
}),
}),
},
user: req.user,
}
);
});
const { count: libraryGroupCount, errors: libraryGroupErrors } = await importStrapiEntries<Key>(
{
type StrapiGroupSubtypes = {
slug: string;
titles: { title: string; language: StrapiLanguage }[];
};
const { count: libraryGroupCount, errors: libraryGroupErrors } = await importStrapiEntries<
Key,
StrapiGroupSubtypes
>({
strapi: {
collection: "group-subtypes",
params: { populate: { titles: { populate: "language" } } },
@ -177,18 +245,28 @@ export const importFromStrapi: CollectionConfig["endpoints"][number] = {
convert: ({ slug, titles }) => ({
name: slug,
type: "LibraryGroup",
translations: titles.map(({ title, language }) => ({
translations: titles.map(({ title, language }) => {
if (isUndefined(language.data))
throw new Error("A language is required for a Keys title translation");
return {
name: title,
language: language.data.attributes.code,
})),
};
}),
}),
},
user: req.user,
}
);
});
const { count: libraryTextualCount, errors: libraryTextualErrors } =
await importStrapiEntries<Key>({
type StrapiTextualSubtypes = {
slug: string;
titles: { title: string; language: StrapiLanguage }[];
};
const { count: libraryTextualCount, errors: libraryTextualErrors } = await importStrapiEntries<
Key,
StrapiTextualSubtypes
>({
strapi: {
collection: "textual-subtypes",
params: { populate: { titles: { populate: "language" } } },
@ -198,17 +276,28 @@ export const importFromStrapi: CollectionConfig["endpoints"][number] = {
convert: ({ slug, titles }) => ({
name: slug,
type: "LibraryTextual",
translations: titles.map(({ title, language }) => ({
translations: titles.map(({ title, language }) => {
if (isUndefined(language.data))
throw new Error("A language is required for a Keys title translation");
return {
name: title,
language: language.data.attributes.code,
})),
};
}),
}),
},
user: req.user,
});
const { count: libraryVideoCount, errors: libraryVideoErrors } = await importStrapiEntries<Key>(
{
type StrapiVideoSubtypes = {
slug: string;
titles: { title: string; language: StrapiLanguage }[];
};
const { count: libraryVideoCount, errors: libraryVideoErrors } = await importStrapiEntries<
Key,
StrapiVideoSubtypes
>({
strapi: {
collection: "video-subtypes",
params: { populate: { titles: { populate: "language" } } },
@ -218,17 +307,28 @@ export const importFromStrapi: CollectionConfig["endpoints"][number] = {
convert: ({ slug, titles }) => ({
name: slug,
type: "LibraryVideo",
translations: titles.map(({ title, language }) => ({
translations: titles.map(({ title, language }) => {
if (isUndefined(language.data))
throw new Error("A language is required for a Keys title translation");
return {
name: title,
language: language.data.attributes.code,
})),
};
}),
}),
},
user: req.user,
}
);
});
const { count: weaponsCount, errors: weaponsErrors } = await importStrapiEntries<Key>({
type StrapiWeaponTypes = {
slug: string;
translations: { name?: string; language: StrapiLanguage }[];
};
const { count: weaponsCount, errors: weaponsErrors } = await importStrapiEntries<
Key,
StrapiWeaponTypes
>({
strapi: {
collection: "weapon-story-types",
params: { populate: { translations: { populate: "language" } } },
@ -238,16 +338,22 @@ export const importFromStrapi: CollectionConfig["endpoints"][number] = {
convert: ({ slug, translations }) => ({
name: slug,
type: "Weapons",
translations: translations.map(({ name, language }) => ({
translations: translations.map(({ name, language }) => {
if (isUndefined(language.data))
throw new Error("A language is required for a Keys title translation");
if (isUndefined(name))
throw new Error("A name is required for a Keys title translation");
return {
name,
language: language.data.attributes.code,
})),
};
}),
}),
},
user: req.user,
});
const { count: wordingsCount, errors: wordingsErrors } = await importStrapiWordings({
const { count: wordingsCount, errors: wordingsErrors } = await importStrapiWordings<Key, Key>({
strapi: { collection: "website-interfaces", params: { populate: "ui_language" } },
payload: { collection: Collections.Keys, convert: (strapiObject) => strapiObject },
user: req.user,

View File

@ -1,5 +1,5 @@
import { text } from "payload/dist/fields/validations";
import { mustBeAdmin } from "../../accesses/mustBeAdmin";
import { mustBeAdmin } from "../../accesses/collections/mustBeAdmin";
import { publicAccess } from "../../accesses/publicAccess";
import { CollectionGroups, Collections } from "../../constants";
import { buildCollectionConfig } from "../../utils/collectionConfig";

View File

@ -2,7 +2,12 @@ import { Collections } from "../../../constants";
import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint";
import { Language } from "../../../types/collections";
export const importFromStrapi = createStrapiImportEndpoint<Language>({
type StrapiLanguage = {
name: string;
code: string;
};
export const importFromStrapi = createStrapiImportEndpoint<Language, StrapiLanguage>({
strapi: {
collection: "languages",
params: {},

View File

@ -1,3 +1,4 @@
import { RowLabelArgs } from "payload/dist/admin/components/forms/RowLabel/types";
import {
CollectionGroups,
Collections,
@ -18,7 +19,6 @@ import { LibraryItem } from "../../types/collections";
import { isDefined } from "../../utils/asserts";
import { buildVersionedCollectionConfig } from "../../utils/versionedCollectionConfig";
import { RowLabel } from "./components/RowLabel";
import { getBySlug } from "./endpoints/getBySlug";
const fields = {
status: "_status",
@ -139,7 +139,6 @@ export const LibraryItems = buildVersionedCollectionConfig({
},
preview: (doc) => `https://accords-library.com/library/${doc.slug}`,
},
endpoints: [getBySlug],
fields: [
{
name: fields.itemType,
@ -480,7 +479,7 @@ export const LibraryItems = buildVersionedCollectionConfig({
"Make sure the page number corresponds to the page number written on \
the scan. You can use negative page numbers if necessary.",
components: {
RowLabel: ({ data }) => RowLabel(data),
RowLabel: ({ data }: RowLabelArgs) => RowLabel(data),
},
},
fields: [

View File

@ -1,59 +0,0 @@
import cleanDeep from "clean-deep";
import { Collections } from "../../../constants";
import { createGetByEndpoint } from "../../../endpoints/createByEndpoint";
import { LibraryItem } from "../../../types/collections";
type ProcessedLibraryItem = Omit<LibraryItem, "size" | "price" | "scans" | "id"> & {
size?: Omit<LibraryItem["size"][number], "id">;
price?: Omit<LibraryItem["price"][number], "id" | "currency"> & { currency: string };
scans?: Omit<LibraryItem["scans"][number], "id" | "obi" | "cover" | "dustjacket"> & {
obi: Omit<LibraryItem["scans"][number]["obi"][number], "id">;
cover: Omit<LibraryItem["scans"][number]["obi"][number], "id">;
dustjacket: Omit<LibraryItem["scans"][number]["obi"][number], "id">;
};
};
export const getBySlug = createGetByEndpoint<LibraryItem, Partial<ProcessedLibraryItem>>(
Collections.LibraryItems,
"slug",
async ({ id, size, price, scans, ...otherProps }) => {
const processedLibraryItem: ProcessedLibraryItem = {
size: processOptionalGroup(size),
price: processPrice(price),
scans: processScans(scans),
...otherProps,
};
return cleanDeep(processedLibraryItem, {
emptyStrings: false,
emptyArrays: false,
emptyObjects: false,
nullValues: true,
undefinedValues: true,
NaNValues: false,
});
}
);
const processScans = (scans: LibraryItem["scans"]): ProcessedLibraryItem["scans"] => {
if (!scans || scans.length === 0) return undefined;
const { cover, dustjacket, id, obi, ...otherProps } = scans[0];
return {
cover: processOptionalGroup(cover),
dustjacket: processOptionalGroup(dustjacket),
obi: processOptionalGroup(obi),
...otherProps,
};
};
const processPrice = (price: LibraryItem["price"]): ProcessedLibraryItem["price"] => {
if (!price || price.length === 0) return undefined;
const { currency, ...otherProps } = processOptionalGroup(price);
return { ...otherProps, currency: typeof currency === "string" ? currency : currency.id };
};
const processOptionalGroup = <T extends { id?: string }>(group: T[] | null | undefined) => {
if (!group || group.length === 0) return undefined;
const { id, ...otherProps } = group[0];
return otherProps;
};

View File

@ -8,7 +8,6 @@ import { beforeDuplicatePiping } from "../../hooks/beforeDuplicatePiping";
import { beforeDuplicateUnpublish } from "../../hooks/beforeDuplicateUnpublish";
import { isDefined, isUndefined } from "../../utils/asserts";
import { buildVersionedCollectionConfig } from "../../utils/versionedCollectionConfig";
import { removeTranslatorsForTranscripts } from "./hooks/beforeValidate";
const fields = {
slug: "slug",
@ -57,9 +56,6 @@ export const Posts = buildVersionedCollectionConfig({
},
preview: (doc) => `https://accords-library.com/news/${doc.slug}`,
},
hooks: {
beforeValidate: [removeTranslatorsForTranscripts],
},
fields: [
{
type: "row",
@ -110,6 +106,15 @@ export const Posts = buildVersionedCollectionConfig({
type: "relationship",
relationTo: Collections.Recorders,
hasMany: true,
hooks: {
beforeChange: [
({ siblingData }) => {
if (siblingData.language === siblingData.sourceLanguage) {
delete siblingData.translators;
}
},
],
},
admin: {
condition: (_, siblingData) => {
if (

View File

@ -1,14 +0,0 @@
import { CollectionBeforeValidateHook } from "payload/types";
import { Post } from "../../../types/collections";
export const removeTranslatorsForTranscripts: CollectionBeforeValidateHook<Post> = async ({
data: { translations, ...data },
}) => ({
...data,
translations: translations?.map(({ translators, ...translation }) => {
if (translation.language === translation.sourceLanguage) {
return { ...translation, translators: [] };
}
return { ...translation, translators };
}),
});

View File

@ -1,5 +1,6 @@
import { mustBeAdmin as mustBeAdminForCollections } from "../../accesses/collections/mustBeAdmin";
import { mustBeAdminOrSelf } from "../../accesses/collections/mustBeAdminOrSelf";
import { mustBeAdmin } from "../../accesses/mustBeAdmin";
import { mustBeAdmin as mustBeAdminForFields } from "../../accesses/fields/mustBeAdmin";
import { QuickFilters } from "../../components/QuickFilters";
import { CollectionGroups, Collections, RecordersRoles } from "../../constants";
import { imageField } from "../../fields/imageField/imageField";
@ -55,7 +56,6 @@ export const Recorders = buildCollectionConfig({
label: "∅ Role",
filter: { where: { role: { not_in: Object.keys(RecordersRoles).join(",") } } },
},
,
],
[{ label: "Anonymized", filter: { where: { anonymize: { equals: true } } } }],
],
@ -65,10 +65,10 @@ export const Recorders = buildCollectionConfig({
},
auth: true,
access: {
unlock: mustBeAdmin,
unlock: mustBeAdminForCollections,
update: mustBeAdminOrSelf,
delete: mustBeAdmin,
create: mustBeAdmin,
delete: mustBeAdminForCollections,
create: mustBeAdminForCollections,
},
hooks: {
beforeLogin: [beforeLoginMustHaveAtLeastOneRole],
@ -111,14 +111,14 @@ export const Recorders = buildCollectionConfig({
description:
"A short personal description about you or your involvement with this project or the franchise",
},
fields: [{ name: fields.biography, type: "textarea" }],
fields: [{ name: fields.biography, required: true, type: "textarea" }],
}),
{
name: fields.role,
type: "select",
access: {
update: mustBeAdmin,
create: mustBeAdmin,
update: mustBeAdminForFields,
create: mustBeAdminForFields,
},
hasMany: true,
options: Object.entries(RecordersRoles).map(([value, label]) => ({

View File

@ -2,10 +2,21 @@ import payload from "payload";
import { Collections } from "../../../constants";
import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint";
import { Recorder } from "../../../types/collections";
import { PayloadCreateData } from "../../../types/payload";
import { StrapiImage, StrapiLanguage } from "../../../types/strapi";
import { isUndefined } from "../../../utils/asserts";
import { uploadStrapiImage } from "../../../utils/localApi";
import { PayloadCreateData } from "../../../utils/types";
export const importFromStrapi = createStrapiImportEndpoint<Recorder>({
type StrapiRecorder = {
username: string;
anonymize: boolean;
anonymous_code: number;
languages: { data: { attributes: { code: string } }[] };
avatar: StrapiImage;
bio: { language: StrapiLanguage; bio?: string }[];
};
export const importFromStrapi = createStrapiImportEndpoint<Recorder, StrapiRecorder>({
strapi: {
collection: "recorders",
params: {
@ -27,10 +38,15 @@ export const importFromStrapi = createStrapiImportEndpoint<Recorder>({
anonymize,
languages: languages.data?.map((language) => language.attributes.code),
avatar: avatarId,
biographies: bios?.map(({ language, bio }) => ({
biographies: bios?.map(({ language, bio }) => {
if (isUndefined(language.data))
throw new Error("A language is required for a Recorder biography");
if (isUndefined(bio)) throw new Error("A bio is required for a Recorder biography");
return {
language: language.data.attributes.code,
biography: bio,
})),
};
}),
};
await payload.create({ collection: Collections.Recorders, data, user });

View File

@ -1,5 +1,5 @@
import { CollectionConfig } from "payload/types";
import { mustBeAdmin } from "../../accesses/mustBeAdmin";
import { mustBeAdmin } from "../../accesses/collections/mustBeAdmin";
import { CollectionGroups, Collections, VideoSources } from "../../constants";
import { buildCollectionConfig } from "../../utils/collectionConfig";
import { importFromStrapi } from "./endpoints/importFromStrapi";

View File

@ -2,9 +2,26 @@ import payload from "payload";
import { Collections } from "../../../constants";
import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint";
import { Video, VideosChannel } from "../../../types/collections";
import { PayloadCreateData } from "../../../utils/types";
import { PayloadCreateData } from "../../../types/payload";
import { isUndefined } from "../../../utils/asserts";
export const importFromStrapi = createStrapiImportEndpoint<Video>({
type StapiVideo = {
uid: string;
title: string;
description: string;
published_date: {
year?: number;
month?: number;
day?: number;
};
views: number;
likes: number;
source?: "YouTube" | "NicoNico" | "Tumblr";
gone: boolean;
channel: { data?: { attributes: { uid: string; title: string; subscribers: number } } };
};
export const importFromStrapi = createStrapiImportEndpoint<Video, StapiVideo>({
strapi: {
collection: "videos",
params: { populate: "published_date,channel" },
@ -25,6 +42,9 @@ export const importFromStrapi = createStrapiImportEndpoint<Video>({
},
user
) => {
if (isUndefined(source)) throw new Error("A source is required to create a Video");
if (isUndefined(channel.data)) throw new Error("A channel is required to create a Video");
try {
const videoChannel: PayloadCreateData<VideosChannel> = {
uid: channel.data.attributes.uid,

View File

@ -1,5 +1,5 @@
import { CollectionConfig } from "payload/types";
import { mustBeAdmin } from "../../accesses/mustBeAdmin";
import { mustBeAdmin } from "../../accesses/collections/mustBeAdmin";
import { CollectionGroups, Collections } from "../../constants";
import { buildCollectionConfig } from "../../utils/collectionConfig";
import { importFromStrapi } from "./endpoints/importFromStrapi";

View File

@ -2,7 +2,13 @@ import { Collections } from "../../../constants";
import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint";
import { VideosChannel } from "../../../types/collections";
export const importFromStrapi = createStrapiImportEndpoint<VideosChannel>({
type StrapiVideoChannel = {
uid: string;
title: string;
subscribers: number;
};
export const importFromStrapi = createStrapiImportEndpoint<VideosChannel, StrapiVideoChannel>({
strapi: {
collection: "video-channels",
params: {},

View File

@ -1,3 +1,4 @@
import { RowLabelArgs } from "payload/dist/admin/components/forms/RowLabel/types";
import { CollectionGroups, Collections, KeysTypes } from "../../constants";
import { imageField } from "../../fields/imageField/imageField";
import { keysField } from "../../fields/keysField/keysField";
@ -78,7 +79,7 @@ export const Weapons = buildVersionedCollectionConfig({
admin: {
initCollapsed: true,
components: {
RowLabel: ({ data }) =>
RowLabel: ({ data }: RowLabelArgs) =>
AppearanceRowLabel({ keyIds: data[fields.appearancesCategories] ?? [] }),
},
},

View File

@ -2,11 +2,31 @@ import payload from "payload";
import { Collections } from "../../../constants";
import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint";
import { Weapon, WeaponsGroup } from "../../../types/collections";
import { isDefined } from "../../../utils/asserts";
import { PayloadCreateData } from "../../../types/payload";
import { StrapiImage, StrapiLanguage } from "../../../types/strapi";
import { isDefined, isUndefined } from "../../../utils/asserts";
import { findCategory, findWeaponType, uploadStrapiImage } from "../../../utils/localApi";
import { PayloadCreateData } from "../../../utils/types";
export const importFromStrapi = createStrapiImportEndpoint<Weapon>({
type StrapiWeapon = {
slug: string;
name: { name: string; language: StrapiLanguage }[];
weapon_group: { data?: { attributes: { slug: string } } };
thumbnail: StrapiImage;
type: { data?: { attributes: { slug: string } } };
stories: {
categories: { data: { attributes: { slug: string } }[] };
translations: {
language: StrapiLanguage;
description?: string;
level_1?: string;
level_2?: string;
level_3?: string;
level_4?: string;
}[];
}[];
};
export const importFromStrapi = createStrapiImportEndpoint<Weapon, StrapiWeapon>({
strapi: {
collection: "weapon-stories",
params: {
@ -23,7 +43,7 @@ export const importFromStrapi = createStrapiImportEndpoint<Weapon>({
payload: {
collection: Collections.Weapons,
import: async ({ slug, type, stories, name: names, weapon_group, thumbnail }, user) => {
let groupId: string;
let groupId: string | undefined;
if (isDefined(weapon_group.data)) {
try {
const groupData: PayloadCreateData<WeaponsGroup> = {
@ -51,6 +71,8 @@ export const importFromStrapi = createStrapiImportEndpoint<Weapon>({
image: thumbnail,
});
if (isUndefined(type.data)) throw new Error("A type is required to create a Weapon");
const data: PayloadCreateData<Weapon> = {
slug,
type: await findWeaponType(type.data.attributes.slug),
@ -62,19 +84,26 @@ export const importFromStrapi = createStrapiImportEndpoint<Weapon>({
categories.data.map(({ attributes }) => findCategory(attributes.slug))
),
translations: translations.map(
({ language, description, level_1, level_2, level_3, level_4 }) => ({
({ language, description, level_1, level_2, level_3, level_4 }) => {
if (isUndefined(language.data))
throw new Error("A language is required to create a Weapon translation");
const name = names.find(
(name) => name.language.data?.attributes.code === language.data?.attributes.code
)?.name;
if (isUndefined(name))
throw new Error("A name is required to create a Weapon translation");
return {
language: language.data?.attributes.code,
sourceLanguage: language.data?.attributes.code,
name: names.find(
(name) => name.language.data?.attributes.code === language.data?.attributes.code
)?.name,
name,
description,
level1: level_1,
level2: level_2,
level3: level_3,
level4: level_4,
transcribers: [user.id],
})
};
}
),
}))
),

View File

@ -1,11 +1,11 @@
import payload from "payload";
import { CollectionConfig } from "payload/types";
import { CollectionEndpoint } from "../types/payload";
export const createGetByEndpoint = <T, R>(
collection: string,
attribute: string,
handler: (doc: T) => Promise<R>
): CollectionConfig["endpoints"][number] => ({
): CollectionEndpoint => ({
path: `/${attribute}/:${attribute}`,
method: "get",
handler: async (req, res) => {

View File

@ -1,9 +1,8 @@
import payload from "payload";
import { CollectionConfig } from "payload/types";
import QueryString from "qs";
import { Recorder } from "../types/collections";
import { CollectionEndpoint, PayloadCreateData } from "../types/payload";
import { isDefined } from "../utils/asserts";
import { PayloadCreateData } from "../utils/types";
export const getAllStrapiEntries = async <T>(
collectionSlug: string,
@ -31,26 +30,26 @@ export const getAllStrapiEntries = async <T>(
return result;
};
type Params<T> = {
type Params<T, S> = {
strapi: {
collection: string;
params: any;
};
payload: {
collection: string;
import?: (strapiObject: any, user: any) => Promise<void>;
convert?: (strapiObject: any, user: any) => PayloadCreateData<T>;
import?: (strapiObject: S, user: any) => Promise<void>;
convert?: (strapiObject: S, user: any) => PayloadCreateData<T>;
};
};
export const importStrapiEntries = async <T>({
export const importStrapiEntries = async <T, S>({
strapi: strapiParams,
payload: payloadParams,
user,
}: Params<T> & { user: Recorder }) => {
}: Params<T, S> & { user: Recorder }) => {
const entries = await getAllStrapiEntries<any>(strapiParams.collection, strapiParams.params);
const errors = [];
const errors: string[] = [];
await Promise.all(
entries.map(async ({ attributes, id }) => {
@ -68,17 +67,17 @@ export const importStrapiEntries = async <T>({
}
} catch (e) {
console.warn(e);
if (typeof e === "object" && isDefined(e) && "name" in e) {
errors.push(`${e.name} with ${id}`);
}
}
})
);
return { count: entries.length, errors };
};
export const createStrapiImportEndpoint = <T>(
params: Params<T>
): CollectionConfig["endpoints"][number] => ({
export const createStrapiImportEndpoint = <T, S>(params: Params<T, S>): CollectionEndpoint => ({
method: "get",
path: "/strapi",
handler: async (req, res) => {

View File

@ -30,7 +30,7 @@ export const backPropagationField = ({
afterRead: [
...afterRead,
async ({ data }) => {
if (isNotEmpty(data.id)) {
if (isNotEmpty(data?.id)) {
const result = await payload.find({
collection: params.relationTo,
where: where(data),

View File

@ -19,5 +19,5 @@ export const keysField = ({
filterOptions: { type: { equals: getKeysTypesKey(relationTo) } },
});
const getKeysTypesKey = (keyType: KeysTypes): string =>
Object.entries(KeysTypes).find(([, value]) => value === keyType)[0];
const getKeysTypesKey = (keyType: KeysTypes): string | undefined =>
Object.entries(KeysTypes).find(([, value]) => value === keyType)?.[0];

View File

@ -1,3 +1,4 @@
import { RowLabelArgs } from "payload/dist/admin/components/forms/RowLabel/types";
import { array } from "payload/dist/fields/validations";
import { ArrayField, Field } from "payload/types";
import { Collections } from "../../constants";
@ -118,6 +119,8 @@ const creditFields: Field = {
],
};
type FieldData = Record<string, any> & { [fieldsNames.language]: string };
export const translatedFields = ({
fields,
validate,
@ -129,7 +132,7 @@ export const translatedFields = ({
admin: {
initCollapsed: true,
components: {
Cell: ({ cellData }) =>
Cell: ({ cellData }: { cellData: FieldData[] }) =>
Cell({
cellData:
cellData?.map((row) => ({
@ -137,7 +140,7 @@ export const translatedFields = ({
title: isDefined(useAsTitle) ? row[useAsTitle] : undefined,
})) ?? [],
}),
RowLabel: ({ data }) =>
RowLabel: ({ data }: RowLabelArgs) =>
RowLabel({
language: data[fieldsNames.language],
title: isDefined(useAsTitle) ? data[useAsTitle] : undefined,
@ -162,6 +165,8 @@ export const translatedFields = ({
if (hasDuplicates(languages)) {
return `There cannot be multiple ${otherProps.name} with the same ${fieldsNames.language}`;
}
return true;
},
fields: [
hasSourceLanguage

View File

@ -4,7 +4,8 @@ import path from "path";
import payload from "payload";
import { Collections, RecordersRoles } from "./constants";
import { Recorder } from "./types/collections";
import { PayloadCreateData } from "./utils/types";
import { PayloadCreateData } from "./types/payload";
import { isDefined, isUndefined } from "./utils/asserts";
const app = express();
@ -15,6 +16,15 @@ app.get("/", (_, res) => {
const start = async () => {
// Initialize Payload
if (isUndefined(process.env.PAYLOAD_SECRET)) {
throw new Error("Missing required env variable: PAYLOAD_SECRET");
}
if (isUndefined(process.env.MONGODB_URI)) {
throw new Error("Missing required env variable: MONGODB_URI");
}
await payload.init({
secret: process.env.PAYLOAD_SECRET,
mongoURL: process.env.MONGODB_URI,
@ -24,8 +34,14 @@ const start = async () => {
const recorders = await payload.find({ collection: Collections.Recorders });
// If no recorders, we seed some initial data
if (
isDefined(process.env.SEEDING_ADMIN_EMAIL) &&
isDefined(process.env.SEEDING_ADMIN_PASSWORD) &&
isDefined(process.env.SEEDING_ADMIN_USERNAME)
) {
if (recorders.docs.length === 0) {
payload.logger.info("Seeding some initial data");
const recorder: PayloadCreateData<Recorder> = {
email: process.env.SEEDING_ADMIN_EMAIL,
password: process.env.SEEDING_ADMIN_PASSWORD,
@ -38,6 +54,7 @@ const start = async () => {
data: recorder,
});
}
}
},
});

View File

@ -14,7 +14,7 @@ export type CategoryTranslations = {
}[];
export type RecorderBiographies = {
language: string | Language;
biography?: string;
biography: string;
id?: string;
}[];
export type ContentFoldersTranslation = {

8
src/types/payload.ts Normal file
View File

@ -0,0 +1,8 @@
import { CollectionConfig } from "payload/types";
export type PayloadCreateData<T> = Omit<
T,
"id" | "updatedAt" | "createdAt" | "sizes" | "updatedBy"
>;
export type CollectionEndpoint = NonNullable<CollectionConfig["endpoints"]>[number];

14
src/types/strapi.ts Normal file
View File

@ -0,0 +1,14 @@
export type StrapiLanguage = {
data?: { attributes: { code: string } };
};
export type StrapiImage = {
data?: {
attributes: {
url: string;
mime: string;
name: string;
size: number;
};
};
};

View File

@ -1,5 +1,6 @@
import payload from "payload";
import { Collections, KeysTypes } from "../constants";
import { StrapiImage } from "../types/strapi";
import { isDefined } from "./asserts";
export const findWeaponType = async (name: string): Promise<string> => {
@ -23,20 +24,10 @@ type UploadStrapiImage = {
collection: Collections;
};
type StrapiImage = {
data?: {
attributes: {
url: string;
mime: string;
name: string;
size: number;
};
};
};
export const uploadStrapiImage = async ({
collection,
image,
}: UploadStrapiImage): Promise<string> => {
}: UploadStrapiImage): Promise<string | undefined> => {
if (isDefined(image.data)) {
const url = `${process.env.STRAPI_URI}${image.data.attributes.url}`;

View File

@ -1,4 +1,5 @@
import { Block, BlockField } from "payload/types";
import { capitalize } from "./string";
const isDefined = <T>(value: T | null | undefined): value is T =>
value !== null && value !== undefined;
@ -26,11 +27,6 @@ export const generateBlocks = <T extends string>(blocksConfig: BlocksConfig<T>):
recursionFieldName in block;
const getInterfaceName = (parents: T[], currentBlockName: T): string => {
const capitalize = (text: string): string => {
if (text.length === 0) return text;
const [firstLetter, ...rest] = text;
return [firstLetter.toUpperCase(), ...rest].join("");
};
return [...parents, currentBlockName]
.map((blockName) => blocksConfig[blockName].block.slug)
.map(capitalize)

View File

@ -1,13 +1,15 @@
import tags from "language-tags";
import { isUndefined } from "./asserts";
export const shortenEllipsis = (text: string, length: number): string =>
text.length - 3 > length ? `${text.substring(0, length)}...` : text;
export const formatLanguageCode = (code: string): string =>
tags(code).valid() ? tags(code).language().descriptions()[0] : code;
tags(code).valid() ? tags(code).language()?.descriptions()[0] ?? code : code;
export const capitalize = (string: string): string => {
const [firstLetter, ...otherLetters] = string;
if (isUndefined(firstLetter)) return "";
return [firstLetter.toUpperCase(), ...otherLetters].join("");
};

View File

@ -1,4 +0,0 @@
export type PayloadCreateData<T> = Omit<
T,
"id" | "updatedAt" | "createdAt" | "sizes" | "updatedBy"
>;

View File

@ -1,18 +1,116 @@
{
"compilerOptions": {
"target": "ES2022",
"moduleResolution": "NodeNext",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"strict": false,
"esModuleInterop": true,
"skipLibCheck": true,
"outDir": "./dist",
"rootDir": "./src",
"jsx": "react",
/* Visit https://aka.ms/tsconfig to read more about this file */
/* Projects */
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
/* Language and Environment */
"target": "ES2022" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
"lib": [
"dom",
"dom.iterable",
"esnext"
] /* Specify a set of bundled library declaration files that describe the target runtime environment. */,
"jsx": "react" /* Specify what JSX code is generated. */,
// "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
/* Modules */
"module": "commonjs" /* Specify what module code is generated. */,
"rootDir": "./src" /* Specify the root folder within your source files. */,
// "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
"paths": {
"payload/generated-types": ["./src/payload-types.ts"]
}
} /* Specify a set of entries that re-map imports to additional lookup locations. */,
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
// "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
// "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
// "resolveJsonModule": true, /* Enable importing .json files. */
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
/* JavaScript Support */
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
/* Emit */
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
"outDir": "./dist" /* Specify an output folder for all emitted files. */,
// "removeComments": true, /* Disable emitting comments. */
// "noEmit": true, /* Disable emitting files from a compilation. */
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
// "newLine": "crlf", /* Set the newline character for emitting files. */
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
/* Interop Constraints */
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
// "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
/* Type Checking */
"strict": true /* Enable all strict type-checking options. */,
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
"alwaysStrict": true /* Ensure 'use strict' is always emitted. */,
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
"noFallthroughCasesInSwitch": true /* Enable error reporting for fallthrough cases in switch statements. */,
"noUncheckedIndexedAccess": true /* Add 'undefined' to a type when accessed using an index. */,
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
/* Completeness */
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
},
"include": ["src"],
"exclude": ["node_modules", "dist", "build"],