Compare commits

..

No commits in common. "master" and "a375cc3" have entirely different histories.

81 changed files with 12 additions and 711 deletions

7
.gitattributes vendored
View File

@ -1,7 +0,0 @@
*.webm filter=lfs diff=lfs merge=lfs -text
*.mp4 filter=lfs diff=lfs merge=lfs -text
*.gif filter=lfs diff=lfs merge=lfs -text
*.png filter=lfs diff=lfs merge=lfs -text
*.jpg filter=lfs diff=lfs merge=lfs -text
*.jpeg filter=lfs diff=lfs merge=lfs -text
*.webp filter=lfs diff=lfs merge=lfs -text

1
.gitignore vendored
View File

@ -1,2 +1 @@
/dist
/scripts/node_modules

View File

@ -13,7 +13,7 @@ Replaces the default bed textures with yiff. Supports 1.12+
* [lime](https://e621.net/posts/1976008)
* [green](https://e621.net/posts/2416598)
* [cyan](https://e621.net/posts/2364029)
* [light_blue](https://e621.net/posts/2194206)
* [light_blue](https://e621.net/posts/2397890)
* [blue](https://e621.net/posts/314664)
* [purple](https://e621.net/posts/1957635)
* [magenta](https://e621.net/posts/1933688)
@ -38,7 +38,7 @@ Replaces the default bed textures with yiff. Supports 1.12+
| Lime | ![Lime](examples/lime.png) | [Source](https://e621.net/posts/1976008) |
| Green | ![Green](examples/green.png) | [Source](https://e621.net/posts/2416598) |
| Cyan | ![Cyan](examples/cyan.png) | [Source](https://e621.net/posts/2364029) |
| Light Blue | ![Light Blue](examples/light_blue.png) | [Source](https://e621.net/posts/2194206) |
| Light Blue | ![Light Blue](examples/light_blue.png) | [Source](https://e621.net/posts/2397890) |
| Blue | ![Blue](examples/blue.png) | [Source](https://e621.net/posts/314664) |
| Purple | ![Purple](examples/purple.png) | [Source](https://e621.net/posts/1957635) |
| Magenta | ![Magenta](examples/magenta.png) | [Source](https://e621.net/posts/1933688) |

BIN
base.png (Stored with Git LFS)

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 721 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 738 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 808 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 676 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 514 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 885 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 510 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 803 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 798 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 865 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1012 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 728 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 784 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 737 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 651 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

BIN
common/pack.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 298 KiB

BIN
crop.png (Stored with Git LFS)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
data/pack.png (Stored with Git LFS)

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 131 B

After

Width:  |  Height:  |  Size: 782 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 131 B

After

Width:  |  Height:  |  Size: 431 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 131 B

After

Width:  |  Height:  |  Size: 429 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 131 B

After

Width:  |  Height:  |  Size: 433 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 131 B

After

Width:  |  Height:  |  Size: 409 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 131 B

After

Width:  |  Height:  |  Size: 364 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 131 B

After

Width:  |  Height:  |  Size: 504 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 131 B

After

Width:  |  Height:  |  Size: 358 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 131 B

After

Width:  |  Height:  |  Size: 401 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 131 B

After

Width:  |  Height:  |  Size: 462 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 131 B

After

Width:  |  Height:  |  Size: 449 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 131 B

After

Width:  |  Height:  |  Size: 538 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 131 B

After

Width:  |  Height:  |  Size: 439 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 131 B

After

Width:  |  Height:  |  Size: 422 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 131 B

After

Width:  |  Height:  |  Size: 424 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 131 B

After

Width:  |  Height:  |  Size: 421 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 131 B

After

Width:  |  Height:  |  Size: 524 KiB

BIN
images/black.png (Stored with Git LFS)

Binary file not shown.

BIN
images/blue.png (Stored with Git LFS)

Binary file not shown.

BIN
images/brown.jpg (Stored with Git LFS)

Binary file not shown.

BIN
images/cyan.png (Stored with Git LFS)

Binary file not shown.

BIN
images/gray.png (Stored with Git LFS)

Binary file not shown.

BIN
images/green.png (Stored with Git LFS)

Binary file not shown.

View File

@ -1,91 +0,0 @@
{
"defaultSize": [512, 512],
"details": {
"top": {
"pos": [10, 117],
"size": [204, 59],
"cut": [0, 0]
},
"middle": {
"pos": [10, 224],
"size": [204, 128],
"cut": [0, 59]
},
"bottom": {
"pos": [176, 186],
"size": [128, 38],
"cut": [38, 187],
"flip": "horizontal"
}
},
"images": [
{
"name": "black",
"sizeMultiplier": 4
},
{
"name": "blue",
"sizeMultiplier": 4,
"rotate": -90
},
{
"name": "brown",
"sizeMultiplier": 4
},
{
"name": "cyan",
"sizeMultiplier": 4
},
{
"name": "gray",
"sizeMultiplier": 4
},
{
"name": "green",
"sizeMultiplier": 4
},
{
"name": "light_blue",
"sizeMultiplier": 4
},
{
"name": "light_gray",
"sizeMultiplier": 4
},
{
"name": "lime",
"sizeMultiplier": 4
},
{
"name": "magenta",
"sizeMultiplier": 4,
"rotate": -90,
"flip": "horizontal"
},
{
"name": "orange",
"sizeMultiplier": 4,
"rotate": -90
},
{
"name": "pink",
"sizeMultiplier": 4
},
{
"name": "purple",
"sizeMultiplier": 4
},
{
"name": "red",
"sizeMultiplier": 4
},
{
"name": "white",
"sizeMultiplier": 4
},
{
"name": "yellow",
"sizeMultiplier": 4
}
]
}

BIN
images/light_blue.png (Stored with Git LFS)

Binary file not shown.

BIN
images/light_gray.jpg (Stored with Git LFS)

Binary file not shown.

BIN
images/lime.png (Stored with Git LFS)

Binary file not shown.

BIN
images/magenta.png (Stored with Git LFS)

Binary file not shown.

BIN
images/orange.jpg (Stored with Git LFS)

Binary file not shown.

BIN
images/pink.png (Stored with Git LFS)

Binary file not shown.

BIN
images/purple.png (Stored with Git LFS)

Binary file not shown.

BIN
images/red.jpg (Stored with Git LFS)

Binary file not shown.

View File

@ -1,29 +0,0 @@
{
"defaultSize": [512, 512],
"details": {
"top": {
"pos": [10, 117],
"size": [204, 59],
"cut": [0, 0]
},
"middle": {
"pos": [10, 224],
"size": [204, 128],
"cut": [0, 59]
},
"bottom": {
"pos": [176, 186],
"size": [128, 38],
"cut": [38, 187],
"flip": "horizontal"
}
},
"images": [
{
"name": "animated"
},
{
"name": "orange"
}
]
}

BIN
images/white.png (Stored with Git LFS)

Binary file not shown.

BIN
images/yellow.png (Stored with Git LFS)

Binary file not shown.

View File

@ -1,22 +1,21 @@
{
"name": "Yiffy Beds",
"name": "yiffy Beds",
"description": "Replaces bed textures with furry porn.",
"url": "https://git.furry.cool/MCFurryPacks/YiffyBeds",
"versions": [
["1.12", 3],
["1.13", 4],
["1.15", 5],
["1.16", 6],
["1.17", 7],
["1.18", 8],
["1.19", 9],
["1.12", 3],
["1.13", 4],
["1.15", 5],
["1.16", 6],
["1.17", 7],
["1.18", 8],
["1.19", 9],
["1.19.3", 12],
["1.19.4", 13],
["1.20", 15],
["1.20", 15],
["1.20.2", 18],
["1.20.3", 26],
["1.20.5", 32],
["1.21", 34]
["1.20.5", 32]
],
"exports": []
}

View File

@ -1,8 +0,0 @@
{
"extends": ["@uwu-codes/eslint-config/esm"],
"rules": {
"unicorn/prevent-abbreviations": "off",
"unicorn/no-process-exit": "off",
"unicorn/import-style": "off"
}
}

Binary file not shown.

View File

@ -1,292 +0,0 @@
import { randomBytes } from "crypto";
import { fileTypeFromBuffer } from "file-type";
import Ffmpeg from "fluent-ffmpeg";
import { rm } from "fs/promises";
import { readdir } from "fs/promises";
import { mkdtemp } from "fs/promises";
import { tmpdir } from "os";
import { basename } from "path";
import sharp, { type OverlayOptions, type Sharp } from "sharp";
import type { Config, Detail, Image } from "./images";
async function makeComposite(image: string | Buffer, details: Detail, sizeMultiplier: number, offset = 0) {
let img = await sharp(image)
.extract({
top: details.cut[1] * sizeMultiplier,
left: details.cut[0] * sizeMultiplier,
width: details.size[0] * sizeMultiplier,
height: details.size[1] * sizeMultiplier
}).toBuffer();
if (details.rotate) {
img = await sharp(img).rotate(details.rotate).toBuffer();
}
if (details.flip) {
img = await sharp(img).flip(details.flip === "horizontal").flop(details.flip === "vertical").toBuffer();
}
return {
input: img,
top: (details.pos[1] * sizeMultiplier) + offset,
left: details.pos[0] * sizeMultiplier
}
}
async function applyOptions(image: string | Buffer, options: Image, width: number, height: number) {
let img = await sharp(image).toBuffer()
if (options.rotate) {
img = await sharp(img).rotate(options.rotate).toBuffer();
}
if (options.flip) {
img = await sharp(img).flip(options.flip === "horizontal").flop(options.flip === "vertical").toBuffer();
}
img = await sharp(img).resize(width, height, { fit: "fill" }).toBuffer();
return img;
}
export async function formatImage(name: string, baseFile: string, config: Config, image: string | string[]) {
const options = config.images.find(img => img.name === name)!;
const isImage = !Array.isArray(image) && ["image/png", "image/jpeg"].includes((await fileTypeFromBuffer(Buffer.isBuffer(image) ? image : await Bun.file(image).arrayBuffer()))!.mime);
let result: Sharp;
const multplier = options.sizeMultiplier ?? 1;
const cropWidth = Math.max(config.details.top.size[0], config.details.middle.size[0], config.details.bottom.size[0]) * multplier,
cropHeight = (config.details.top.size[1] + config.details.middle.size[1] + config.details.bottom.size[1]) * multplier,
fullWidth = Math.floor(config.defaultSize[0] * multplier),
fullHeight = Math.floor(config.defaultSize[1] * multplier);
const base = sharp(baseFile);
if (multplier !== 1) {
base.resize(fullWidth, fullHeight, {
kernel: sharp.kernel.nearest
});
}
const baseBuf = await base.toBuffer();
if (isImage) {
const img = await applyOptions(image, options, cropWidth, cropHeight);
result = await sharp(baseBuf)
.composite([
await makeComposite(img, config.details.top, multplier),
await makeComposite(img, config.details.middle, multplier),
await makeComposite(img, config.details.bottom, multplier)
]);
} else {
let tmpDir: string | undefined;
let files: string[] = [];
tmpDir = await mkdtemp(`${tmpdir()}/split-frames-`);
if (!Array.isArray(image)) {
let select = "";
if (Array.isArray(options.frames)) {
select = options.frames.map(f => `eq(n\\,${f})`).join("+");
} else if (options.frames) {
select = `gte(n\\, ${options.frames.start})${options.frames.end ? `*lte(n\\,${options.frames.end}` : ""})`;
} else {
select = "n";
}
console.debug("Input file %s is not an image, assuming we need to extract frames.", name);
await new Promise<void>((resolve) => {
Ffmpeg(image)
.videoFilter(`scale=${cropWidth}:${cropHeight},select='${select}'`)
.addOption("-vsync vfr")
.output(`${tmpDir}/frame%04d.png`)
.on("end", async() => {
files = (await readdir(tmpDir!)).filter(f => /frame\d+\.png/.test(f)).sort().map(f => `${tmpDir}/${f}`);
if (files.length === 0) {
console.error("No frames extracted for %s.", name);
if (tmpDir) {
await rm(tmpDir, { recursive: true });
}
process.exit(1);
}
console.log("Frames extracted to %s, %d total for %s", tmpDir, files.length, name);
resolve();
})
.on("error", async(err) => {
if (tmpDir) {
await rm(tmpDir, { recursive: true });
}
console.error("Failed to extract frames for %s.", name);
console.error(err);
process.exit(1);
})
.run()
});
} else {
for (const img of image) {
await sharp(await applyOptions(img, options, cropWidth, cropHeight)).toFile(`${tmpDir}/${basename(img)}`);
}
files = image.map(f => `${tmpDir}/${basename(f)}`).sort();
}
const parts: OverlayOptions[] = [];
for (const img of files) {
const i = files.indexOf(img);
console.log("Processing %s (%d/%d) for %s", img, i + 1, files.length, name);
parts.push(
{ input: baseBuf, top: fullHeight * i, left: 0 },
await makeComposite(img, config.details.top, multplier, fullHeight * i),
await makeComposite(img, config.details.middle, multplier, fullHeight * i),
await makeComposite(img, config.details.bottom, multplier, fullHeight * i)
);
}
result = await sharp({
create: {
width: fullWidth,
height: fullHeight * files.length,
channels: 4,
background: { r: 0, g: 0, b: 0, alpha: 0 }
},
limitInputPixels: false
})
.composite(parts)
if (tmpDir) {
await rm(tmpDir, { recursive: true });
}
}
return { isImage, result: result! };
}
const frameSizePercent = 0.1;
async function createFrame(colors: number[], percent: number, width: number, height: number) {
let frameW = width, frameH = height, i = 0;
const parts: OverlayOptions[] = [];
const frameVH = Math.floor(Math.min(width, height) * percent);
while (frameW > 0) {
if (i > colors.length - 1) {
i = 0;
}
const color = colors[i], r = (color >> 16) & 0xFF, g = (color >> 8) & 0xFF, b = color & 0xFF,
w = width * frameSizePercent, h = frameVH;
parts.push({
input: {
create: {
width: w,
height: h,
channels: 4,
background: { r, g, b, alpha: 255 }
}
},
top: 0,
// left to right
left: i * w
},
{
input: {
create: {
width: w,
height: h,
channels: 4,
background: { r, g, b, alpha: 255 }
}
},
top: frameH - h,
// right to left
left: width - (i + 1) * w
});
frameW -= w;
i += 1;
}
i = 0;
colors = colors.toReversed();
while (frameH > 0) {
if (i > colors.length - 1) {
i = 0;
}
const color = colors[i], r = (color >> 16) & 0xFF, g = (color >> 8) & 0xFF, b = color & 0xFF,
w = frameVH, h = height * frameSizePercent;
parts.push({
input: {
create: {
width: w,
height: h,
channels: 4,
background: { r, g, b, alpha: 255 }
}
},
top: i * h,
// top to bottom
left: 0
},
{
input: {
create: {
width: w,
height: h,
channels: 4,
background: { r, g, b, alpha: 255 }
}
},
top: height - (i + 1) * h,
// bottom to top
left: width - w
});
frameH -= h;
i += 1;
}
return sharp({
create: {
width,
height,
channels: 4,
background: { r: 0, g: 0, b: 0, alpha: 0 }
},
limitInputPixels: false
}).composite(parts).png().toBuffer();
}
export const rgb = (hex: number) => ({ r: (hex >> 16) & 0xFF, g: (hex >> 8) & 0xFF, b: hex & 0xFF });
const kzBorderColor = rgb(0x6B3F7F)
const kzFillColor = rgb(0xD67FFF);
const plankSize = 4;
// the things I do to not have to make this a file
const plank = Buffer.from("iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAB3RJTUUH6AUfFCAKmCc95gAAATJJREFUKM9lUsFqwzAMlSXFXRYIYQRy6GX/tq/Zh/ZQGKUEOi9WpOyg1jWpL1Gen+X3/BS+vz6TCDxW2zQAoGZZ1et6JRF0UiSab1skUrMkklV9O4k4omZ+hv2TVfsuOC8SAQAh+paazbdtGu6/fLos9wrD4+a1aFht8+J0WZzAx4/Dq4ck0jZNEgEI820b+7sQQmRnF1TNXIyaubaxB5cKAKDK3rIdntTiyhtNQ2wrnM/XXGvd+Vlt25nkaYg19XzNfRdKGkV6yYFrxwAwDVHNPDhHXFWhcds0u05PiwCRaOyfD5BV+XzNq22urzjZZVLjew9VAlDdwz/z6kz0UfG3KsGVAStK+i74RDEhtojQiXetJ5QQk0gkotccAIBCWNQAlgPhosaYH1HI75+9v6F7+AdOcdbDy169/gAAAABJRU5ErkJggg==", "base64");
export async function makeKZ(squareSize: number, gridSize = 16) {
const kzBorderSize = squareSize * 0.0625;
const kzPlank = await sharp(plank).resize(squareSize, squareSize).toBuffer();
const plankStart = gridSize - plankSize;
const kzParts: OverlayOptions[] = [];
// i = top to bottom
// j = left to right
for (let i = 0; i < gridSize; i++) {
for (let j = 0; j < gridSize; j++) {
if (i < plankSize && j >= plankStart) {
kzParts.push({
input: kzPlank,
top: squareSize * i,
left: squareSize * j
});
} else {
kzParts.push({
input: {
create: {
width: kzBorderSize,
height: squareSize,
channels: 3,
background: kzBorderColor
}
},
top: squareSize * i,
left: squareSize * j
},{
input: {
create: {
width: squareSize,
height: kzBorderSize,
channels: 3,
background: kzBorderColor
}
},
top: squareSize * i,
left: squareSize * j
});
}
}
}
return sharp({
create: {
width: squareSize * 16,
height: squareSize * 16,
channels: 3,
background: kzFillColor
},
limitInputPixels: false
}).composite(kzParts).png().toBuffer();
}

View File

@ -1,117 +0,0 @@
import type { PathLike } from "fs";
import { access } from "fs/promises";
import { dirname, resolve } from "path";
import sharp, { type OverlayOptions } from "sharp";
import { parseArgs } from "util";
import { formatImage, makeKZ } from "./common";
import { readFile } from "fs/promises";
import { writeFile } from "fs/promises";
import { rm } from "fs/promises";
import { mkdir } from "fs/promises";
import { stat } from "fs/promises";
import { readdir } from "fs/promises";
const { values: args } = parseArgs({
args: Bun.argv,
options: {
images: {
type: "string",
short: "i",
default: "images/images.json"
},
imagedir: {
type: "string",
short: "d",
default: "images"
},
outdir: {
type: "string",
short: "o",
default: "data"
},
basefile: {
type: "string",
short: "b",
default: "base.png"
},
throw: {
type: "boolean",
short: "t",
default: false
}
},
strict: true,
allowPositionals: true
});
const dirExists = async(path: PathLike) => access(path).then(() => true, () => false);
const imagesPath = resolve(args.images ?? "images.json");
const imageDir = resolve(args.imagedir ?? dirname(imagesPath));
const baseFile = resolve(args.basefile ?? "base.png");
const outDir = resolve(args.outdir ?? "data");
const throwOnMissing = !!args.throw;
if (!await dirExists(imageDir)) {
console.error("Image directory %s does not exist.", imageDir);
process.exit(1);
}
const ap = (p: string) => resolve(p, "assets/minecraft/textures/entity/bed");
// const ap = (p: string) => resolve(p, "../img");
await rm(`${outDir}/assets`, { recursive: true, force: true });
await mkdir(ap(outDir), { recursive: true });
export interface Image {
animation?: { frametime?: number; };
frames?: { start: number; end?: number; } | Array<number>;
name: string;
sizeMultiplier?: number;
rotate?: number;
flip?: "horizontal" | "vertical";
}
export interface Detail {
pos: [number, number];
size: [number, number];
cut: [number, number];
rotate?: number;
flip?: "horizontal" | "vertical";
}
export interface Details {
top: Detail;
middle: Detail;
bottom: Detail;
}
export interface Config {
defaultSize: [width: number, height: number];
details: Details;
images: Array<Image>;
}
const config = await Bun.file(imagesPath).json() as Config;
for (const image of config.images) {
let img: string | Array<string>;
if (await stat(`${imageDir}/${image.name}`).then(s => s.isDirectory(), () => false)) {
img = await readdir(`${imageDir}/${image.name}`).then(files => files.map(f => `${imageDir}/${image.name}/${f}`).sort());
} else {
const [file] = await Array.fromAsync(new Bun.Glob(`${image.name}.*`).scan({ onlyFiles: true, cwd: imageDir }));
if (!file) {
console.error("Image %s does not exist.", image.name);
if (throwOnMissing) {
process.exit(1);
}
continue;
}
img = `${imageDir}/${file}`
}
console.log("Processing %s", image.name);
const { isImage, result } = await formatImage(image.name, baseFile, config, img);
await result.toFile(`${ap(outDir)}/${image.name}.png`);
if (!isImage) {
await writeFile(`${ap(outDir)}/${image.name}.png.mcmeta`, JSON.stringify({ animation: image.animation ?? {} }));
}
}

View File

@ -1,26 +0,0 @@
{
"name": "image-maker",
"module": "index.ts",
"type": "module",
"devDependencies": {
"@types/bun": "latest",
"@types/fluent-ffmpeg": "^2.1.24",
"@types/node": "^20.12.13",
"@uwu-codes/eslint-config": "^1.1.28",
"eslint": "^9.3.0"
},
"peerDependencies": {
"typescript": "^5.0.0"
},
"dependencies": {
"file-type": "^19.0.0",
"fluent-ffmpeg": "^2.1.3",
"sharp": "^0.33.4"
},
"scripts": {
"compile": "bun build ./images.ts --compile --outfile bin/make-images"
},
"bin": {
"make-images": "./images.ts"
}
}

View File

@ -1,22 +0,0 @@
{
"compilerOptions": {
"lib": ["ESNext"],
"target": "ESNext",
"module": "ESNext",
"moduleDetection": "force",
"jsx": "react-jsx",
"allowJs": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"noEmit": true,
/* Linting */
"skipLibCheck": true,
"strict": true,
"noFallthroughCasesInSwitch": true,
"forceConsistentCasingInFileNames": true
}
}