generated from tpl/obsidian-sample-plugin
Add redownload cover command and move image functions to services
This commit is contained in:
parent
210ba33297
commit
2f89a703d3
|
|
@ -0,0 +1,34 @@
|
|||
import type { BookTrackerPluginSettings } from "@ui/settings";
|
||||
import { Notice, type Editor, type MarkdownFileInfo, type MarkdownView, type MetadataCache } from "obsidian";
|
||||
import { CoverImageDownloaderService } from "../services/CoverImageDownloaderService";
|
||||
import { EditorCheckCommand } from "./Command";
|
||||
|
||||
export class RedownloadCoverCommand extends EditorCheckCommand {
|
||||
constructor(
|
||||
private readonly metadataCache: MetadataCache,
|
||||
private readonly settings: BookTrackerPluginSettings,
|
||||
private readonly downloader: CoverImageDownloaderService,
|
||||
) {
|
||||
super("redownload-cover", "Redownload Cover from Current Image URL");
|
||||
}
|
||||
|
||||
protected check(_editor: Editor, ctx: MarkdownView | MarkdownFileInfo): boolean {
|
||||
return ctx.file != null;
|
||||
}
|
||||
|
||||
protected async run(_editor: Editor, ctx: MarkdownView | MarkdownFileInfo): Promise<void> {
|
||||
const file = ctx.file!;
|
||||
const fm = this.metadataCache.getFileCache(file)?.frontmatter!;
|
||||
const url = fm[this.settings.coverImageUrlProperty];
|
||||
|
||||
try {
|
||||
await this.downloader.download(url, file.basename, true);
|
||||
} catch (error) {
|
||||
console.error("Failed to download cover image:", error);
|
||||
new Notice("Failed to download cover image. Check console for details.");
|
||||
return;
|
||||
}
|
||||
|
||||
new Notice("Fetched newest cover image.");
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +1,13 @@
|
|||
import { type Editor, type MarkdownView, type MarkdownFileInfo, type App, type TFile, Notice } from "obsidian";
|
||||
import { EditorCheckCommand } from "./Command";
|
||||
import type { CoverImageDownloaderService } from "@services/CoverImageDownloaderService";
|
||||
import type { BookTrackerPluginSettings } from "@ui/settings";
|
||||
import { Notice, type Editor, type FileManager, type MarkdownFileInfo, type MarkdownView, type TFile } from "obsidian";
|
||||
import { EditorCheckCommand } from "./Command";
|
||||
|
||||
export class UpdateCoverFromURLCommand extends EditorCheckCommand {
|
||||
constructor(
|
||||
private readonly app: App,
|
||||
private readonly fileManager: FileManager,
|
||||
private readonly settings: BookTrackerPluginSettings,
|
||||
private readonly downloadCoverImage: (url: string, fileName: string, overwrite?: boolean) => Promise<TFile>,
|
||||
private readonly downloader: CoverImageDownloaderService,
|
||||
) {
|
||||
super("update-cover-from-url", "Update Cover from URL");
|
||||
}
|
||||
|
|
@ -21,14 +22,14 @@ export class UpdateCoverFromURLCommand extends EditorCheckCommand {
|
|||
|
||||
let coverFile: TFile;
|
||||
try {
|
||||
coverFile = await this.downloadCoverImage(url, file.basename, true);
|
||||
coverFile = await this.downloader.download(url, file.basename, true);
|
||||
} catch (error) {
|
||||
console.error("Failed to download cover image:", error);
|
||||
new Notice("Failed to download cover image. Check console for details.");
|
||||
return;
|
||||
}
|
||||
|
||||
this.app.fileManager.processFrontMatter(file, (fm) => {
|
||||
this.fileManager.processFrontMatter(file, (fm) => {
|
||||
fm[this.settings.coverImageUrlProperty] = url;
|
||||
});
|
||||
new Notice("Updated cover image.")
|
||||
|
|
|
|||
|
|
@ -130,7 +130,34 @@ export interface SearchResult {
|
|||
export class Goodreads {
|
||||
async getNextData(legacyId: number): Promise<NextData> {
|
||||
const url = "https://www.goodreads.com/book/show/" + legacyId;
|
||||
const res = await requestUrl({ url });
|
||||
const res = await requestUrl({
|
||||
url,
|
||||
headers: {
|
||||
"Accept": "text/html, application/json",
|
||||
"Accept-Language": "en-US,en;q=0.9",
|
||||
"Content-Type": "application/json",
|
||||
"Device-Memory": "8",
|
||||
"Downlink": "10",
|
||||
"Dpr": "2",
|
||||
"Ect": "4g",
|
||||
"Origin": "https://www.amazon.com",
|
||||
"Priority": "u=1, i",
|
||||
"Rtt": "50",
|
||||
"Sec-Ch-Device-Memory": "8",
|
||||
"Sec-Ch-Dpr": "2",
|
||||
"Sec-Ch-Ua": "\"Google Chrome\";v=\"131\", \"Chromium\";v=\"131\", \"Not_A Brand\";v=\"24\"",
|
||||
"Sec-Ch-Ua-Mobile": "?0",
|
||||
"Sec-Ch-Ua-Platform": "\"macOS\"",
|
||||
"Sec-Ch-Viewport-Width": "1170",
|
||||
"Sec-Fetch-Dest": "empty",
|
||||
"Sec-Fetch-Mode": "cors",
|
||||
"Sec-Fetch-Site": "same-origin",
|
||||
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
|
||||
"Viewport-Width": "1170",
|
||||
"X-Amz-Amabot-Click-Attributes": "disable",
|
||||
"X-Requested-With": "XMLHttpRequest",
|
||||
},
|
||||
});
|
||||
const doc = new DOMParser().parseFromString(res.text, "text/html");
|
||||
const nextDataRaw = doc.getElementById("__NEXT_DATA__")?.textContent;
|
||||
if (typeof nextDataRaw !== "string") {
|
||||
|
|
|
|||
132
src/main.ts
132
src/main.ts
|
|
@ -1,41 +1,39 @@
|
|||
import {
|
||||
Notice,
|
||||
Plugin,
|
||||
requestUrl,
|
||||
TFile,
|
||||
type FrontMatterCache,
|
||||
} from "obsidian";
|
||||
import {
|
||||
type BookTrackerPluginSettings,
|
||||
DEFAULT_SETTINGS,
|
||||
BookTrackerSettingTab,
|
||||
} from "@ui/settings";
|
||||
import { safeString, Templater } from "@utils/Templater";
|
||||
import { CONTENT_TYPE_EXTENSIONS } from "./const";
|
||||
import { Storage } from "@utils/Storage";
|
||||
import { ReadingLog } from "@utils/ReadingLog";
|
||||
import { CreateBookFromGoodreadsUrlCommand } from "@commands/CreateBookFromGoodreadsUrlCommand";
|
||||
import { BackupReadingLogCommand } from "@commands/CreateReadingLogBackupCommand";
|
||||
import { LogReadingFinishedCommand } from "@commands/LogReadingFinishedCommand";
|
||||
import { LogReadingProgressCommand } from "@commands/LogReadingProgressCommand";
|
||||
import { LogReadingStartedCommand } from "@commands/LogReadingStartedCommand";
|
||||
import { RedownloadCoverCommand } from "@commands/RedownloadCoverCommand";
|
||||
import { ReloadReadingLogCommand } from "@commands/ReloadReadingLogCommand";
|
||||
import { ResetReadingStatusCommand } from "@commands/ResetReadingStatusCommand";
|
||||
import { RestoreReadingLogBackupCommand } from "@commands/RestoreReadingLogBackupCommand";
|
||||
import { SearchGoodreadsCommand } from "@commands/SearchGoodreadsCommand";
|
||||
import { UpdateCoverFromURLCommand } from "@commands/UpdateCoverFromURLCommand";
|
||||
import { Goodreads } from "@data-sources/Goodreads";
|
||||
import moment from "@external/moment";
|
||||
import {
|
||||
registerReadingLogCodeBlockProcessor,
|
||||
registerReadingStatsCodeBlockProcessor,
|
||||
} from "@ui/code-blocks";
|
||||
import type { Book, BookMetadata, ReadingState } from "./types";
|
||||
import { SearchGoodreadsCommand } from "@commands/SearchGoodreadsCommand";
|
||||
import { LogReadingStartedCommand } from "@commands/LogReadingStartedCommand";
|
||||
import { LogReadingProgressCommand } from "@commands/LogReadingProgressCommand";
|
||||
import { LogReadingFinishedCommand } from "@commands/LogReadingFinishedCommand";
|
||||
import { ResetReadingStatusCommand } from "@commands/ResetReadingStatusCommand";
|
||||
import { BackupReadingLogCommand } from "@commands/CreateReadingLogBackupCommand";
|
||||
import { RestoreReadingLogBackupCommand } from "@commands/RestoreReadingLogBackupCommand";
|
||||
import { Goodreads } from "@data-sources/Goodreads";
|
||||
import { CreateBookFromGoodreadsUrlCommand } from "@commands/CreateBookFromGoodreadsUrlCommand";
|
||||
import { registerShelfCodeBlockProcessor } from "@ui/code-blocks/ShelfCodeBlock";
|
||||
import { ReloadReadingLogCommand } from "@commands/ReloadReadingLogCommand";
|
||||
import { registerReadingCalendarCodeBlockProcessor } from "@ui/code-blocks/ReadingCalendarCodeBlock";
|
||||
import { registerAToZChallengeCodeBlockProcessor } from "@ui/code-blocks/AToZChallengeCodeBlock";
|
||||
import moment from "@external/moment";
|
||||
import { compressImage } from "@utils/image";
|
||||
import { titleSortValue } from "@utils/text";
|
||||
import { UpdateCoverFromURLCommand } from "@commands/UpdateCoverFromURLCommand";
|
||||
import { registerReadingCalendarCodeBlockProcessor } from "@ui/code-blocks/ReadingCalendarCodeBlock";
|
||||
import { registerShelfCodeBlockProcessor } from "@ui/code-blocks/ShelfCodeBlock";
|
||||
import {
|
||||
BookTrackerSettingTab,
|
||||
DEFAULT_SETTINGS,
|
||||
type BookTrackerPluginSettings,
|
||||
} from "@ui/settings";
|
||||
import { ReadingLog } from "@utils/ReadingLog";
|
||||
import { Storage } from "@utils/Storage";
|
||||
import { Templater, safeString } from "@utils/Templater";
|
||||
import {
|
||||
Plugin,
|
||||
TFile,
|
||||
type FrontMatterCache,
|
||||
} from "obsidian";
|
||||
import { CoverImageDownloaderService } from "./services/CoverImageDownloaderService";
|
||||
import { ImageCompressorService } from "./services/ImageCompressorService";
|
||||
import type { Book, BookMetadata, ReadingState } from "./types";
|
||||
|
||||
export default class BookTrackerPlugin extends Plugin {
|
||||
public settings: BookTrackerPluginSettings;
|
||||
|
|
@ -43,6 +41,8 @@ export default class BookTrackerPlugin extends Plugin {
|
|||
public storage: Storage;
|
||||
public readingLog: ReadingLog;
|
||||
public goodreads: Goodreads = new Goodreads();
|
||||
public imageCompressor: ImageCompressorService;
|
||||
public coverImageDownloader: CoverImageDownloaderService;
|
||||
|
||||
private onSettingsSavedHandlers: ((
|
||||
settings: BookTrackerPluginSettings
|
||||
|
|
@ -58,6 +58,8 @@ export default class BookTrackerPlugin extends Plugin {
|
|||
this.templater = new Templater(this.app);
|
||||
this.storage = new Storage(this);
|
||||
this.readingLog = new ReadingLog(this.storage);
|
||||
this.imageCompressor = new ImageCompressorService(this.app.vault);
|
||||
this.coverImageDownloader = new CoverImageDownloaderService(this.app.vault, this.app.fileManager, this.imageCompressor, this.settings);
|
||||
|
||||
this.addCommand(
|
||||
new SearchGoodreadsCommand(
|
||||
|
|
@ -91,7 +93,8 @@ export default class BookTrackerPlugin extends Plugin {
|
|||
)
|
||||
);
|
||||
this.addCommand(new ReloadReadingLogCommand(this.readingLog));
|
||||
this.addCommand(new UpdateCoverFromURLCommand(this.app, this.settings, this.downloadCoverImage.bind(this)));
|
||||
this.addCommand(new UpdateCoverFromURLCommand(this.app.fileManager, this.settings, this.coverImageDownloader));
|
||||
this.addCommand(new RedownloadCoverCommand(this.app.metadataCache, this.settings, this.coverImageDownloader));
|
||||
|
||||
this.addSettingTab(new BookTrackerSettingTab(this));
|
||||
|
||||
|
|
@ -145,65 +148,6 @@ export default class BookTrackerPlugin extends Plugin {
|
|||
};
|
||||
}
|
||||
|
||||
async downloadCoverImage(
|
||||
coverImageUrl: string,
|
||||
fileName: string,
|
||||
overwrite?: boolean
|
||||
): Promise<TFile> {
|
||||
const response = await requestUrl(coverImageUrl);
|
||||
const header = Object.keys(response.headers).find(k => k.toLowerCase() === "content-type") ?? "Content-Type";
|
||||
const contentType = response.headers[header] ?? "application/octet-stream";
|
||||
|
||||
if (!contentType.startsWith("image/") && contentType !== "application/octet-stream") {
|
||||
throw new Error("Unexpected content type: " + contentType);
|
||||
}
|
||||
|
||||
const urlExtension = coverImageUrl.split(".").pop();
|
||||
const extension =
|
||||
CONTENT_TYPE_EXTENSIONS[contentType || ""] ?? urlExtension ?? "jpg";
|
||||
|
||||
let filePath = this.settings.coverFolder + "/";
|
||||
if (this.settings.groupCoversByFirstLetter) {
|
||||
let groupName = titleSortValue(fileName).charAt(0).toUpperCase();
|
||||
if (!/^[A-Z]$/.test(groupName)) {
|
||||
groupName = "#";
|
||||
}
|
||||
|
||||
filePath += groupName + "/";
|
||||
}
|
||||
filePath += fileName + "." + extension;
|
||||
|
||||
let file = this.app.vault.getFileByPath(filePath);
|
||||
if (file) {
|
||||
if (this.settings.overwriteExistingCovers || overwrite) {
|
||||
await this.app.vault.modifyBinary(file, response.arrayBuffer);
|
||||
} else {
|
||||
new Notice("Cover image already exists: " + filePath);
|
||||
return file;
|
||||
}
|
||||
} else {
|
||||
file = await this.app.vault.createBinary(
|
||||
filePath,
|
||||
response.arrayBuffer
|
||||
);
|
||||
}
|
||||
|
||||
await compressImage(this.app, file, {
|
||||
height: 400,
|
||||
quality: 0.8,
|
||||
maintainAspectRatio: true,
|
||||
});
|
||||
|
||||
if (file.extension !== "jpg") {
|
||||
await this.app.fileManager.renameFile(
|
||||
file,
|
||||
file.path.replace(/\.[^.]+$/, ".jpg")
|
||||
);
|
||||
}
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
async createEntry(book: Book): Promise<void> {
|
||||
const fileName = this.templater
|
||||
.renderTemplate(this.settings.fileNameFormat, {
|
||||
|
|
@ -239,7 +183,7 @@ export default class BookTrackerPlugin extends Plugin {
|
|||
};
|
||||
|
||||
if (this.settings.downloadCovers && book.coverImageUrl) {
|
||||
const coverImageFile = await this.downloadCoverImage(
|
||||
const coverImageFile = await this.coverImageDownloader.download(
|
||||
book.coverImageUrl,
|
||||
fileName
|
||||
);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,74 @@
|
|||
import { CONTENT_TYPE_EXTENSIONS } from "@src/const";
|
||||
import type { BookTrackerPluginSettings } from "@ui/settings";
|
||||
import { titleSortValue } from "@utils/text";
|
||||
import { Notice, requestUrl, type FileManager, type TFile, type Vault } from "obsidian";
|
||||
import type { ImageCompressorService } from "./ImageCompressorService";
|
||||
|
||||
export class CoverImageDownloaderService {
|
||||
constructor(
|
||||
private readonly vault: Vault,
|
||||
private readonly fileManager: FileManager,
|
||||
private readonly compressor: ImageCompressorService,
|
||||
private readonly settings: BookTrackerPluginSettings,
|
||||
) { }
|
||||
|
||||
async download(
|
||||
coverImageUrl: string,
|
||||
fileName: string,
|
||||
overwrite?: boolean
|
||||
): Promise<TFile> {
|
||||
const response = await requestUrl(coverImageUrl);
|
||||
const header = Object.keys(response.headers).find(k => k.toLowerCase() === "content-type") ?? "Content-Type";
|
||||
const contentType = response.headers[header] ?? "application/octet-stream";
|
||||
|
||||
if (!contentType.startsWith("image/") && contentType !== "application/octet-stream") {
|
||||
throw new Error("Unexpected content type: " + contentType);
|
||||
}
|
||||
|
||||
const urlExtension = coverImageUrl.split(".").pop();
|
||||
const extension =
|
||||
CONTENT_TYPE_EXTENSIONS[contentType || ""] ?? urlExtension ?? "jpg";
|
||||
|
||||
let filePath = this.settings.coverFolder + "/";
|
||||
if (this.settings.groupCoversByFirstLetter) {
|
||||
let groupName = titleSortValue(fileName).charAt(0).toUpperCase();
|
||||
if (!/^[A-Z]$/.test(groupName)) {
|
||||
groupName = "#";
|
||||
}
|
||||
|
||||
filePath += groupName + "/";
|
||||
}
|
||||
filePath += fileName + "." + extension;
|
||||
|
||||
let file = this.vault.getFileByPath(filePath);
|
||||
if (file) {
|
||||
if (this.settings.overwriteExistingCovers || overwrite) {
|
||||
await this.vault.modifyBinary(file, response.arrayBuffer);
|
||||
} else {
|
||||
new Notice("Cover image already exists: " + filePath);
|
||||
return file;
|
||||
}
|
||||
} else {
|
||||
file = await this.vault.createBinary(
|
||||
filePath,
|
||||
response.arrayBuffer
|
||||
);
|
||||
}
|
||||
|
||||
await this.compressor.compress(file, {
|
||||
height: 400,
|
||||
quality: 0.8,
|
||||
maintainAspectRatio: true,
|
||||
});
|
||||
|
||||
if (file.extension !== "jpg") {
|
||||
await this.fileManager.renameFile(
|
||||
file,
|
||||
file.path.replace(/\.[^.]+$/, ".jpg")
|
||||
);
|
||||
}
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
import type { TFile, Vault } from "obsidian";
|
||||
|
||||
interface CompressOptions {
|
||||
width?: number;
|
||||
height?: number;
|
||||
maintainAspectRatio?: boolean;
|
||||
quality?: number;
|
||||
}
|
||||
|
||||
export class ImageCompressorService {
|
||||
|
||||
constructor(private readonly vault: Vault) {
|
||||
}
|
||||
|
||||
async compress(
|
||||
file: TFile,
|
||||
options: CompressOptions
|
||||
) {
|
||||
const img = await new Promise<HTMLImageElement>((resolve) => {
|
||||
const img = new Image();
|
||||
img.src = this.vault.getResourcePath(file);
|
||||
img.onload = () => {
|
||||
resolve(img);
|
||||
};
|
||||
});
|
||||
|
||||
const canvas = document.createElement("canvas");
|
||||
const ctx = canvas.getContext("2d")!;
|
||||
|
||||
const quality = options.quality ?? 1;
|
||||
let height = options.height;
|
||||
let width = options.width;
|
||||
|
||||
if (!width && !height) {
|
||||
width = img.width;
|
||||
height = img.height;
|
||||
} else if (!width && height) {
|
||||
width = height * (img.width / img.height);
|
||||
} else if (!height && width) {
|
||||
height = width * (img.height / img.width);
|
||||
}
|
||||
|
||||
if (options.maintainAspectRatio) {
|
||||
const aspectRatio = img.width / img.height;
|
||||
|
||||
if (options.height)
|
||||
if (width! > height!) {
|
||||
height = width! / aspectRatio;
|
||||
} else {
|
||||
width = height! * aspectRatio;
|
||||
}
|
||||
}
|
||||
|
||||
canvas.width = width!;
|
||||
canvas.height = height!;
|
||||
|
||||
ctx.drawImage(img, 0, 0, width!, height!);
|
||||
|
||||
const blob = await new Promise<Blob>((resolve, reject) => {
|
||||
canvas.toBlob(
|
||||
(blob) => {
|
||||
if (blob) {
|
||||
resolve(blob);
|
||||
} else {
|
||||
reject(new Error("Failed to compress image"));
|
||||
}
|
||||
},
|
||||
"image/jpeg",
|
||||
quality
|
||||
);
|
||||
});
|
||||
|
||||
return this.vault.modifyBinary(file, await blob.arrayBuffer());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
import { type App, type TFile } from "obsidian";
|
||||
|
||||
interface CompressOptions {
|
||||
width?: number;
|
||||
height?: number;
|
||||
maintainAspectRatio?: boolean;
|
||||
quality?: number;
|
||||
}
|
||||
|
||||
export async function compressImage(
|
||||
app: App,
|
||||
file: TFile,
|
||||
options: CompressOptions
|
||||
) {
|
||||
const img = await new Promise<HTMLImageElement>((resolve) => {
|
||||
const img = new Image();
|
||||
img.src = app.vault.getResourcePath(file);
|
||||
img.onload = () => {
|
||||
resolve(img);
|
||||
};
|
||||
});
|
||||
|
||||
const canvas = document.createElement("canvas");
|
||||
const ctx = canvas.getContext("2d")!;
|
||||
|
||||
const quality = options.quality ?? 1;
|
||||
let height = options.height;
|
||||
let width = options.width;
|
||||
|
||||
if (!width && !height) {
|
||||
width = img.width;
|
||||
height = img.height;
|
||||
} else if (!width && height) {
|
||||
width = height * (img.width / img.height);
|
||||
} else if (!height && width) {
|
||||
height = width * (img.height / img.width);
|
||||
}
|
||||
|
||||
if (options.maintainAspectRatio) {
|
||||
const aspectRatio = img.width / img.height;
|
||||
|
||||
if (options.height)
|
||||
if (width! > height!) {
|
||||
height = width! / aspectRatio;
|
||||
} else {
|
||||
width = height! * aspectRatio;
|
||||
}
|
||||
}
|
||||
|
||||
canvas.width = width!;
|
||||
canvas.height = height!;
|
||||
|
||||
ctx.drawImage(img, 0, 0, width!, height!);
|
||||
|
||||
const blob = await new Promise<Blob>((resolve, reject) => {
|
||||
canvas.toBlob(
|
||||
(blob) => {
|
||||
if (blob) {
|
||||
resolve(blob);
|
||||
} else {
|
||||
reject(new Error("Failed to compress image"));
|
||||
}
|
||||
},
|
||||
"image/jpeg",
|
||||
quality
|
||||
);
|
||||
});
|
||||
|
||||
return app.vault.modifyBinary(file, await blob.arrayBuffer());
|
||||
}
|
||||
|
|
@ -1,36 +1,51 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"inlineSourceMap": true,
|
||||
"inlineSources": true,
|
||||
"module": "ESNext",
|
||||
"target": "ES6",
|
||||
"allowJs": true,
|
||||
"noImplicitAny": true,
|
||||
"moduleResolution": "node",
|
||||
"importHelpers": true,
|
||||
"isolatedModules": true,
|
||||
"strictNullChecks": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"skipLibCheck": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"lib": [
|
||||
"DOM",
|
||||
"ES5",
|
||||
"ES6",
|
||||
"ES7"
|
||||
],
|
||||
"paths": {
|
||||
"@commands/*": ["src/commands/*"],
|
||||
"@data-sources/*": ["src/data-sources/*"],
|
||||
"@external/*": ["src/external/*"],
|
||||
"@ui/*": ["src/ui/*"],
|
||||
"@utils/*": ["src/utils/*"],
|
||||
"@src/*": ["src/*"]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"**/*.ts",
|
||||
"**/*.svelte"
|
||||
]
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"inlineSourceMap": true,
|
||||
"inlineSources": true,
|
||||
"module": "ESNext",
|
||||
"target": "ES6",
|
||||
"allowJs": true,
|
||||
"noImplicitAny": true,
|
||||
"moduleResolution": "node",
|
||||
"importHelpers": true,
|
||||
"isolatedModules": true,
|
||||
"strictNullChecks": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"skipLibCheck": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"lib": [
|
||||
"DOM",
|
||||
"ES5",
|
||||
"ES6",
|
||||
"ES7"
|
||||
],
|
||||
"paths": {
|
||||
"@commands/*": [
|
||||
"src/commands/*"
|
||||
],
|
||||
"@data-sources/*": [
|
||||
"src/data-sources/*"
|
||||
],
|
||||
"@external/*": [
|
||||
"src/external/*"
|
||||
],
|
||||
"@services/*": [
|
||||
"src/services/*"
|
||||
],
|
||||
"@ui/*": [
|
||||
"src/ui/*"
|
||||
],
|
||||
"@utils/*": [
|
||||
"src/utils/*"
|
||||
],
|
||||
"@src/*": [
|
||||
"src/*"
|
||||
]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"**/*.ts",
|
||||
"**/*.svelte"
|
||||
]
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue