fix some issues; migrate to bun'
This commit is contained in:
parent
cf3d5c6d6f
commit
54293ac3a1
56 changed files with 1360 additions and 764 deletions
13
.github/workflows/release.yml
vendored
13
.github/workflows/release.yml
vendored
|
|
@ -1,4 +1,6 @@
|
|||
name: Build obsidian plugin
|
||||
name: Build Obsidian Plugin
|
||||
|
||||
# adapted from https://github.com/argenos/nldates-obsidian/blob/master/.github/workflows/release.yml
|
||||
|
||||
on:
|
||||
push:
|
||||
|
|
@ -15,15 +17,14 @@ jobs:
|
|||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@v3
|
||||
- uses: oven-sh/setup-bun@v1
|
||||
with:
|
||||
node-version: '16.x' # You might need to adjust this value to your own version
|
||||
bun-version: latest
|
||||
- name: Build
|
||||
id: build
|
||||
run: |
|
||||
yarn
|
||||
yarn run build --if-present
|
||||
bun install
|
||||
bun run build
|
||||
mkdir ${{ env.PLUGIN_NAME }}
|
||||
cp main.js manifest.json styles.css ${{ env.PLUGIN_NAME }}
|
||||
zip -r ${{ env.PLUGIN_NAME }}.zip ${{ env.PLUGIN_NAME }}
|
||||
|
|
|
|||
38
automation/build/buildBanner.ts
Normal file
38
automation/build/buildBanner.ts
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
import manifest from '../../manifest.json' assert { type: 'json' };
|
||||
|
||||
export function getBuildBanner(buildType: string, getVersion: (version: string) => string) {
|
||||
return `/*
|
||||
-------------------------------------------
|
||||
${manifest.name} - ${buildType}
|
||||
-------------------------------------------
|
||||
By: ${manifest.author} (${manifest.authorUrl})
|
||||
Time: ${new Date().toUTCString()}
|
||||
Version: ${getVersion(manifest.version)}
|
||||
-------------------------------------------
|
||||
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
|
||||
if you want to view the source, please visit the github repository of this plugin
|
||||
-------------------------------------------
|
||||
MIT License
|
||||
|
||||
Copyright (c) ${new Date().getFullYear()} ${manifest.author}
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
`;
|
||||
}
|
||||
|
|
@ -1,21 +1,10 @@
|
|||
import esbuild from 'esbuild';
|
||||
import builtins from 'builtin-modules';
|
||||
import esbuild from 'esbuild';
|
||||
import esbuildSvelte from 'esbuild-svelte';
|
||||
import sveltePreprocess from 'svelte-preprocess';
|
||||
import manifest from './manifest.json' assert { type: 'json' };
|
||||
import { getBuildBanner } from 'build/buildBanner';
|
||||
|
||||
const banner = `/*
|
||||
-------------------------------------------
|
||||
${manifest.name} - Release Build
|
||||
-------------------------------------------
|
||||
By: ${manifest.author} (${manifest.authorUrl})
|
||||
Time: ${new Date().toUTCString()}
|
||||
Version: ${manifest.version}
|
||||
-------------------------------------------
|
||||
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
|
||||
if you want to view the source, please visit the github repository of this plugin
|
||||
*/
|
||||
`;
|
||||
const banner = getBuildBanner('Release Build', version => version);
|
||||
|
||||
const build = await esbuild.build({
|
||||
banner: {
|
||||
|
|
@ -47,14 +36,22 @@ const build = await esbuild.build({
|
|||
outfile: 'main.js',
|
||||
minify: true,
|
||||
metafile: true,
|
||||
define: {
|
||||
MB_GLOBAL_CONFIG_DEV_BUILD: 'false',
|
||||
},
|
||||
plugins: [
|
||||
esbuildSvelte({
|
||||
compilerOptions: { css: 'injected' },
|
||||
compilerOptions: { css: 'injected', dev: false, sveltePath: 'svelte' },
|
||||
preprocess: sveltePreprocess(),
|
||||
filterWarnings: warning => {
|
||||
// we don't want warnings from node modules that we can do nothing about
|
||||
return !warning.filename.includes('node_modules');
|
||||
return !warning.filename?.includes('node_modules');
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
const file = Bun.file('meta.txt');
|
||||
await Bun.write(file, JSON.stringify(build.metafile, null, '\t'));
|
||||
|
||||
process.exit(0);
|
||||
65
automation/build/esbuild.dev.config.ts
Normal file
65
automation/build/esbuild.dev.config.ts
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
import esbuild from 'esbuild';
|
||||
import copy from 'esbuild-plugin-copy-watch';
|
||||
import esbuildSvelte from 'esbuild-svelte';
|
||||
import sveltePreprocess from 'svelte-preprocess';
|
||||
import manifest from '../../manifest.json' assert { type: 'json' };
|
||||
import { getBuildBanner } from 'build/buildBanner';
|
||||
|
||||
const banner = getBuildBanner('Dev Build', _ => 'Dev Build');
|
||||
|
||||
const context = await esbuild.context({
|
||||
banner: {
|
||||
js: banner,
|
||||
},
|
||||
entryPoints: ['src/main.ts'],
|
||||
bundle: true,
|
||||
external: [
|
||||
'obsidian',
|
||||
'electron',
|
||||
'@codemirror/autocomplete',
|
||||
'@codemirror/collab',
|
||||
'@codemirror/commands',
|
||||
'@codemirror/language',
|
||||
'@codemirror/lint',
|
||||
'@codemirror/search',
|
||||
'@codemirror/state',
|
||||
'@codemirror/view',
|
||||
'@lezer/common',
|
||||
'@lezer/highlight',
|
||||
'@lezer/lr',
|
||||
],
|
||||
format: 'cjs',
|
||||
target: 'es2018',
|
||||
logLevel: 'info',
|
||||
sourcemap: 'inline',
|
||||
treeShaking: true,
|
||||
outdir: `exampleVault/.obsidian/plugins/${manifest.id}/`,
|
||||
outbase: 'src',
|
||||
define: {
|
||||
MB_GLOBAL_CONFIG_DEV_BUILD: 'true',
|
||||
},
|
||||
plugins: [
|
||||
copy({
|
||||
paths: [
|
||||
{
|
||||
from: './styles.css',
|
||||
to: '',
|
||||
},
|
||||
{
|
||||
from: './manifest.json',
|
||||
to: '',
|
||||
},
|
||||
],
|
||||
}),
|
||||
esbuildSvelte({
|
||||
compilerOptions: { css: 'injected', dev: true, sveltePath: 'svelte' },
|
||||
preprocess: sveltePreprocess(),
|
||||
filterWarnings: warning => {
|
||||
// we don't want warnings from node modules that we can do nothing about
|
||||
return !warning.filename?.includes('node_modules');
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
await context.watch();
|
||||
5
automation/config.json
Normal file
5
automation/config.json
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"devBranch": "master",
|
||||
"releaseBranch": "release",
|
||||
"github": "https://github.com/mProjectsCode/obsidian-media-db-plugin"
|
||||
}
|
||||
171
automation/release.ts
Normal file
171
automation/release.ts
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
import { UserError } from 'utils/utils';
|
||||
import { CanaryVersion, Version, getIncrementOptions, parseVersion, stringifyVersion } from 'utils/versionUtils';
|
||||
import config from './config.json';
|
||||
import { $choice as $choice, $confirm, $seq, CMD_FMT, Verboseness } from 'utils/shellUtils';
|
||||
|
||||
async function runPreconditions(): Promise<void> {
|
||||
// run preconditions
|
||||
await $seq(
|
||||
[`bun run format`, `bun run lint:fix`, `bun run test`],
|
||||
(cmd: string) => {
|
||||
throw new UserError(`precondition "${cmd}" failed`);
|
||||
},
|
||||
() => {},
|
||||
undefined,
|
||||
Verboseness.VERBOSE,
|
||||
);
|
||||
|
||||
// add changed files
|
||||
await $seq(
|
||||
[`git add .`],
|
||||
() => {
|
||||
throw new UserError('failed to add preconditions changes to git');
|
||||
},
|
||||
() => {},
|
||||
undefined,
|
||||
Verboseness.NORMAL,
|
||||
);
|
||||
|
||||
// check if there were any changes
|
||||
let changesToCommit = false;
|
||||
await $seq(
|
||||
[`git diff --quiet`, `git diff --cached --quiet`],
|
||||
() => {
|
||||
changesToCommit = true;
|
||||
},
|
||||
() => {},
|
||||
undefined,
|
||||
Verboseness.QUITET,
|
||||
);
|
||||
|
||||
// if there were any changes, commit them
|
||||
if (changesToCommit) {
|
||||
await $seq(
|
||||
[`git commit -m "[auto] run release preconditions"`],
|
||||
() => {
|
||||
throw new UserError('failed to add preconditions changes to git');
|
||||
},
|
||||
() => {},
|
||||
undefined,
|
||||
Verboseness.NORMAL,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async function run() {
|
||||
console.log('looking for untracked changes ...');
|
||||
|
||||
// check for any uncommited files and exit if there are any
|
||||
await $seq(
|
||||
[`git add .`, `git diff --quiet`, `git diff --cached --quiet`, `git checkout ${config.devBranch}`],
|
||||
() => {
|
||||
throw new UserError('there are still untracked changes');
|
||||
},
|
||||
() => {},
|
||||
undefined,
|
||||
Verboseness.QUITET,
|
||||
);
|
||||
|
||||
console.log('\nrunning preconditions ...\n');
|
||||
|
||||
await runPreconditions();
|
||||
|
||||
console.log('\nbumping versions ...\n');
|
||||
|
||||
const manifestFile = Bun.file('./manifest.json');
|
||||
const manifest = await manifestFile.json();
|
||||
|
||||
const versionString: string = manifest.version;
|
||||
const currentVersion: Version = parseVersion(versionString);
|
||||
const currentVersionString = stringifyVersion(currentVersion);
|
||||
|
||||
const versionIncrementOptions = getIncrementOptions(currentVersion);
|
||||
|
||||
const selectedIndex = await $choice(
|
||||
`Current version "${currentVersionString}". Select new version`,
|
||||
versionIncrementOptions.map(x => stringifyVersion(x)),
|
||||
);
|
||||
const newVersion = versionIncrementOptions[selectedIndex];
|
||||
const newVersionString = stringifyVersion(newVersion);
|
||||
|
||||
console.log('');
|
||||
|
||||
await $confirm(`Version will be updated "${currentVersionString}" -> "${newVersionString}". Are you sure`, () => {
|
||||
throw new UserError('user canceled script');
|
||||
});
|
||||
|
||||
if (!(newVersion instanceof CanaryVersion)) {
|
||||
manifest.version = newVersionString;
|
||||
}
|
||||
|
||||
await Bun.write(manifestFile, JSON.stringify(manifest, null, '\t'));
|
||||
|
||||
const betaManifest = structuredClone(manifest);
|
||||
betaManifest.version = newVersionString;
|
||||
|
||||
const betaManifestFile = Bun.file('./manifest-beta.json');
|
||||
await Bun.write(betaManifestFile, JSON.stringify(betaManifest, null, '\t'));
|
||||
|
||||
if (!(newVersion instanceof CanaryVersion)) {
|
||||
const versionsFile = Bun.file('./versions.json');
|
||||
const versionsJson = await versionsFile.json();
|
||||
|
||||
versionsJson[newVersionString] = manifest.minAppVersion;
|
||||
|
||||
await Bun.write(versionsFile, JSON.stringify(versionsJson, null, '\t'));
|
||||
|
||||
const packageFile = Bun.file('./package.json');
|
||||
const packageJson = await packageFile.json();
|
||||
|
||||
packageJson.version = newVersionString;
|
||||
|
||||
await Bun.write(packageFile, JSON.stringify(packageJson, null, '\t'));
|
||||
}
|
||||
|
||||
await $seq(
|
||||
[`bun run format`, `git add .`, `git commit -m "[auto] bump version to \`${newVersionString}\`"`],
|
||||
() => {
|
||||
throw new UserError('failed to add preconditions changes to git');
|
||||
},
|
||||
() => {},
|
||||
undefined,
|
||||
Verboseness.NORMAL,
|
||||
);
|
||||
|
||||
console.log('\ncreating release tag ...\n');
|
||||
|
||||
await $seq(
|
||||
[
|
||||
`git checkout ${config.releaseBranch}`,
|
||||
`git merge ${config.devBranch} --commit -m "[auto] merge \`${newVersionString}\` release commit"`,
|
||||
`git push origin ${config.releaseBranch}`,
|
||||
`git tag -a ${newVersionString} -m "release version ${newVersionString}"`,
|
||||
`git push origin ${newVersionString}`,
|
||||
`git checkout ${config.devBranch}`,
|
||||
`git merge ${config.releaseBranch}`,
|
||||
`git push origin ${config.devBranch}`,
|
||||
],
|
||||
() => {
|
||||
throw new UserError('failed to merge or create tag');
|
||||
},
|
||||
() => {},
|
||||
undefined,
|
||||
Verboseness.NORMAL,
|
||||
);
|
||||
|
||||
console.log('');
|
||||
|
||||
console.log(`${CMD_FMT.BgGreen}done${CMD_FMT.Reset}`);
|
||||
console.log(`${config.github}`);
|
||||
console.log(`${config.github}/releases/tag/${newVersionString}`);
|
||||
}
|
||||
|
||||
try {
|
||||
await run();
|
||||
} catch (e) {
|
||||
if (e instanceof UserError) {
|
||||
console.error(e.message);
|
||||
} else {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
160
automation/stats.ts
Normal file
160
automation/stats.ts
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
import * as fs from 'fs';
|
||||
|
||||
interface Stat {
|
||||
fileType: string;
|
||||
count: number;
|
||||
lines: number;
|
||||
}
|
||||
|
||||
abstract class StatsBase {
|
||||
parent: StatsBase | undefined;
|
||||
path: string;
|
||||
name: string;
|
||||
stats: Stat[];
|
||||
|
||||
constructor(parent: StatsBase | undefined, path: string, name: string, stats: Stat[]) {
|
||||
this.parent = parent;
|
||||
|
||||
this.path = path;
|
||||
this.name = name;
|
||||
this.stats = stats;
|
||||
}
|
||||
|
||||
abstract addChild(child: StatsBase): void;
|
||||
|
||||
abstract mergeStats(stats: Stat[]): void;
|
||||
|
||||
abstract print(depth: number, lastChildArr: boolean[]): void;
|
||||
|
||||
abstract sort(): void;
|
||||
|
||||
getPrefix(depth: number, lastChildArr: boolean[]): string {
|
||||
let prefix = '';
|
||||
for (let i = 0; i < depth; i++) {
|
||||
prefix += lastChildArr[i] ? ' ' : '│ ';
|
||||
}
|
||||
|
||||
if (lastChildArr.at(-1)) {
|
||||
prefix += '└─ ';
|
||||
} else {
|
||||
prefix += '├─ ';
|
||||
}
|
||||
|
||||
return prefix;
|
||||
}
|
||||
}
|
||||
|
||||
class FolderStats extends StatsBase {
|
||||
children: StatsBase[];
|
||||
|
||||
constructor(parent: StatsBase | undefined, path: string, name: string) {
|
||||
super(parent, path, name, []);
|
||||
|
||||
this.children = [];
|
||||
}
|
||||
|
||||
addChild(child: StatsBase) {
|
||||
this.children.push(child);
|
||||
this.mergeStats(child.stats);
|
||||
}
|
||||
|
||||
mergeStats(stats: Stat[]): void {
|
||||
// console.log(this, stats);
|
||||
for (const stat of stats) {
|
||||
const existingStat = this.stats.find(s => s.fileType === stat.fileType);
|
||||
if (existingStat) {
|
||||
existingStat.count += stat.count;
|
||||
existingStat.lines += stat.lines;
|
||||
} else {
|
||||
this.stats.push(structuredClone(stat));
|
||||
}
|
||||
}
|
||||
|
||||
this.parent?.mergeStats(stats);
|
||||
}
|
||||
|
||||
print(depth: number, lastChildArr: boolean[]): void {
|
||||
console.log(
|
||||
`${this.getPrefix(depth, lastChildArr)}${this.name} | ${this.stats.reduce((acc, s) => acc + s.count, 0)} files | ${this.stats.reduce((acc, s) => acc + s.lines, 0)} lines`,
|
||||
);
|
||||
for (let i = 0; i < this.children.length; i++) {
|
||||
const child = this.children[i];
|
||||
child.print(depth + 1, [...lastChildArr, i === this.children.length - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
sort(): void {
|
||||
this.children.sort((a, b) => {
|
||||
if (a instanceof FolderStats && b instanceof FileStats) {
|
||||
return 1;
|
||||
} else if (a instanceof FileStats && b instanceof FolderStats) {
|
||||
return -1;
|
||||
} else {
|
||||
return a.name.localeCompare(b.name);
|
||||
}
|
||||
});
|
||||
this.children.forEach(c => c.sort());
|
||||
}
|
||||
}
|
||||
|
||||
class FileStats extends StatsBase {
|
||||
constructor(parent: StatsBase, path: string, name: string, stats: Stat[]) {
|
||||
super(parent, path, name, stats);
|
||||
}
|
||||
|
||||
addChild(_child: StatsBase): void {
|
||||
throw new Error('Cannot add child to file');
|
||||
}
|
||||
|
||||
mergeStats(_stats: Stat[]): void {
|
||||
throw new Error('Cannot merge stats to file');
|
||||
}
|
||||
|
||||
print(depth: number, lastChildArr: boolean[]): void {
|
||||
console.log(`${this.getPrefix(depth, lastChildArr)}${this.name} | ${this.stats[0].lines} lines`);
|
||||
}
|
||||
|
||||
sort(): void {}
|
||||
}
|
||||
|
||||
function collectStats() {
|
||||
const root = new FolderStats(undefined, './src', 'src');
|
||||
const ignore = ['node_modules', 'extraTypes', 'bun.lockb'];
|
||||
|
||||
const todo: FolderStats[] = [root];
|
||||
|
||||
while (todo.length > 0) {
|
||||
const current = todo.pop()!;
|
||||
const children = fs.readdirSync(current.path, { withFileTypes: true });
|
||||
|
||||
for (const child of children) {
|
||||
if (ignore.includes(child.name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (child.isDirectory()) {
|
||||
const folder = new FolderStats(current, `${current.path}/${child.name}`, child.name);
|
||||
current.addChild(folder);
|
||||
|
||||
todo.push(folder);
|
||||
} else {
|
||||
const content = fs.readFileSync(`${current.path}/${child.name}`, 'utf-8');
|
||||
|
||||
const file = new FileStats(current, `${current.path}/${child.name}`, child.name, [
|
||||
{
|
||||
fileType: child.name.split('.').splice(1).join('.'),
|
||||
count: 1,
|
||||
lines: content.split('\n').length,
|
||||
},
|
||||
]);
|
||||
current.addChild(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
root.sort();
|
||||
|
||||
root.print(0, [true]);
|
||||
}
|
||||
|
||||
collectStats();
|
||||
20
automation/tsconfig.json
Normal file
20
automation/tsconfig.json
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"module": "ESNext",
|
||||
"target": "ESNext",
|
||||
"allowJs": true,
|
||||
"noImplicitAny": true,
|
||||
"strict": true,
|
||||
"strictNullChecks": true,
|
||||
"noImplicitReturns": true,
|
||||
"moduleResolution": "node",
|
||||
"importHelpers": true,
|
||||
"isolatedModules": true,
|
||||
"lib": ["DOM", "ES5", "ES6", "ES7", "Es2021"],
|
||||
"types": ["bun-types"],
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"resolveJsonModule": true
|
||||
},
|
||||
"include": ["**/*.ts"]
|
||||
}
|
||||
168
automation/utils/shellUtils.ts
Normal file
168
automation/utils/shellUtils.ts
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
import { Subprocess } from 'bun';
|
||||
import stringArgv from 'string-argv';
|
||||
|
||||
export enum Verboseness {
|
||||
QUITET,
|
||||
NORMAL,
|
||||
VERBOSE,
|
||||
}
|
||||
|
||||
function exec(c: string, cwd?: string): Subprocess<'ignore', 'pipe', 'inherit'> {
|
||||
return Bun.spawn(stringArgv(c), { cwd: cwd });
|
||||
}
|
||||
|
||||
export async function $(cmd: string, cwd?: string | undefined, verboseness: Verboseness = Verboseness.NORMAL): Promise<{ stdout: string; stderr: string; exit: number }> {
|
||||
if (verboseness === Verboseness.NORMAL || verboseness === Verboseness.VERBOSE) {
|
||||
if (cwd !== undefined) {
|
||||
console.log(`\n${CMD_FMT.Bright}running${CMD_FMT.Reset} in ${cwd} - ${cmd}\n`);
|
||||
} else {
|
||||
console.log(`\n${CMD_FMT.Bright}running${CMD_FMT.Reset} - ${cmd}\n`);
|
||||
}
|
||||
}
|
||||
|
||||
const proc = exec(cmd, cwd);
|
||||
const stdout = await new Response(proc.stdout).text();
|
||||
const stderr = await new Response(proc.stderr).text();
|
||||
|
||||
if (verboseness === Verboseness.VERBOSE) {
|
||||
if (stdout !== '') {
|
||||
console.log(
|
||||
stdout
|
||||
.split('\n')
|
||||
.map(x => `${CMD_FMT.FgGray}>${CMD_FMT.Reset} ${x}\n`)
|
||||
.join(''),
|
||||
);
|
||||
}
|
||||
|
||||
if (stderr !== '') {
|
||||
console.log(
|
||||
stderr
|
||||
.split('\n')
|
||||
.map(x => `${CMD_FMT.FgRed}>${CMD_FMT.Reset} ${x}\n`)
|
||||
.join(''),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const exit = await proc.exited;
|
||||
|
||||
if (verboseness === Verboseness.NORMAL || verboseness === Verboseness.VERBOSE) {
|
||||
if (exit === 0) {
|
||||
console.log(`${CMD_FMT.FgGreen}success${CMD_FMT.Reset} - ${cmd}\n`);
|
||||
} else {
|
||||
console.log(`${CMD_FMT.FgRed}fail${CMD_FMT.Reset} - ${cmd} - code ${exit}\n`);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
stdout,
|
||||
stderr,
|
||||
exit,
|
||||
};
|
||||
}
|
||||
|
||||
export async function $seq(
|
||||
cmds: string[],
|
||||
onError: (cmd: string, index: number) => void,
|
||||
onSuccess: () => void,
|
||||
cwd?: string | undefined,
|
||||
verboseness: Verboseness = Verboseness.NORMAL,
|
||||
): Promise<void> {
|
||||
const results = [];
|
||||
for (let i = 0; i < cmds.length; i += 1) {
|
||||
const cmd = cmds[i];
|
||||
const result = await $(cmd, cwd, verboseness);
|
||||
|
||||
if (result.exit !== 0) {
|
||||
onError(cmd, i);
|
||||
return;
|
||||
}
|
||||
|
||||
results.push(result);
|
||||
}
|
||||
onSuccess();
|
||||
}
|
||||
|
||||
export async function $input(message: string): Promise<string> {
|
||||
console.write(`${message} `);
|
||||
const stdin = Bun.stdin.stream();
|
||||
const reader = stdin.getReader();
|
||||
const chunk = await reader.read();
|
||||
reader.releaseLock();
|
||||
const text = Buffer.from(chunk.value ?? '').toString();
|
||||
return text.trim();
|
||||
}
|
||||
|
||||
export async function $choice(message: string, options: string[]): Promise<number> {
|
||||
console.log(`${message} `);
|
||||
|
||||
let optionNumbers = new Map<string, number>();
|
||||
|
||||
for (let i = 0; i < options.length; i++) {
|
||||
const option = options[i];
|
||||
console.log(`[${i}] ${option}`);
|
||||
|
||||
optionNumbers.set(i.toString(), i);
|
||||
}
|
||||
|
||||
let ret: undefined | number = undefined;
|
||||
|
||||
while (ret === undefined) {
|
||||
const selectedStr = await $input(`Select [${[...optionNumbers.keys()].join('/')}]:`);
|
||||
|
||||
ret = optionNumbers.get(selectedStr);
|
||||
|
||||
if (ret === undefined) {
|
||||
console.log(`${CMD_FMT.FgRed}invalid selection, please select a valid option${CMD_FMT.Reset}`);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
export async function $confirm(message: string, onReject: () => void): Promise<void> {
|
||||
while (true) {
|
||||
const answer = await $input(`${message} [Y/N]?`);
|
||||
|
||||
if (answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (answer.toLowerCase() === 'n' || answer.toLowerCase() === 'no') {
|
||||
onReject();
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`${CMD_FMT.FgRed}invalid selection, please select a valid option${CMD_FMT.Reset}`);
|
||||
}
|
||||
}
|
||||
|
||||
export const CMD_FMT = {
|
||||
Reset: '\x1b[0m',
|
||||
Bright: '\x1b[1m',
|
||||
Dim: '\x1b[2m',
|
||||
Underscore: '\x1b[4m',
|
||||
Blink: '\x1b[5m',
|
||||
Reverse: '\x1b[7m',
|
||||
Hidden: '\x1b[8m',
|
||||
|
||||
FgBlack: '\x1b[30m',
|
||||
FgRed: '\x1b[31m',
|
||||
FgGreen: '\x1b[32m',
|
||||
FgYellow: '\x1b[33m',
|
||||
FgBlue: '\x1b[34m',
|
||||
FgMagenta: '\x1b[35m',
|
||||
FgCyan: '\x1b[36m',
|
||||
FgWhite: '\x1b[37m',
|
||||
FgGray: '\x1b[90m',
|
||||
|
||||
BgBlack: '\x1b[40m',
|
||||
BgRed: '\x1b[41m',
|
||||
BgGreen: '\x1b[42m',
|
||||
BgYellow: '\x1b[43m',
|
||||
BgBlue: '\x1b[44m',
|
||||
BgMagenta: '\x1b[45m',
|
||||
BgCyan: '\x1b[46m',
|
||||
BgWhite: '\x1b[47m',
|
||||
BgGray: '\x1b[100m',
|
||||
};
|
||||
6
automation/utils/utils.ts
Normal file
6
automation/utils/utils.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
export class UserError extends Error {}
|
||||
|
||||
export interface ProjectConfig {
|
||||
corePackages: string[];
|
||||
packages: string[];
|
||||
}
|
||||
108
automation/utils/versionUtils.ts
Normal file
108
automation/utils/versionUtils.ts
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
import { Parser } from '@lemons_dev/parsinom/lib/Parser';
|
||||
import { P_UTILS } from '@lemons_dev/parsinom/lib/ParserUtils';
|
||||
import { P } from '@lemons_dev/parsinom/lib/ParsiNOM';
|
||||
import Moment from 'moment';
|
||||
import { UserError } from 'utils/utils';
|
||||
|
||||
export class Version {
|
||||
major: number;
|
||||
minor: number;
|
||||
patch: number;
|
||||
|
||||
constructor(major: number, minor: number, patch: number) {
|
||||
this.major = major;
|
||||
this.minor = minor;
|
||||
this.patch = patch;
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
return `${this.major}.${this.minor}.${this.patch}`;
|
||||
}
|
||||
}
|
||||
|
||||
export class CanaryVersion extends Version {
|
||||
canary: string;
|
||||
|
||||
constructor(major: number, minor: number, patch: number, canary: string) {
|
||||
super(major, minor, patch);
|
||||
this.canary = canary;
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
return `${super.toString()}-canary.${this.canary}`;
|
||||
}
|
||||
}
|
||||
|
||||
const numberParser: Parser<number> = P_UTILS.digits()
|
||||
.map(x => Number.parseInt(x))
|
||||
.chain(x => {
|
||||
if (Number.isNaN(x)) {
|
||||
return P.fail('a number');
|
||||
} else {
|
||||
return P.succeed(x);
|
||||
}
|
||||
});
|
||||
|
||||
const canaryParser: Parser<string> = P.sequenceMap(
|
||||
(_, c1, c2, c3) => {
|
||||
return c1 + c2 + c3;
|
||||
},
|
||||
P.string('-canary.'),
|
||||
P_UTILS.digit()
|
||||
.repeat(8, 8)
|
||||
.map(x => x.join('')),
|
||||
P.string('T'),
|
||||
P_UTILS.digit()
|
||||
.repeat(6, 6)
|
||||
.map(x => x.join('')),
|
||||
);
|
||||
|
||||
export const versionParser: Parser<Version> = P.or(
|
||||
P.sequenceMap(
|
||||
(major, _1, minor, _2, patch) => {
|
||||
return new Version(major, minor, patch);
|
||||
},
|
||||
numberParser,
|
||||
P.string('.'),
|
||||
numberParser,
|
||||
P.string('.'),
|
||||
numberParser,
|
||||
P_UTILS.eof(),
|
||||
),
|
||||
P.sequenceMap(
|
||||
(major, _1, minor, _2, patch, canary) => {
|
||||
return new CanaryVersion(major, minor, patch, canary);
|
||||
},
|
||||
numberParser,
|
||||
P.string('.'),
|
||||
numberParser,
|
||||
P.string('.'),
|
||||
numberParser,
|
||||
canaryParser,
|
||||
P_UTILS.eof(),
|
||||
),
|
||||
);
|
||||
|
||||
export function parseVersion(str: string): Version {
|
||||
const parserRes = versionParser.tryParse(str);
|
||||
if (parserRes.success) {
|
||||
return parserRes.value;
|
||||
} else {
|
||||
throw new UserError(`failed to parse manifest version "${str}"`);
|
||||
}
|
||||
}
|
||||
|
||||
export function stringifyVersion(version: Version): string {
|
||||
return version.toString();
|
||||
}
|
||||
|
||||
export function getIncrementOptions(version: Version): [Version, Version, Version, CanaryVersion] {
|
||||
const moment = Moment();
|
||||
const canary = moment.utcOffset(0).format('YYYYMMDDTHHmmss');
|
||||
return [
|
||||
new Version(version.major + 1, 0, 0),
|
||||
new Version(version.major, version.minor + 1, 0),
|
||||
new Version(version.major, version.minor, version.patch + 1),
|
||||
new CanaryVersion(version.major, version.minor, version.patch, canary),
|
||||
];
|
||||
}
|
||||
BIN
bun.lockb
Executable file
BIN
bun.lockb
Executable file
Binary file not shown.
|
|
@ -1,63 +0,0 @@
|
|||
import esbuild from 'esbuild';
|
||||
import process from 'process';
|
||||
import builtins from 'builtin-modules';
|
||||
import esbuildSvelte from 'esbuild-svelte';
|
||||
import sveltePreprocess from 'svelte-preprocess';
|
||||
import manifest from './manifest.json' assert { type: 'json' };
|
||||
|
||||
const banner = `/*
|
||||
-------------------------------------------
|
||||
${manifest.name} - Dev Build
|
||||
-------------------------------------------
|
||||
By: ${manifest.author} (${manifest.authorUrl})
|
||||
Time: ${new Date().toUTCString()}
|
||||
Version: Dev Build
|
||||
-------------------------------------------
|
||||
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
|
||||
if you want to view the source, please visit the github repository of this plugin
|
||||
*/
|
||||
`;
|
||||
|
||||
const context = await esbuild
|
||||
.context({
|
||||
banner: {
|
||||
js: banner,
|
||||
},
|
||||
entryPoints: ['src/main.ts'],
|
||||
bundle: true,
|
||||
external: [
|
||||
'obsidian',
|
||||
'electron',
|
||||
'@codemirror/autocomplete',
|
||||
'@codemirror/collab',
|
||||
'@codemirror/commands',
|
||||
'@codemirror/language',
|
||||
'@codemirror/lint',
|
||||
'@codemirror/search',
|
||||
'@codemirror/state',
|
||||
'@codemirror/view',
|
||||
'@lezer/common',
|
||||
'@lezer/highlight',
|
||||
'@lezer/lr',
|
||||
...builtins,
|
||||
],
|
||||
format: 'cjs',
|
||||
target: 'es2018',
|
||||
logLevel: 'info',
|
||||
sourcemap: 'inline',
|
||||
treeShaking: true,
|
||||
outfile: 'main.js',
|
||||
plugins: [
|
||||
esbuildSvelte({
|
||||
compilerOptions: { css: 'injected' },
|
||||
preprocess: sveltePreprocess(),
|
||||
filterWarnings: warning => {
|
||||
// we don't want warnings from node modules that we can do nothing about
|
||||
return !warning.filename.includes('node_modules');
|
||||
},
|
||||
}),
|
||||
],
|
||||
})
|
||||
.catch(() => process.exit(1));
|
||||
|
||||
await context.watch();
|
||||
1
exampleVault/.obsidian/app.json
vendored
Normal file
1
exampleVault/.obsidian/app.json
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
{}
|
||||
4
exampleVault/.obsidian/appearance.json
vendored
Normal file
4
exampleVault/.obsidian/appearance.json
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"accentColor": "",
|
||||
"theme": "obsidian"
|
||||
}
|
||||
1
exampleVault/.obsidian/community-plugins.json
vendored
Normal file
1
exampleVault/.obsidian/community-plugins.json
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
["obsidian-media-db-plugin"]
|
||||
31
exampleVault/.obsidian/core-plugins-migration.json
vendored
Normal file
31
exampleVault/.obsidian/core-plugins-migration.json
vendored
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
{
|
||||
"file-explorer": true,
|
||||
"global-search": true,
|
||||
"switcher": true,
|
||||
"graph": true,
|
||||
"backlink": true,
|
||||
"canvas": true,
|
||||
"outgoing-link": true,
|
||||
"tag-pane": true,
|
||||
"properties": false,
|
||||
"page-preview": true,
|
||||
"daily-notes": true,
|
||||
"templates": true,
|
||||
"note-composer": true,
|
||||
"command-palette": true,
|
||||
"slash-command": false,
|
||||
"editor-status": true,
|
||||
"bookmarks": true,
|
||||
"markdown-importer": false,
|
||||
"zk-prefixer": false,
|
||||
"random-note": false,
|
||||
"outline": true,
|
||||
"word-count": true,
|
||||
"slides": false,
|
||||
"audio-recorder": false,
|
||||
"workspaces": false,
|
||||
"file-recovery": true,
|
||||
"publish": false,
|
||||
"sync": false,
|
||||
"browser": false
|
||||
}
|
||||
20
exampleVault/.obsidian/core-plugins.json
vendored
Normal file
20
exampleVault/.obsidian/core-plugins.json
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
[
|
||||
"file-explorer",
|
||||
"global-search",
|
||||
"switcher",
|
||||
"graph",
|
||||
"backlink",
|
||||
"canvas",
|
||||
"outgoing-link",
|
||||
"tag-pane",
|
||||
"page-preview",
|
||||
"daily-notes",
|
||||
"templates",
|
||||
"note-composer",
|
||||
"command-palette",
|
||||
"editor-status",
|
||||
"bookmarks",
|
||||
"outline",
|
||||
"word-count",
|
||||
"file-recovery"
|
||||
]
|
||||
8
exampleVault/.obsidian/hotkeys.json
vendored
Normal file
8
exampleVault/.obsidian/hotkeys.json
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"app:reload": [
|
||||
{
|
||||
"modifiers": ["Mod"],
|
||||
"key": "R"
|
||||
}
|
||||
]
|
||||
}
|
||||
0
exampleVault/.obsidian/plugins/obsidian-media-db-plugin/.hotreload
vendored
Normal file
0
exampleVault/.obsidian/plugins/obsidian-media-db-plugin/.hotreload
vendored
Normal file
10
exampleVault/.obsidian/plugins/obsidian-media-db-plugin/manifest.json
vendored
Normal file
10
exampleVault/.obsidian/plugins/obsidian-media-db-plugin/manifest.json
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"id": "obsidian-media-db-plugin",
|
||||
"name": "Media DB",
|
||||
"version": "0.6.0",
|
||||
"minAppVersion": "1.5.0",
|
||||
"description": "A plugin that can query multiple APIs for movies, series, anime, games, music and wiki articles, and import them into your vault.",
|
||||
"author": "Moritz Jung",
|
||||
"authorUrl": "https://www.moritzjung.dev",
|
||||
"isDesktopOnly": false
|
||||
}
|
||||
126
exampleVault/.obsidian/plugins/obsidian-media-db-plugin/styles.css
vendored
Normal file
126
exampleVault/.obsidian/plugins/obsidian-media-db-plugin/styles.css
vendored
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
.media-db-plugin-list-wrapper {
|
||||
display: flex;
|
||||
align-content: center;
|
||||
margin-bottom: 5px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.media-db-plugin-list-toggle {
|
||||
}
|
||||
|
||||
.media-db-plugin-list-text-wrapper {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.media-db-plugin-list-text {
|
||||
display: block;
|
||||
}
|
||||
|
||||
small.media-db-plugin-list-text {
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.media-db-plugin-select-modal {
|
||||
display: contents;
|
||||
}
|
||||
|
||||
.media-db-plugin-select-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 5px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.media-db-plugin-select-element {
|
||||
cursor: pointer;
|
||||
border-left: 5px solid transparent;
|
||||
padding: 5px;
|
||||
margin: 5px 0 5px 0;
|
||||
border-radius: 5px;
|
||||
white-space: pre-wrap;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.media-db-plugin-select-element-selected {
|
||||
border-left: 5px solid var(--interactive-accent) !important;
|
||||
background: var(--background-secondary-alt);
|
||||
}
|
||||
|
||||
.media-db-plugin-select-element-hover {
|
||||
background: var(--background-secondary-alt);
|
||||
}
|
||||
|
||||
.media-db-plugin-preview-modal {
|
||||
display: contents;
|
||||
}
|
||||
|
||||
.media-db-plugin-preview-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.media-db-plugin-spacer {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
/* region property mappings */
|
||||
.media-db-plugin-property-mappings-model-container {
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
border-radius: 5px;
|
||||
padding: 10px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.media-db-plugin-property-mappings-container {
|
||||
margin: 10px 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.media-db-plugin-property-mapping-element {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.media-db-plugin-property-mapping-element-property-name-wrapper {
|
||||
min-width: 160px;
|
||||
background: var(--background-modifier-form-field);
|
||||
padding: 2px 5px;
|
||||
border-radius: 5px;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.media-db-plugin-property-mapping-element-property-name {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.media-db-plugin-property-mappings-save-button {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.media-db-plugin-property-mapping-to {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.media-db-plugin-property-mapping-validation {
|
||||
color: var(--text-error);
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.media-db-plugin-button:focus {
|
||||
/*outline: 1px solid white;*/
|
||||
}
|
||||
|
||||
.media-db-plugin-preview {
|
||||
border-radius: var(--modal-radius);
|
||||
border: var(--modal-border-width) solid var(--modal-border-color);
|
||||
padding: var(--size-4-4);
|
||||
}
|
||||
|
||||
/* endregion */
|
||||
151
exampleVault/.obsidian/workspace.json
vendored
Normal file
151
exampleVault/.obsidian/workspace.json
vendored
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
{
|
||||
"main": {
|
||||
"id": "9618d22a4d8c511c",
|
||||
"type": "split",
|
||||
"children": [
|
||||
{
|
||||
"id": "982b4a04c4a0e996",
|
||||
"type": "tabs",
|
||||
"children": [
|
||||
{
|
||||
"id": "b912b847e62b0980",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "empty",
|
||||
"state": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"direction": "vertical"
|
||||
},
|
||||
"left": {
|
||||
"id": "a448fa9a0495e7a4",
|
||||
"type": "split",
|
||||
"children": [
|
||||
{
|
||||
"id": "0148e129668970ae",
|
||||
"type": "tabs",
|
||||
"children": [
|
||||
{
|
||||
"id": "5f549c8ae9af971e",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "file-explorer",
|
||||
"state": {
|
||||
"sortOrder": "alphabetical"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "b782c94865111b8f",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "search",
|
||||
"state": {
|
||||
"query": "",
|
||||
"matchingCase": false,
|
||||
"explainSearch": false,
|
||||
"collapseAll": false,
|
||||
"extraContext": false,
|
||||
"sortOrder": "alphabetical"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "942c8e2bba79177d",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "bookmarks",
|
||||
"state": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"direction": "horizontal",
|
||||
"width": 300
|
||||
},
|
||||
"right": {
|
||||
"id": "8a6d32cd2e32c39d",
|
||||
"type": "split",
|
||||
"children": [
|
||||
{
|
||||
"id": "b712a27a0deec50f",
|
||||
"type": "tabs",
|
||||
"children": [
|
||||
{
|
||||
"id": "81240f6821eb54dc",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "backlink",
|
||||
"state": {
|
||||
"collapseAll": false,
|
||||
"extraContext": false,
|
||||
"sortOrder": "alphabetical",
|
||||
"showSearch": false,
|
||||
"searchQuery": "",
|
||||
"backlinkCollapsed": false,
|
||||
"unlinkedCollapsed": true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "2dda1a67c07f367d",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "outgoing-link",
|
||||
"state": {
|
||||
"linksCollapsed": false,
|
||||
"unlinkedCollapsed": true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "cb0aecbffefcb594",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "tag",
|
||||
"state": {
|
||||
"sortOrder": "frequency",
|
||||
"useHierarchy": true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "1be09685a129aa6e",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "outline",
|
||||
"state": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"direction": "horizontal",
|
||||
"width": 300,
|
||||
"collapsed": true
|
||||
},
|
||||
"left-ribbon": {
|
||||
"hiddenItems": {
|
||||
"obsidian-media-db-plugin:Add new Media DB entry": false,
|
||||
"switcher:Open quick switcher": false,
|
||||
"graph:Open graph view": false,
|
||||
"canvas:Create new canvas": false,
|
||||
"daily-notes:Open today's daily note": false,
|
||||
"templates:Insert template": false,
|
||||
"command-palette:Open command palette": false
|
||||
}
|
||||
},
|
||||
"active": "b912b847e62b0980",
|
||||
"lastOpenFiles": [
|
||||
"Media DB/games/Limbo (2010).md",
|
||||
"Media DB/games/Hollow Knight (2017).md",
|
||||
"Media DB/movies/2001 - A Space Odyssey (1968).md",
|
||||
"Media DB/games",
|
||||
"Media DB/movies",
|
||||
"Media DB"
|
||||
]
|
||||
}
|
||||
0
exampleVault/index.md
Normal file
0
exampleVault/index.md
Normal file
|
|
@ -1,7 +0,0 @@
|
|||
module.exports = {
|
||||
roots: ['<rootDir>/src', '<rootDir>'],
|
||||
testMatch: ['**/__tests__/**/*.+(ts|tsx|js)', '**/?(*.)+(spec|test).+(ts|tsx|js)'],
|
||||
transform: {
|
||||
'^.+\\.(ts|tsx)$': 'ts-jest',
|
||||
},
|
||||
};
|
||||
10
manifest-beta.json
Normal file
10
manifest-beta.json
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"id": "obsidian-media-db-plugin",
|
||||
"name": "Media DB",
|
||||
"version": "0.6.0",
|
||||
"minAppVersion": "1.5.0",
|
||||
"description": "A plugin that can query multiple APIs for movies, series, anime, games, music and wiki articles, and import them into your vault.",
|
||||
"author": "Moritz Jung",
|
||||
"authorUrl": "https://www.moritzjung.dev",
|
||||
"isDesktopOnly": false
|
||||
}
|
||||
|
|
@ -2,9 +2,9 @@
|
|||
"id": "obsidian-media-db-plugin",
|
||||
"name": "Media DB",
|
||||
"version": "0.6.0",
|
||||
"minAppVersion": "0.14.0",
|
||||
"minAppVersion": "1.5.0",
|
||||
"description": "A plugin that can query multiple APIs for movies, series, anime, games, music and wiki articles, and import them into your vault.",
|
||||
"author": "Moritz Jung",
|
||||
"authorUrl": "https://mprojectscode.github.io/",
|
||||
"authorUrl": "https://www.moritzjung.dev",
|
||||
"isDesktopOnly": false
|
||||
}
|
||||
|
|
|
|||
54
package.json
54
package.json
|
|
@ -4,34 +4,48 @@
|
|||
"description": "A plugin that can query multiple APIs for movies, series, anime, games, music and wiki articles, and import them into your vault.",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
"dev": "node esbuild.dev.config.mjs",
|
||||
"build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production",
|
||||
"version": "node version-bump.mjs && git add manifest.json versions.json",
|
||||
"test": "jest",
|
||||
"format": "prettier --write ."
|
||||
"dev": "bun run automation/build/esbuild.dev.config.ts",
|
||||
"build": "bun run tsc && bun run automation/build/esbuild.config.ts",
|
||||
"tsc": "tsc -noEmit -skipLibCheck",
|
||||
"test": "bun test",
|
||||
"test:log": "LOG_TESTS=true bun test",
|
||||
"format": "prettier --write --plugin prettier-plugin-svelte .",
|
||||
"format:check": "prettier --check --plugin prettier-plugin-svelte .",
|
||||
"lint": "eslint --max-warnings=0 src/**",
|
||||
"lint:fix": "eslint --max-warnings=0 --fix src/**",
|
||||
"svelte-check": "svelte-check --compiler-warnings \"unused-export-let:ignore\"",
|
||||
"check": "bun run format:check && bun run tsc && bun run lint && bun run test",
|
||||
"check:fix": "bun run format && bun run tsc && bun run lint:fix && bun run test",
|
||||
"release": "bun run automation/release.ts",
|
||||
"stats": "bun run automation/stats.ts"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "Moritz Jung",
|
||||
"license": "GPL-3.0",
|
||||
"devDependencies": {
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"@tsconfig/svelte": "^5.0.2",
|
||||
"@types/jest": "^29.5.11",
|
||||
"@types/node": "^20.10.4",
|
||||
"@typescript-eslint/eslint-plugin": "^6.14.0",
|
||||
"@typescript-eslint/parser": "^6.14.0",
|
||||
"@lemons_dev/parsinom": "^0.0.12",
|
||||
"@happy-dom/global-registrator": "^14.3.6",
|
||||
"@tsconfig/svelte": "^5.0.3",
|
||||
"@types/bun": "^1.0.10",
|
||||
"@typescript-eslint/eslint-plugin": "^7.3.1",
|
||||
"@typescript-eslint/parser": "^7.3.1",
|
||||
"builtin-modules": "^3.3.0",
|
||||
"esbuild": "^0.19.9",
|
||||
"esbuild": "^0.20.2",
|
||||
"esbuild-plugin-copy-watch": "^2.1.0",
|
||||
"esbuild-svelte": "^0.8.0",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-import": "^2.29.1",
|
||||
"eslint-plugin-isaacscript": "^3.12.2",
|
||||
"eslint-plugin-only-warn": "^1.1.0",
|
||||
"jest": "^29.7.0",
|
||||
"jest-fetch-mock": "^3.0.3",
|
||||
"obsidian": "^1.4.11",
|
||||
"prettier": "3.1.1",
|
||||
"svelte": "^4.2.8",
|
||||
"svelte-preprocess": "^5.1.2",
|
||||
"ts-jest": "^29.1.1",
|
||||
"tslib": "2.6.2",
|
||||
"typescript": "^5.3.3"
|
||||
"obsidian": "latest",
|
||||
"prettier": "^3.2.5",
|
||||
"prettier-plugin-svelte": "^3.2.2",
|
||||
"string-argv": "^0.3.2",
|
||||
"svelte": "^4.2.12",
|
||||
"svelte-check": "^3.6.8",
|
||||
"svelte-preprocess": "^5.1.3",
|
||||
"tslib": "^2.6.2",
|
||||
"typescript": "^5.4.3"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,8 +21,12 @@ export class APIManager {
|
|||
|
||||
for (const api of this.apis) {
|
||||
if (apisToQuery.contains(api.apiName)) {
|
||||
const apiRes = await api.searchByTitle(query);
|
||||
res = res.concat(apiRes);
|
||||
try {
|
||||
const apiRes = await api.searchByTitle(query);
|
||||
res = res.concat(apiRes);
|
||||
} catch (e) {
|
||||
console.warn(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { MediaTypeModel } from '../models/MediaTypeModel';
|
||||
import { MediaType } from '../utils/MediaType';
|
||||
import { MediaDbPluginSettings } from 'src/settings/Settings';
|
||||
import MediaDbPlugin from '../main';
|
||||
|
||||
export abstract class APIModel {
|
||||
|
|
@ -20,7 +19,10 @@ export abstract class APIModel {
|
|||
abstract getById(id: string): Promise<MediaTypeModel>;
|
||||
|
||||
hasType(type: MediaType): boolean {
|
||||
if (this.types.contains(type) && (Boolean((this.plugin.settings.apiToggle as any)?.[this.apiName]?.[type] as MediaDbPluginSettings) === true || (this.plugin.settings.apiToggle as any)?.[this.apiName]?.[type] as MediaDbPluginSettings === undefined)) {
|
||||
if (
|
||||
this.types.contains(type) &&
|
||||
(Boolean((this.plugin.settings.apiToggle as any)?.[this.apiName]?.[type]) === true || (this.plugin.settings.apiToggle as any)?.[this.apiName]?.[type] === undefined)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,64 +0,0 @@
|
|||
import { APIModel } from '../APIModel';
|
||||
import { MediaTypeModel } from '../../models/MediaTypeModel';
|
||||
import MediaDbPlugin from '../../main';
|
||||
|
||||
// WIP
|
||||
export class LocGovAPI extends APIModel {
|
||||
plugin: MediaDbPlugin;
|
||||
typeMappings: Map<string, string>;
|
||||
|
||||
constructor(plugin: MediaDbPlugin) {
|
||||
super();
|
||||
|
||||
this.plugin = plugin;
|
||||
this.apiName = 'loc.gov API';
|
||||
this.apiDescription = 'A free API for the Library of Congress collections.';
|
||||
this.apiUrl = 'https://libraryofcongress.github.io/data-exploration/index.html';
|
||||
this.types = [];
|
||||
this.typeMappings = new Map<string, string>();
|
||||
// this.typeMappings.set('movie', 'movie');
|
||||
}
|
||||
|
||||
async searchByTitle(title: string): Promise<MediaTypeModel[]> {
|
||||
console.log(`MDB | api "${this.apiName}" queried by Title`);
|
||||
|
||||
const searchUrl = `https://www.loc.gov/search/?q=${encodeURIComponent(title)}&fo=json&c=20`;
|
||||
const fetchData = await fetch(searchUrl);
|
||||
console.debug(fetchData);
|
||||
|
||||
if (fetchData.status !== 200) {
|
||||
throw Error(`MDB | Received status code ${fetchData.status} from an API.`);
|
||||
}
|
||||
|
||||
const data = await fetchData.json();
|
||||
console.debug(data);
|
||||
let ret: MediaTypeModel[] = [];
|
||||
|
||||
throw new Error('MDB | Under construction, API implementation not finished');
|
||||
|
||||
// return ret;
|
||||
}
|
||||
|
||||
async getById(id: string): Promise<MediaTypeModel> {
|
||||
console.log(`MDB | api "${this.apiName}" queried by ID`);
|
||||
|
||||
const searchUrl = `https://www.loc.gov/item/${encodeURIComponent(id)}/?fo=json`;
|
||||
const fetchData = await fetch(searchUrl);
|
||||
if (fetchData.status !== 200) {
|
||||
throw Error(`MDB | Received status code ${fetchData.status} from an API.`);
|
||||
}
|
||||
|
||||
const data = await fetchData.json();
|
||||
console.debug(data);
|
||||
const result = data.data;
|
||||
|
||||
const type = this.typeMappings.get(result.type.toLowerCase());
|
||||
if (type === undefined) {
|
||||
throw Error(`${result.type.toLowerCase()} is an unsupported type.`);
|
||||
}
|
||||
|
||||
throw new Error('MDB | Under construction, API implementation not finished');
|
||||
|
||||
// return;
|
||||
}
|
||||
}
|
||||
|
|
@ -41,7 +41,13 @@ export class SteamAPI extends APIModel {
|
|||
const filteredData = [];
|
||||
|
||||
for (const app of data.applist.apps) {
|
||||
if (app.name.toLowerCase().includes(title.toLowerCase())) {
|
||||
if (
|
||||
app.name
|
||||
.normalize('NFD')
|
||||
.replace(/\p{Diacritic}/gu, '')
|
||||
.toLowerCase()
|
||||
.includes(title.toLowerCase())
|
||||
) {
|
||||
filteredData.push(app);
|
||||
}
|
||||
if (filteredData.length > 20) {
|
||||
|
|
|
|||
105
src/main.ts
105
src/main.ts
|
|
@ -22,11 +22,19 @@ import { BoardGameGeekAPI } from './api/apis/BoardGameGeekAPI';
|
|||
import { OpenLibraryAPI } from './api/apis/OpenLibraryAPI';
|
||||
import { MobyGamesAPI } from './api/apis/MobyGamesAPI';
|
||||
import { PropertyMapper } from './settings/PropertyMapper';
|
||||
import { YAMLConverter } from './utils/YAMLConverter';
|
||||
import { MediaDbFolderImportModal } from './modals/MediaDbFolderImportModal';
|
||||
import { PropertyMapping, PropertyMappingModel } from './settings/PropertyMapping';
|
||||
import { ModalHelper, ModalResultCode, SearchModalOptions } from './utils/ModalHelper';
|
||||
import { DateFormatter } from './utils/DateFormatter';
|
||||
import { MediaType } from 'src/utils/MediaType';
|
||||
|
||||
export type Metadata = Record<string, unknown>;
|
||||
|
||||
export interface MediaTypeModelObj {
|
||||
id: string;
|
||||
type: MediaType;
|
||||
dataSource: string;
|
||||
}
|
||||
|
||||
export default class MediaDbPlugin extends Plugin {
|
||||
settings: MediaDbPluginSettings;
|
||||
|
|
@ -186,7 +194,12 @@ export default class MediaDbPlugin extends Plugin {
|
|||
let apiSearchResults: MediaTypeModel[] = await this.modalHelper.openSearchModal(searchModalOptions ?? {}, async searchModalData => {
|
||||
types = searchModalData.types;
|
||||
const apis = this.apiManager.apis.filter(x => x.hasTypeOverlap(searchModalData.types)).map(x => x.apiName);
|
||||
return await this.apiManager.query(searchModalData.query, apis);
|
||||
try {
|
||||
return await this.apiManager.query(searchModalData.query, apis);
|
||||
} catch (e) {
|
||||
console.warn(e);
|
||||
return [];
|
||||
}
|
||||
});
|
||||
|
||||
if (!apiSearchResults) {
|
||||
|
|
@ -309,21 +322,40 @@ export default class MediaDbPlugin extends Plugin {
|
|||
|
||||
generateMediaDbNoteFrontmatterPreview(mediaTypeModel: MediaTypeModel): string {
|
||||
const fileMetadata = this.modelPropertyMapper.convertObject(mediaTypeModel.toMetaDataObject());
|
||||
return this.settings.useCustomYamlStringifier ? YAMLConverter.toYaml(fileMetadata) : stringifyYaml(fileMetadata);
|
||||
return stringifyYaml(fileMetadata);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the content of a note from a media model and some options.
|
||||
*
|
||||
* @param mediaTypeModel
|
||||
* @param options
|
||||
*/
|
||||
async generateMediaDbNoteContents(mediaTypeModel: MediaTypeModel, options: CreateNoteOptions): Promise<string> {
|
||||
let template = await this.mediaTypeManager.getTemplate(mediaTypeModel, this.app);
|
||||
const template = await this.mediaTypeManager.getTemplate(mediaTypeModel, this.app);
|
||||
|
||||
if (this.settings.useDefaultFrontMatter || !template) {
|
||||
return this.generateContentWithDefaultFrontMatter(mediaTypeModel, options, template);
|
||||
} else {
|
||||
return this.generateContentWithCustomFrontMatter(mediaTypeModel, options, template);
|
||||
}
|
||||
return this.generateContentWithDefaultFrontMatter(mediaTypeModel, options, template);
|
||||
|
||||
// if (this.settings.useDefaultFrontMatter || !template) {
|
||||
// return this.generateContentWithDefaultFrontMatter(mediaTypeModel, options, template);
|
||||
// } else {
|
||||
// return this.generateContentWithCustomFrontMatter(mediaTypeModel, options, template);
|
||||
// }
|
||||
}
|
||||
|
||||
async generateContentWithDefaultFrontMatter(mediaTypeModel: MediaTypeModel, options: CreateNoteOptions, template?: string): Promise<string> {
|
||||
let fileMetadata = this.modelPropertyMapper.convertObject(mediaTypeModel.toMetaDataObject());
|
||||
let fileMetadata: Record<string, unknown>;
|
||||
|
||||
if (this.settings.useDefaultFrontMatter) {
|
||||
fileMetadata = this.modelPropertyMapper.convertObject(mediaTypeModel.toMetaDataObject());
|
||||
} else {
|
||||
fileMetadata = {
|
||||
id: mediaTypeModel.id,
|
||||
type: mediaTypeModel.type,
|
||||
dataSource: mediaTypeModel.dataSource,
|
||||
};
|
||||
}
|
||||
|
||||
let fileContent = '';
|
||||
template = options.attachTemplate ? template : '';
|
||||
|
||||
|
|
@ -331,27 +363,20 @@ export default class MediaDbPlugin extends Plugin {
|
|||
({ fileMetadata, fileContent } = await this.attachTemplate(fileMetadata, fileContent, template));
|
||||
|
||||
if (this.settings.enableTemplaterIntegration && hasTemplaterPlugin(this.app)) {
|
||||
// Only support stringifyYaml for templater plugin
|
||||
// Include the media variable in all templater commands by using a top level JavaScript execution command.
|
||||
fileContent = `---\n<%* const media = ${JSON.stringify(mediaTypeModel)} %>\n${stringifyYaml(fileMetadata)}---\n${fileContent}`;
|
||||
} else {
|
||||
fileContent = `---\n${this.settings.useCustomYamlStringifier ? YAMLConverter.toYaml(fileMetadata) : stringifyYaml(fileMetadata)}---\n` + fileContent;
|
||||
fileContent = `---\n${stringifyYaml(fileMetadata)}---\n${fileContent}`;
|
||||
}
|
||||
|
||||
return fileContent;
|
||||
}
|
||||
|
||||
async generateContentWithCustomFrontMatter(mediaTypeModel: MediaTypeModel, options: CreateNoteOptions, template: string): Promise<string> {
|
||||
const frontMatterRegex = /^---*\n([\s\S]*?)\n---\h*/;
|
||||
const regExp = new RegExp(this.frontMatterRexExpPattern);
|
||||
|
||||
const match = template.match(frontMatterRegex);
|
||||
|
||||
if (!match || match.length !== 2) {
|
||||
throw new Error('Cannot find YAML front matter for template.');
|
||||
}
|
||||
|
||||
let frontMatter = parseYaml(match[1]);
|
||||
let fileContent: string = template.replace(frontMatterRegex, '');
|
||||
const frontMatter = this.getMetaDataFromFileContent(template);
|
||||
let fileContent: string = template.replace(regExp, '');
|
||||
|
||||
// Updating a previous file
|
||||
if (options.attachFile) {
|
||||
|
|
@ -359,7 +384,7 @@ export default class MediaDbPlugin extends Plugin {
|
|||
|
||||
// Use contents (below front matter) from previous file
|
||||
fileContent = await this.app.vault.read(options.attachFile);
|
||||
const regExp = new RegExp(this.frontMatterRexExpPattern);
|
||||
|
||||
fileContent = fileContent.replace(regExp, '');
|
||||
fileContent = fileContent.startsWith('\n') ? fileContent.substring(1) : fileContent;
|
||||
|
||||
|
|
@ -391,18 +416,19 @@ export default class MediaDbPlugin extends Plugin {
|
|||
// Include the media variable in all templater commands by using a top level JavaScript execution command.
|
||||
fileContent = `---\n<%* const media = ${JSON.stringify(mediaTypeModel)} %>\n${stringifyYaml(frontMatter)}---\n${fileContent}`;
|
||||
} else {
|
||||
fileContent = `---\n${this.settings.useCustomYamlStringifier ? YAMLConverter.toYaml(frontMatter) : stringifyYaml(frontMatter)}---\n` + fileContent;
|
||||
fileContent = `---\n${stringifyYaml(frontMatter)}---\n${fileContent}`;
|
||||
}
|
||||
|
||||
return fileContent;
|
||||
}
|
||||
|
||||
async attachFile(fileMetadata: any, fileContent: string, fileToAttach?: TFile): Promise<{ fileMetadata: any; fileContent: string }> {
|
||||
async attachFile(fileMetadata: Metadata, fileContent: string, fileToAttach?: TFile): Promise<{ fileMetadata: Metadata; fileContent: string }> {
|
||||
if (!fileToAttach) {
|
||||
return { fileMetadata: fileMetadata, fileContent: fileContent };
|
||||
}
|
||||
|
||||
const attachFileMetadata: any = this.getMetadataFromFileCache(fileToAttach);
|
||||
// TODO: better object merging
|
||||
fileMetadata = Object.assign(attachFileMetadata, fileMetadata);
|
||||
|
||||
let attachFileContent: string = await this.app.vault.read(fileToAttach);
|
||||
|
|
@ -414,12 +440,13 @@ export default class MediaDbPlugin extends Plugin {
|
|||
return { fileMetadata: fileMetadata, fileContent: fileContent };
|
||||
}
|
||||
|
||||
async attachTemplate(fileMetadata: any, fileContent: string, template: string): Promise<{ fileMetadata: any; fileContent: string }> {
|
||||
async attachTemplate(fileMetadata: Metadata, fileContent: string, template: string): Promise<{ fileMetadata: Metadata; fileContent: string }> {
|
||||
if (!template) {
|
||||
return { fileMetadata: fileMetadata, fileContent: fileContent };
|
||||
}
|
||||
|
||||
const templateMetadata: any = this.getMetaDataFromFileContent(template);
|
||||
const templateMetadata: Metadata = this.getMetaDataFromFileContent(template);
|
||||
// TODO: better object merging
|
||||
fileMetadata = Object.assign(templateMetadata, fileMetadata);
|
||||
|
||||
const regExp = new RegExp(this.frontMatterRexExpPattern);
|
||||
|
|
@ -429,8 +456,8 @@ export default class MediaDbPlugin extends Plugin {
|
|||
return { fileMetadata: fileMetadata, fileContent: fileContent };
|
||||
}
|
||||
|
||||
getMetaDataFromFileContent(fileContent: string): any {
|
||||
let metadata: any;
|
||||
getMetaDataFromFileContent(fileContent: string): Metadata {
|
||||
let metadata: Metadata;
|
||||
|
||||
const regExp = new RegExp(this.frontMatterRexExpPattern);
|
||||
const frontMatterRegExpResult = regExp.exec(fileContent);
|
||||
|
|
@ -455,15 +482,9 @@ export default class MediaDbPlugin extends Plugin {
|
|||
return metadata;
|
||||
}
|
||||
|
||||
getMetadataFromFileCache(file: TFile): any {
|
||||
let metadata: any = this.app.metadataCache.getFileCache(file).frontmatter;
|
||||
if (metadata) {
|
||||
metadata = Object.assign({}, metadata); // copy
|
||||
delete metadata.position;
|
||||
} else {
|
||||
metadata = {};
|
||||
}
|
||||
return metadata;
|
||||
getMetadataFromFileCache(file: TFile): Metadata {
|
||||
const metadata: Metadata | undefined = this.app.metadataCache.getFileCache(file).frontmatter;
|
||||
return structuredClone(metadata ?? {});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -513,7 +534,7 @@ export default class MediaDbPlugin extends Plugin {
|
|||
throw new Error('MDB | there is no active note');
|
||||
}
|
||||
|
||||
let metadata: any = this.getMetadataFromFileCache(activeFile);
|
||||
let metadata = this.getMetadataFromFileCache(activeFile);
|
||||
metadata = this.modelPropertyMapper.convertObjectBack(metadata);
|
||||
|
||||
console.debug(`MDB | read metadata`, metadata);
|
||||
|
|
@ -522,10 +543,12 @@ export default class MediaDbPlugin extends Plugin {
|
|||
throw new Error('MDB | active note is not a Media DB entry or is missing metadata');
|
||||
}
|
||||
|
||||
const oldMediaTypeModel = this.mediaTypeManager.createMediaTypeModelFromMediaType(metadata, metadata.type);
|
||||
const validOldMetadata: MediaTypeModelObj = metadata as unknown as MediaTypeModelObj;
|
||||
|
||||
const oldMediaTypeModel = this.mediaTypeManager.createMediaTypeModelFromMediaType(validOldMetadata, validOldMetadata.type);
|
||||
// console.debug(oldMediaTypeModel);
|
||||
|
||||
let newMediaTypeModel = await this.apiManager.queryDetailedInfoById(metadata.id, metadata.dataSource);
|
||||
let newMediaTypeModel = await this.apiManager.queryDetailedInfoById(validOldMetadata.id, validOldMetadata.dataSource);
|
||||
if (!newMediaTypeModel) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -533,8 +556,6 @@ export default class MediaDbPlugin extends Plugin {
|
|||
newMediaTypeModel = Object.assign(oldMediaTypeModel, newMediaTypeModel.getWithOutUserData());
|
||||
// console.debug(newMediaTypeModel);
|
||||
|
||||
// deletion not happening anymore why is this log statement still here
|
||||
console.debug('MDB | deleting old entry');
|
||||
if (onlyMetadata) {
|
||||
await this.createMediaDbNoteFromModel(newMediaTypeModel, { attachFile: activeFile, folder: activeFile.parent, openNote: true });
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -31,12 +31,12 @@ export abstract class MediaTypeModel {
|
|||
|
||||
abstract getTags(): string[];
|
||||
|
||||
toMetaDataObject(): object {
|
||||
toMetaDataObject(): Record<string, unknown> {
|
||||
return { ...this.getWithOutUserData(), ...this.userData, tags: this.getTags().join('/') };
|
||||
}
|
||||
|
||||
getWithOutUserData(): object {
|
||||
const copy = Object.assign({}, this);
|
||||
getWithOutUserData(): Record<string, unknown> {
|
||||
const copy = structuredClone(this) as Record<string, unknown>;
|
||||
delete copy.userData;
|
||||
return copy;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ export class WikiModel extends MediaTypeModel {
|
|||
length: number;
|
||||
article: string;
|
||||
|
||||
userData: {};
|
||||
userData: Record<string, unknown>;
|
||||
|
||||
constructor(obj: any = {}) {
|
||||
super();
|
||||
|
|
@ -45,8 +45,8 @@ export class WikiModel extends MediaTypeModel {
|
|||
return MediaType.Wiki;
|
||||
}
|
||||
|
||||
override getWithOutUserData(): object {
|
||||
const copy = Object.assign({}, this);
|
||||
override getWithOutUserData(): Record<string, unknown> {
|
||||
const copy = structuredClone(this) as Record<string, unknown>;
|
||||
delete copy.userData;
|
||||
delete copy.article;
|
||||
return copy;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
<!--adapted from @joethei's code: https://github.com/joethei/obsidian-rss/blob/master/src/view/IconComponent.svelte-->
|
||||
<!--adapted from @javalent's code: https://discord.com/channels/686053708261228577/840286264964022302/902949764209987654-->
|
||||
<script lang="ts">
|
||||
import {setIcon} from 'obsidian';
|
||||
import {onMount} from 'svelte';
|
||||
import { setIcon } from 'obsidian';
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
export let iconName: string = '';
|
||||
export let iconSize: number = 20;
|
||||
|
|
@ -14,23 +14,23 @@
|
|||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.icon-wrapper {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
.icon {
|
||||
position: absolute;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
top: calc(50% - 10px);
|
||||
}
|
||||
</style>
|
||||
|
||||
{#if iconName.length > 0}
|
||||
<div class="icon-wrapper">
|
||||
<div bind:this={iconEl} class="icon"></div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.icon-wrapper {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
.icon {
|
||||
position: absolute;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
top: calc(50% - 10px);
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -15,15 +15,13 @@ export class PropertyMapper {
|
|||
*
|
||||
* @param obj
|
||||
*/
|
||||
convertObject(obj: object): object {
|
||||
convertObject(obj: Record<string, unknown>): Record<string, unknown> {
|
||||
if (!obj.hasOwnProperty('type')) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
// console.log(obj.type);
|
||||
|
||||
// @ts-ignore
|
||||
if (MEDIA_TYPES.filter(x => x.toString() == obj.type).length < 1) {
|
||||
return obj;
|
||||
}
|
||||
|
|
@ -31,7 +29,7 @@ export class PropertyMapper {
|
|||
// @ts-ignore
|
||||
const propertyMappings = this.plugin.settings.propertyMappingModels.find(x => x.type === obj.type).properties;
|
||||
|
||||
const newObj: object = {};
|
||||
const newObj: Record<string, unknown> = {};
|
||||
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
for (const propertyMapping of propertyMappings) {
|
||||
|
|
@ -59,20 +57,18 @@ export class PropertyMapper {
|
|||
*
|
||||
* @param obj
|
||||
*/
|
||||
convertObjectBack(obj: object): object {
|
||||
convertObjectBack(obj: Record<string, unknown>): Record<string, unknown> {
|
||||
if (!obj.hasOwnProperty('type')) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
if (MEDIA_TYPES.contains(obj.type)) {
|
||||
if (MEDIA_TYPES.contains(obj.type as any)) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
const propertyMappings = this.plugin.settings.propertyMappingModels.find(x => x.type === obj.type).properties;
|
||||
|
||||
const originalObj: object = {};
|
||||
const originalObj: Record<string, unknown> = {};
|
||||
|
||||
objLoop: for (const [key, value] of Object.entries(obj)) {
|
||||
// first try if it is a normal property
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ export class PropertyMappingModel {
|
|||
};
|
||||
}
|
||||
|
||||
getMappedProperties() {
|
||||
getMappedProperties(): PropertyMapping[] {
|
||||
return this.properties.filter(x => x.mapping === PropertyMappingOption.Map);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
<script lang="ts">
|
||||
import {PropertyMappingModel, PropertyMappingOption, propertyMappingOptions} from './PropertyMapping';
|
||||
import {capitalizeFirstLetter} from '../utils/Utils';
|
||||
import { PropertyMappingModel, PropertyMappingOption, propertyMappingOptions } from './PropertyMapping';
|
||||
import { capitalizeFirstLetter } from '../utils/Utils';
|
||||
import Icon from './Icon.svelte';
|
||||
|
||||
export let model: PropertyMappingModel;
|
||||
export let save: (model: PropertyMappingModel) => void;
|
||||
|
||||
let validationResult: { res: boolean, err?: Error };
|
||||
let validationResult: { res: boolean; err?: Error };
|
||||
|
||||
$: modelChanged(model);
|
||||
|
||||
|
|
@ -15,23 +15,17 @@
|
|||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
|
||||
<div class="media-db-plugin-property-mappings-model-container">
|
||||
<div class="setting-item-name">{capitalizeFirstLetter(model.type)}</div>
|
||||
<div class="media-db-plugin-property-mappings-container">
|
||||
{ #each model.properties as property }
|
||||
{#each model.properties as property}
|
||||
<div class="media-db-plugin-property-mapping-element">
|
||||
<div class="media-db-plugin-property-mapping-element-property-name-wrapper">
|
||||
<pre class="media-db-plugin-property-mapping-element-property-name"><code>{property.property}</code></pre>
|
||||
</div>
|
||||
{ #if property.locked }
|
||||
<div class="media-db-plugin-property-binding-text">
|
||||
property can not be remapped
|
||||
</div>
|
||||
{ :else }
|
||||
{#if property.locked}
|
||||
<div class="media-db-plugin-property-binding-text">property can not be remapped</div>
|
||||
{:else}
|
||||
<select class="dropdown" bind:value={property.mapping}>
|
||||
{#each propertyMappingOptions as remappingOption}
|
||||
<option value={remappingOption}>
|
||||
|
|
@ -40,23 +34,29 @@
|
|||
{/each}
|
||||
</select>
|
||||
|
||||
{ #if property.mapping === PropertyMappingOption.Map }
|
||||
<Icon iconName="arrow-right"/>
|
||||
{#if property.mapping === PropertyMappingOption.Map}
|
||||
<Icon iconName="arrow-right" />
|
||||
<div class="media-db-plugin-property-mapping-to">
|
||||
<input type="text" spellcheck="false" bind:value="{property.newProperty}">
|
||||
<input type="text" spellcheck="false" bind:value={property.newProperty} />
|
||||
</div>
|
||||
{ /if }
|
||||
{ /if }
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
{ /each }
|
||||
{/each}
|
||||
</div>
|
||||
{ #if !validationResult?.res }
|
||||
{#if !validationResult?.res}
|
||||
<div class="media-db-plugin-property-mapping-validation">
|
||||
{validationResult?.err?.message}
|
||||
</div>
|
||||
{ /if }
|
||||
{/if}
|
||||
<button
|
||||
class="media-db-plugin-property-mappings-save-button {validationResult?.res ? 'mod-cta' : 'mod-muted'}"
|
||||
on:click={() => { if(model.validate().res) save(model) }}>Save
|
||||
on:click={() => {
|
||||
if (model.validate().res) save(model);
|
||||
}}
|
||||
>Save
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,19 +1,15 @@
|
|||
<script lang="ts">
|
||||
import {PropertyMappingModel} from './PropertyMapping';
|
||||
import { PropertyMappingModel } from './PropertyMapping';
|
||||
import PropertyMappingModelComponent from './PropertyMappingModelComponent.svelte';
|
||||
|
||||
export let models: PropertyMappingModel[] = [];
|
||||
export let save: (model: PropertyMappingModel) => void;
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
|
||||
<div class="setting-item" style="display: flex; gap: 10px; flex-direction: column; align-items: stretch;">
|
||||
{ #each models as model }
|
||||
<PropertyMappingModelComponent model={model} save={save}></PropertyMappingModelComponent>
|
||||
{ /each }
|
||||
{#each models as model}
|
||||
<PropertyMappingModelComponent {model} {save}></PropertyMappingModelComponent>
|
||||
{/each}
|
||||
|
||||
<!--
|
||||
<pre>{JSON.stringify(models, null, 4)}</pre>
|
||||
|
|
@ -25,3 +21,6 @@
|
|||
{/each}
|
||||
-->
|
||||
</div>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ export interface MediaDbPluginSettings {
|
|||
OMDbKey: string;
|
||||
MobyGamesKey: string;
|
||||
sfwFilter: boolean;
|
||||
useCustomYamlStringifier: boolean;
|
||||
templates: boolean;
|
||||
customDateFormat: string;
|
||||
openNoteInNewTab: boolean;
|
||||
|
|
@ -24,18 +23,18 @@ export interface MediaDbPluginSettings {
|
|||
movie: boolean;
|
||||
series: boolean;
|
||||
game: boolean;
|
||||
},
|
||||
};
|
||||
MALAPI: {
|
||||
movie: boolean;
|
||||
series: boolean;
|
||||
},
|
||||
};
|
||||
SteamAPI: {
|
||||
game: boolean;
|
||||
},
|
||||
};
|
||||
MobyGamesAPI: {
|
||||
game: boolean;
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
movieTemplate: string;
|
||||
seriesTemplate: string;
|
||||
mangaTemplate: string;
|
||||
|
|
@ -79,7 +78,6 @@ const DEFAULT_SETTINGS: MediaDbPluginSettings = {
|
|||
OMDbKey: '',
|
||||
MobyGamesKey: '',
|
||||
sfwFilter: true,
|
||||
useCustomYamlStringifier: true,
|
||||
templates: true,
|
||||
customDateFormat: 'L',
|
||||
openNoteInNewTab: true,
|
||||
|
|
@ -100,7 +98,7 @@ const DEFAULT_SETTINGS: MediaDbPluginSettings = {
|
|||
},
|
||||
MobyGamesAPI: {
|
||||
game: true,
|
||||
}
|
||||
},
|
||||
},
|
||||
movieTemplate: '',
|
||||
seriesTemplate: '',
|
||||
|
|
@ -190,7 +188,7 @@ export class MediaDbSettingTab extends PluginSettingTab {
|
|||
.setValue(this.plugin.settings.OMDbKey)
|
||||
.onChange(data => {
|
||||
this.plugin.settings.OMDbKey = data;
|
||||
this.plugin.saveSettings();
|
||||
void this.plugin.saveSettings();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -202,7 +200,7 @@ export class MediaDbSettingTab extends PluginSettingTab {
|
|||
.setValue(this.plugin.settings.MobyGamesKey)
|
||||
.onChange(data => {
|
||||
this.plugin.settings.MobyGamesKey = data;
|
||||
this.plugin.saveSettings();
|
||||
void this.plugin.saveSettings();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -212,17 +210,7 @@ export class MediaDbSettingTab extends PluginSettingTab {
|
|||
.addToggle(cb => {
|
||||
cb.setValue(this.plugin.settings.sfwFilter).onChange(data => {
|
||||
this.plugin.settings.sfwFilter = data;
|
||||
this.plugin.saveSettings();
|
||||
});
|
||||
});
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('YAML formatter')
|
||||
.setDesc('Add optional quotation marks around strings in the metadata block.')
|
||||
.addToggle(cb => {
|
||||
cb.setValue(this.plugin.settings.useCustomYamlStringifier).onChange(data => {
|
||||
this.plugin.settings.useCustomYamlStringifier = data;
|
||||
this.plugin.saveSettings();
|
||||
void this.plugin.saveSettings();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -232,7 +220,7 @@ export class MediaDbSettingTab extends PluginSettingTab {
|
|||
.addToggle(cb => {
|
||||
cb.setValue(this.plugin.settings.templates).onChange(data => {
|
||||
this.plugin.settings.templates = data;
|
||||
this.plugin.saveSettings();
|
||||
void this.plugin.saveSettings();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -241,10 +229,10 @@ export class MediaDbSettingTab extends PluginSettingTab {
|
|||
.setDesc(
|
||||
fragWithHTML(
|
||||
"Your custom date format. Use <em>'YYYY-MM-DD'</em> for example.<br>" +
|
||||
"For more syntax, refer to <a href='https://momentjs.com/docs/#/displaying/format/'>format reference</a>.<br>" +
|
||||
"Your current syntax looks like this: <b><a id='media-db-dateformat-preview' style='pointer-events: none; cursor: default; text-decoration: none;'>" +
|
||||
this.plugin.dateFormatter.getPreview() +
|
||||
'</a></b>',
|
||||
"For more syntax, refer to <a href='https://momentjs.com/docs/#/displaying/format/'>format reference</a>.<br>" +
|
||||
"Your current syntax looks like this: <b><a id='media-db-dateformat-preview' style='pointer-events: none; cursor: default; text-decoration: none;'>" +
|
||||
this.plugin.dateFormatter.getPreview() +
|
||||
'</a></b>',
|
||||
),
|
||||
)
|
||||
.addText(cb => {
|
||||
|
|
@ -254,7 +242,7 @@ export class MediaDbSettingTab extends PluginSettingTab {
|
|||
const newDateFormat = data ? data : DEFAULT_SETTINGS.customDateFormat;
|
||||
this.plugin.settings.customDateFormat = newDateFormat;
|
||||
document.getElementById('media-db-dateformat-preview').textContent = this.plugin.dateFormatter.getPreview(newDateFormat); // update preview
|
||||
this.plugin.saveSettings();
|
||||
void this.plugin.saveSettings();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -264,17 +252,17 @@ export class MediaDbSettingTab extends PluginSettingTab {
|
|||
.addToggle(cb => {
|
||||
cb.setValue(this.plugin.settings.openNoteInNewTab).onChange(data => {
|
||||
this.plugin.settings.openNoteInNewTab = data;
|
||||
this.plugin.saveSettings();
|
||||
void this.plugin.saveSettings();
|
||||
});
|
||||
});
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Use default front matter')
|
||||
.setDesc('Wheter to use the default front matter. If disabled, the front matter from the template will be used. Same as mapping everything to remove.')
|
||||
.setDesc('Whether to use the default front matter. If disabled, the front matter from the template will be used. Same as mapping everything to remove.')
|
||||
.addToggle(cb => {
|
||||
cb.setValue(this.plugin.settings.useDefaultFrontMatter).onChange(data => {
|
||||
this.plugin.settings.useDefaultFrontMatter = data;
|
||||
this.plugin.saveSettings();
|
||||
void this.plugin.saveSettings();
|
||||
// Redraw settings to display/remove the property mappings
|
||||
this.display();
|
||||
});
|
||||
|
|
@ -288,11 +276,10 @@ export class MediaDbSettingTab extends PluginSettingTab {
|
|||
.addToggle(cb => {
|
||||
cb.setValue(this.plugin.settings.enableTemplaterIntegration).onChange(data => {
|
||||
this.plugin.settings.enableTemplaterIntegration = data;
|
||||
this.plugin.saveSettings();
|
||||
void this.plugin.saveSettings();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
containerEl.createEl('h3', { text: 'APIs Per Media Type' });
|
||||
containerEl.createEl('h5', { text: 'Movies' });
|
||||
new Setting(containerEl)
|
||||
|
|
@ -301,7 +288,7 @@ export class MediaDbSettingTab extends PluginSettingTab {
|
|||
.addToggle(cb => {
|
||||
cb.setValue(this.plugin.settings.apiToggle.OMDbAPI.movie).onChange(data => {
|
||||
this.plugin.settings.apiToggle.OMDbAPI.movie = data;
|
||||
this.plugin.saveSettings();
|
||||
void this.plugin.saveSettings();
|
||||
});
|
||||
});
|
||||
new Setting(containerEl)
|
||||
|
|
@ -310,7 +297,7 @@ export class MediaDbSettingTab extends PluginSettingTab {
|
|||
.addToggle(cb => {
|
||||
cb.setValue(this.plugin.settings.apiToggle.MALAPI.movie).onChange(data => {
|
||||
this.plugin.settings.apiToggle.MALAPI.movie = data;
|
||||
this.plugin.saveSettings();
|
||||
void this.plugin.saveSettings();
|
||||
});
|
||||
});
|
||||
containerEl.createEl('h5', { text: 'Series' });
|
||||
|
|
@ -320,7 +307,7 @@ export class MediaDbSettingTab extends PluginSettingTab {
|
|||
.addToggle(cb => {
|
||||
cb.setValue(this.plugin.settings.apiToggle.OMDbAPI.series).onChange(data => {
|
||||
this.plugin.settings.apiToggle.OMDbAPI.series = data;
|
||||
this.plugin.saveSettings();
|
||||
void this.plugin.saveSettings();
|
||||
});
|
||||
});
|
||||
new Setting(containerEl)
|
||||
|
|
@ -329,7 +316,7 @@ export class MediaDbSettingTab extends PluginSettingTab {
|
|||
.addToggle(cb => {
|
||||
cb.setValue(this.plugin.settings.apiToggle.MALAPI.series).onChange(data => {
|
||||
this.plugin.settings.apiToggle.MALAPI.series = data;
|
||||
this.plugin.saveSettings();
|
||||
void this.plugin.saveSettings();
|
||||
});
|
||||
});
|
||||
containerEl.createEl('h5', { text: 'Games' });
|
||||
|
|
@ -339,7 +326,7 @@ export class MediaDbSettingTab extends PluginSettingTab {
|
|||
.addToggle(cb => {
|
||||
cb.setValue(this.plugin.settings.apiToggle.OMDbAPI.game).onChange(data => {
|
||||
this.plugin.settings.apiToggle.OMDbAPI.game = data;
|
||||
this.plugin.saveSettings();
|
||||
void this.plugin.saveSettings();
|
||||
});
|
||||
});
|
||||
new Setting(containerEl)
|
||||
|
|
@ -348,7 +335,7 @@ export class MediaDbSettingTab extends PluginSettingTab {
|
|||
.addToggle(cb => {
|
||||
cb.setValue(this.plugin.settings.apiToggle.SteamAPI.game).onChange(data => {
|
||||
this.plugin.settings.apiToggle.SteamAPI.game = data;
|
||||
this.plugin.saveSettings();
|
||||
void this.plugin.saveSettings();
|
||||
});
|
||||
});
|
||||
new Setting(containerEl)
|
||||
|
|
@ -357,7 +344,7 @@ export class MediaDbSettingTab extends PluginSettingTab {
|
|||
.addToggle(cb => {
|
||||
cb.setValue(this.plugin.settings.apiToggle.MobyGamesAPI.game).onChange(data => {
|
||||
this.plugin.settings.apiToggle.MobyGamesAPI.game = data;
|
||||
this.plugin.saveSettings();
|
||||
void this.plugin.saveSettings();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -372,7 +359,7 @@ export class MediaDbSettingTab extends PluginSettingTab {
|
|||
.setValue(this.plugin.settings.movieFolder)
|
||||
.onChange(data => {
|
||||
this.plugin.settings.movieFolder = data;
|
||||
this.plugin.saveSettings();
|
||||
void this.plugin.saveSettings();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -385,7 +372,7 @@ export class MediaDbSettingTab extends PluginSettingTab {
|
|||
.setValue(this.plugin.settings.seriesFolder)
|
||||
.onChange(data => {
|
||||
this.plugin.settings.seriesFolder = data;
|
||||
this.plugin.saveSettings();
|
||||
void this.plugin.saveSettings();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -398,7 +385,7 @@ export class MediaDbSettingTab extends PluginSettingTab {
|
|||
.setValue(this.plugin.settings.mangaFolder)
|
||||
.onChange(data => {
|
||||
this.plugin.settings.mangaFolder = data;
|
||||
this.plugin.saveSettings();
|
||||
void this.plugin.saveSettings();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -411,7 +398,7 @@ export class MediaDbSettingTab extends PluginSettingTab {
|
|||
.setValue(this.plugin.settings.gameFolder)
|
||||
.onChange(data => {
|
||||
this.plugin.settings.gameFolder = data;
|
||||
this.plugin.saveSettings();
|
||||
void this.plugin.saveSettings();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -424,7 +411,7 @@ export class MediaDbSettingTab extends PluginSettingTab {
|
|||
.setValue(this.plugin.settings.wikiFolder)
|
||||
.onChange(data => {
|
||||
this.plugin.settings.wikiFolder = data;
|
||||
this.plugin.saveSettings();
|
||||
void this.plugin.saveSettings();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -437,7 +424,7 @@ export class MediaDbSettingTab extends PluginSettingTab {
|
|||
.setValue(this.plugin.settings.musicReleaseFolder)
|
||||
.onChange(data => {
|
||||
this.plugin.settings.musicReleaseFolder = data;
|
||||
this.plugin.saveSettings();
|
||||
void this.plugin.saveSettings();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -450,7 +437,7 @@ export class MediaDbSettingTab extends PluginSettingTab {
|
|||
.setValue(this.plugin.settings.boardgameFolder)
|
||||
.onChange(data => {
|
||||
this.plugin.settings.boardgameFolder = data;
|
||||
this.plugin.saveSettings();
|
||||
void this.plugin.saveSettings();
|
||||
});
|
||||
});
|
||||
new Setting(containerEl)
|
||||
|
|
@ -462,7 +449,7 @@ export class MediaDbSettingTab extends PluginSettingTab {
|
|||
.setValue(this.plugin.settings.bookFolder)
|
||||
.onChange(data => {
|
||||
this.plugin.settings.bookFolder = data;
|
||||
this.plugin.saveSettings();
|
||||
void this.plugin.saveSettings();
|
||||
});
|
||||
});
|
||||
// endregion
|
||||
|
|
@ -478,7 +465,7 @@ export class MediaDbSettingTab extends PluginSettingTab {
|
|||
.setValue(this.plugin.settings.movieTemplate)
|
||||
.onChange(data => {
|
||||
this.plugin.settings.movieTemplate = data;
|
||||
this.plugin.saveSettings();
|
||||
void this.plugin.saveSettings();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -491,7 +478,7 @@ export class MediaDbSettingTab extends PluginSettingTab {
|
|||
.setValue(this.plugin.settings.seriesTemplate)
|
||||
.onChange(data => {
|
||||
this.plugin.settings.seriesTemplate = data;
|
||||
this.plugin.saveSettings();
|
||||
void this.plugin.saveSettings();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -504,7 +491,7 @@ export class MediaDbSettingTab extends PluginSettingTab {
|
|||
.setValue(this.plugin.settings.mangaTemplate)
|
||||
.onChange(data => {
|
||||
this.plugin.settings.mangaTemplate = data;
|
||||
this.plugin.saveSettings();
|
||||
void this.plugin.saveSettings();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -517,7 +504,7 @@ export class MediaDbSettingTab extends PluginSettingTab {
|
|||
.setValue(this.plugin.settings.gameTemplate)
|
||||
.onChange(data => {
|
||||
this.plugin.settings.gameTemplate = data;
|
||||
this.plugin.saveSettings();
|
||||
void this.plugin.saveSettings();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -530,7 +517,7 @@ export class MediaDbSettingTab extends PluginSettingTab {
|
|||
.setValue(this.plugin.settings.wikiTemplate)
|
||||
.onChange(data => {
|
||||
this.plugin.settings.wikiTemplate = data;
|
||||
this.plugin.saveSettings();
|
||||
void this.plugin.saveSettings();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -543,7 +530,7 @@ export class MediaDbSettingTab extends PluginSettingTab {
|
|||
.setValue(this.plugin.settings.musicReleaseTemplate)
|
||||
.onChange(data => {
|
||||
this.plugin.settings.musicReleaseTemplate = data;
|
||||
this.plugin.saveSettings();
|
||||
void this.plugin.saveSettings();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -556,7 +543,7 @@ export class MediaDbSettingTab extends PluginSettingTab {
|
|||
.setValue(this.plugin.settings.boardgameTemplate)
|
||||
.onChange(data => {
|
||||
this.plugin.settings.boardgameTemplate = data;
|
||||
this.plugin.saveSettings();
|
||||
void this.plugin.saveSettings();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -569,7 +556,7 @@ export class MediaDbSettingTab extends PluginSettingTab {
|
|||
.setValue(this.plugin.settings.bookTemplate)
|
||||
.onChange(data => {
|
||||
this.plugin.settings.bookTemplate = data;
|
||||
this.plugin.saveSettings();
|
||||
void this.plugin.saveSettings();
|
||||
});
|
||||
});
|
||||
// endregion
|
||||
|
|
@ -584,7 +571,7 @@ export class MediaDbSettingTab extends PluginSettingTab {
|
|||
.setValue(this.plugin.settings.movieFileNameTemplate)
|
||||
.onChange(data => {
|
||||
this.plugin.settings.movieFileNameTemplate = data;
|
||||
this.plugin.saveSettings();
|
||||
void this.plugin.saveSettings();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -596,7 +583,7 @@ export class MediaDbSettingTab extends PluginSettingTab {
|
|||
.setValue(this.plugin.settings.seriesFileNameTemplate)
|
||||
.onChange(data => {
|
||||
this.plugin.settings.seriesFileNameTemplate = data;
|
||||
this.plugin.saveSettings();
|
||||
void this.plugin.saveSettings();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -608,7 +595,7 @@ export class MediaDbSettingTab extends PluginSettingTab {
|
|||
.setValue(this.plugin.settings.mangaFileNameTemplate)
|
||||
.onChange(data => {
|
||||
this.plugin.settings.mangaFileNameTemplate = data;
|
||||
this.plugin.saveSettings();
|
||||
void this.plugin.saveSettings();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -620,7 +607,7 @@ export class MediaDbSettingTab extends PluginSettingTab {
|
|||
.setValue(this.plugin.settings.gameFileNameTemplate)
|
||||
.onChange(data => {
|
||||
this.plugin.settings.gameFileNameTemplate = data;
|
||||
this.plugin.saveSettings();
|
||||
void this.plugin.saveSettings();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -632,7 +619,7 @@ export class MediaDbSettingTab extends PluginSettingTab {
|
|||
.setValue(this.plugin.settings.wikiFileNameTemplate)
|
||||
.onChange(data => {
|
||||
this.plugin.settings.wikiFileNameTemplate = data;
|
||||
this.plugin.saveSettings();
|
||||
void this.plugin.saveSettings();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -644,7 +631,7 @@ export class MediaDbSettingTab extends PluginSettingTab {
|
|||
.setValue(this.plugin.settings.musicReleaseFileNameTemplate)
|
||||
.onChange(data => {
|
||||
this.plugin.settings.musicReleaseFileNameTemplate = data;
|
||||
this.plugin.saveSettings();
|
||||
void this.plugin.saveSettings();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -656,7 +643,7 @@ export class MediaDbSettingTab extends PluginSettingTab {
|
|||
.setValue(this.plugin.settings.boardgameFileNameTemplate)
|
||||
.onChange(data => {
|
||||
this.plugin.settings.boardgameFileNameTemplate = data;
|
||||
this.plugin.saveSettings();
|
||||
void this.plugin.saveSettings();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -668,7 +655,7 @@ export class MediaDbSettingTab extends PluginSettingTab {
|
|||
.setValue(this.plugin.settings.bookFileNameTemplate)
|
||||
.onChange(data => {
|
||||
this.plugin.settings.bookFileNameTemplate = data;
|
||||
this.plugin.saveSettings();
|
||||
void this.plugin.saveSettings();
|
||||
});
|
||||
});
|
||||
// endregion
|
||||
|
|
@ -709,7 +696,7 @@ export class MediaDbSettingTab extends PluginSettingTab {
|
|||
|
||||
this.plugin.settings.propertyMappingModels = propertyMappingModels;
|
||||
new Notice(`MDB: Property Mappings for ${model.type} saved successfully.`);
|
||||
this.plugin.saveSettings();
|
||||
void this.plugin.saveSettings();
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -165,7 +165,7 @@ export abstract class TextInputSuggest<T> implements ISuggestOwner<T> {
|
|||
(<any>this.app).keymap.popScope(this.scope);
|
||||
|
||||
this.suggest.setSuggestions([]);
|
||||
this.popper.destroy();
|
||||
this.popper?.destroy();
|
||||
this.suggestEl.detach();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,104 +0,0 @@
|
|||
import fetchMock, { enableFetchMocks } from 'jest-fetch-mock';
|
||||
import { MediaDbPluginSettings } from 'src/settings/Settings';
|
||||
import { LocGovAPI } from '../api/apis/LocGovAPI';
|
||||
import { MALAPI } from '../api/apis/MALAPI';
|
||||
import { MusicBrainzAPI } from '../api/apis/MusicBrainzAPI';
|
||||
import { OMDbAPI } from '../api/apis/OMDbAPI';
|
||||
import { SteamAPI } from '../api/apis/SteamAPI';
|
||||
import { WikipediaAPI } from '../api/apis/WikipediaAPI';
|
||||
import MediaDbPlugin from '../main';
|
||||
import { setMALResponseMock, setMusicBrainzResponseMock, setOMDbResponseMock, setSteamResponseMock, setWikipediaResponseMock } from './mockHelpers';
|
||||
import MALMockMovie from './ResponseMocks/MALMockMovie.json';
|
||||
import MusicBrainzResponseMock from './ResponseMocks/MusicBrainzMockResponse.json';
|
||||
import OMDBMockMovie from './ResponseMocks/OMDBMockResponse.json';
|
||||
import SteamAPIResponseMock from './ResponseMocks/SteamAPIMockResponse.json';
|
||||
import WikipediaMockResponse from './ResponseMocks/WikipediaMockResponse.json';
|
||||
|
||||
enableFetchMocks();
|
||||
export let apiMock: OMDbAPI | MALAPI | LocGovAPI | MusicBrainzAPI | SteamAPI | WikipediaAPI;
|
||||
|
||||
describe.each([{ name: OMDbAPI }, { name: MALAPI }, { name: LocGovAPI }, { name: MusicBrainzAPI }, { name: SteamAPI }, { name: WikipediaAPI }])(
|
||||
'$name.name',
|
||||
({ name: parameterizedApi }) => {
|
||||
beforeAll(() => {
|
||||
let settingsMock: MediaDbPluginSettings = {} as MediaDbPluginSettings;
|
||||
let pluginMock = {} as MediaDbPlugin;
|
||||
pluginMock.settings = settingsMock;
|
||||
// TODO: add fake API key?
|
||||
apiMock = new parameterizedApi(pluginMock);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fetchMock.resetMocks();
|
||||
});
|
||||
|
||||
test('searchByTitle behavior when API returns garbage data', async () => {
|
||||
const garbageResponse = JSON.stringify({
|
||||
data: 'string',
|
||||
});
|
||||
fetchMock.mockResponseOnce(garbageResponse);
|
||||
await expect(apiMock.searchByTitle('sample')).resolves.toEqual([]);
|
||||
// }
|
||||
expect(fetch).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('searchByTitle behavior when requestUrl/fetch returns 401', async () => {
|
||||
let sampleResponse = {
|
||||
data: 'string',
|
||||
};
|
||||
fetchMock.mockResponse(JSON.stringify(sampleResponse), { status: 401 });
|
||||
// TODO: Check API name and fix message
|
||||
// TODO: Externalize string
|
||||
await expect(apiMock.searchByTitle('sample')).rejects.toThrow(`MDB | Received status code ${401} from an API.`);
|
||||
expect(fetchMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('searchByTitle behavior when requestUrl/fetch returns 403', async () => {
|
||||
let sampleResponse = {
|
||||
data: 'string',
|
||||
};
|
||||
fetchMock.mockResponse(JSON.stringify(sampleResponse), { status: 403 });
|
||||
// TODO: Check API name and fix message
|
||||
// TODO: Externalize string/import?
|
||||
await expect(apiMock.searchByTitle('sample')).rejects.toThrow(`MDB | Received status code ${403} from an API.`);
|
||||
expect(fetchMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('searchByTitle behavior when requestUrl/fetch returns 200', async () => {
|
||||
let sampleResponse;
|
||||
let ret;
|
||||
switch (parameterizedApi) {
|
||||
case OMDbAPI:
|
||||
ret = setOMDbResponseMock();
|
||||
sampleResponse = OMDBMockMovie;
|
||||
break;
|
||||
case WikipediaAPI:
|
||||
ret = setWikipediaResponseMock();
|
||||
sampleResponse = WikipediaMockResponse;
|
||||
break;
|
||||
case MALAPI:
|
||||
// TODO: MAL needs more tests for different types of content
|
||||
ret = setMALResponseMock();
|
||||
sampleResponse = MALMockMovie;
|
||||
case LocGovAPI:
|
||||
// TODO: Add soon
|
||||
break;
|
||||
case SteamAPI:
|
||||
sampleResponse = SteamAPIResponseMock;
|
||||
ret = setSteamResponseMock();
|
||||
break;
|
||||
case MusicBrainzAPI:
|
||||
sampleResponse = MusicBrainzResponseMock;
|
||||
ret = setMusicBrainzResponseMock();
|
||||
break;
|
||||
default:
|
||||
throw Error();
|
||||
}
|
||||
fetchMock.mockResponse(JSON.stringify(sampleResponse), { status: 200 });
|
||||
// TODO: Check API name and fix message
|
||||
// TODO: Externalize string
|
||||
await expect(apiMock.searchByTitle('Hooking Season Playtest')).resolves.toEqual(ret);
|
||||
expect(fetchMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
{
|
||||
"data": [
|
||||
{
|
||||
"mal_id": 2890,
|
||||
"url": "https://myanimelist.net/anime/2890/Gake_no_Ue_no_Ponyo",
|
||||
"title": "Gake no Ue no Ponyo",
|
||||
"title_english": "Ponyo",
|
||||
"type": "Movie",
|
||||
"source": "Original",
|
||||
"episodes": 1,
|
||||
"aired": {
|
||||
"from": "2008-07-19T00:00:00+00:00",
|
||||
"to": null,
|
||||
"prop": {
|
||||
"from": {
|
||||
"day": 19,
|
||||
"month": 7,
|
||||
"year": 2008
|
||||
},
|
||||
"to": {
|
||||
"day": null,
|
||||
"month": null,
|
||||
"year": null
|
||||
}
|
||||
},
|
||||
"string": "Jul 19, 2008"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
{
|
||||
"release-groups": [
|
||||
{
|
||||
"id": "9cf08bf9-1948-4087-abe1-783210ea1fae",
|
||||
"primary-type-id": "f529b476-6e62-324f-b0aa-1f3e33d313fc",
|
||||
"title": "Halo Halo",
|
||||
"first-release-date": "2013-07-08",
|
||||
"primary-type": "Album",
|
||||
"artist-credit": [
|
||||
{
|
||||
"name": "Halo Halo",
|
||||
"artist": {
|
||||
"name": "Halo Halo"
|
||||
}
|
||||
}
|
||||
],
|
||||
"releases": [
|
||||
{
|
||||
"id": "58dd1d57-2201-472e-9e36-5d497dcedb6f",
|
||||
"title": "Halo Halo"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
{
|
||||
"Search": [
|
||||
{
|
||||
"Title": "Guardians of the Galaxy",
|
||||
"Year": "2014",
|
||||
"imdbID": "tt2015381",
|
||||
"Type": "movie",
|
||||
"Poster": "https://m.media-amazon.com/images/M/MV5BMTAwMjU5OTgxNjZeQTJeQWpwZ15BbWU4MDUxNDYxODEx._V1_SX300.jpg"
|
||||
}
|
||||
],
|
||||
"totalResults": "1",
|
||||
"Response": "True"
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
{
|
||||
"applist": {
|
||||
"apps": [
|
||||
{
|
||||
"appid": 2076590,
|
||||
"name": "Hooking Season Playtest"
|
||||
},
|
||||
{
|
||||
"appid": 2076600,
|
||||
"name": "MonsterTamer"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
{
|
||||
"query": {
|
||||
"searchinfo": {
|
||||
"totalhits": 1199001,
|
||||
"suggestion": "book",
|
||||
"suggestionsnippet": "book"
|
||||
},
|
||||
"search": [
|
||||
{
|
||||
"ns": 0,
|
||||
"title": "Book",
|
||||
"pageid": 3778,
|
||||
"size": 68829,
|
||||
"wordcount": 8821,
|
||||
"snippet": "called <span class=\"searchmatch\">books</span> or chapters or parts, are parts. The intellectual content in a physical book need not be a composition, nor even be called a book. <span class=\"searchmatch\">Books</span> can",
|
||||
"timestamp": "2022-08-19T19:13:56Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
import { APIModel } from '../api/APIModel';
|
||||
import { MediaTypeModel } from '../models/MediaTypeModel';
|
||||
import MediaDbPlugin from '../main';
|
||||
|
||||
export class TestAPI extends APIModel {
|
||||
plugin: MediaDbPlugin;
|
||||
|
||||
constructor(plugin: MediaDbPlugin) {
|
||||
super();
|
||||
|
||||
this.plugin = plugin;
|
||||
this.apiName = 'TestAPI';
|
||||
this.apiDescription = 'A test API for automated testing.';
|
||||
this.apiUrl = '';
|
||||
this.types = [];
|
||||
}
|
||||
|
||||
async getById(id: string): Promise<MediaTypeModel> {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
async searchByTitle(title: string): Promise<MediaTypeModel[]> {
|
||||
return [] as MediaTypeModel[];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,95 +0,0 @@
|
|||
import { GameModel } from '../models/GameModel';
|
||||
import { MovieModel } from '../models/MovieModel';
|
||||
import { MusicReleaseModel } from '../models/MusicReleaseModel';
|
||||
import { WikiModel } from '../models/WikiModel';
|
||||
import { MediaType } from '../utils/MediaType';
|
||||
import { apiMock } from './ParameterizedAPI.test';
|
||||
import MALMockMovie from './ResponseMocks/MALMockMovie.json';
|
||||
import MusicBrainzResponseMock from './ResponseMocks/MusicBrainzMockResponse.json';
|
||||
import OMDBMockMovie from './ResponseMocks/OMDBMockResponse.json';
|
||||
import SteamAPIResponseMock from './ResponseMocks/SteamAPIMockResponse.json';
|
||||
import WikipediaMockResponse from './ResponseMocks/WikipediaMockResponse.json';
|
||||
|
||||
export function setWikipediaResponseMock() {
|
||||
let ret = [];
|
||||
let wikiresponse = WikipediaMockResponse.query.search[0];
|
||||
ret.push(
|
||||
new WikiModel({
|
||||
type: 'wiki',
|
||||
title: wikiresponse.title,
|
||||
englishTitle: wikiresponse.title,
|
||||
year: '',
|
||||
dataSource: apiMock.apiName,
|
||||
id: wikiresponse.pageid,
|
||||
}),
|
||||
);
|
||||
return ret;
|
||||
}
|
||||
|
||||
export function setOMDbResponseMock() {
|
||||
let ret = [];
|
||||
let omdbresponse = OMDBMockMovie.Search[0];
|
||||
ret.push(
|
||||
new MovieModel({
|
||||
type: 'wiki',
|
||||
title: omdbresponse.Title,
|
||||
englishTitle: omdbresponse.Title,
|
||||
year: omdbresponse.Year,
|
||||
dataSource: apiMock.apiName,
|
||||
id: omdbresponse.imdbID,
|
||||
}),
|
||||
);
|
||||
return ret;
|
||||
}
|
||||
|
||||
export function setMALResponseMock() {
|
||||
let ret = [];
|
||||
let result = MALMockMovie.data[0];
|
||||
ret.push(
|
||||
new MovieModel({
|
||||
type: result.type,
|
||||
title: result.title,
|
||||
englishTitle: result.title_english,
|
||||
year: result.aired.prop.from.year,
|
||||
dataSource: apiMock.apiName,
|
||||
id: result.mal_id,
|
||||
}),
|
||||
);
|
||||
return ret;
|
||||
}
|
||||
|
||||
export function setSteamResponseMock() {
|
||||
let ret = [];
|
||||
let steamResponse = SteamAPIResponseMock.applist.apps[0];
|
||||
ret.push(
|
||||
new GameModel({
|
||||
type: MediaType.Game,
|
||||
title: steamResponse.name,
|
||||
englishTitle: steamResponse.name,
|
||||
year: '',
|
||||
dataSource: apiMock.apiName,
|
||||
id: steamResponse.appid,
|
||||
}),
|
||||
);
|
||||
return ret;
|
||||
}
|
||||
|
||||
export function setMusicBrainzResponseMock() {
|
||||
let ret = [];
|
||||
let result = MusicBrainzResponseMock['release-groups'][0];
|
||||
ret.push(
|
||||
new MusicReleaseModel({
|
||||
type: 'musicRelease',
|
||||
title: result.title,
|
||||
englishTitle: result.title,
|
||||
year: new Date(result['first-release-date']).getFullYear().toString(),
|
||||
dataSource: apiMock.apiName,
|
||||
url: '',
|
||||
id: result.id,
|
||||
|
||||
artists: result['artist-credit'].map((a: any) => a.name),
|
||||
subType: result['primary-type'],
|
||||
} as MusicReleaseModel),
|
||||
);
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
import { containsOnlyLettersAndUnderscores, replaceIllegalFileNameCharactersInString, wrapAround } from '../utils/Utils';
|
||||
|
||||
test('If wrapAround wraps correctly', () => {
|
||||
expect(wrapAround(100, 5)).toBe(0);
|
||||
expect(wrapAround(100, 7)).toBe(2);
|
||||
});
|
||||
|
||||
test('If wrapAround errors out when dividing by zero', () => {
|
||||
expect(wrapAround(100, 0)).toThrow();
|
||||
});
|
||||
|
||||
test('If wrapAround errors out when size is negative', () => {
|
||||
expect(wrapAround(100, -5)).toThrow();
|
||||
});
|
||||
|
||||
test('Letter and underscore string validity', () => {
|
||||
expect(containsOnlyLettersAndUnderscores('asdkfj_')).toBe(true);
|
||||
expect(containsOnlyLettersAndUnderscores('asdkfj0')).toBe(false);
|
||||
});
|
||||
|
||||
// since this is used to check if a string is a valid name for an object property, unicode characters shouldn't be allowed, thus the name of the function is misleading
|
||||
test('Letter and underscore unicode char test', () => {
|
||||
expect(containsOnlyLettersAndUnderscores('asdkaÈj')).toBe(true);
|
||||
expect(containsOnlyLettersAndUnderscores('asdkaÈj0')).toBe(false);
|
||||
});
|
||||
|
||||
test('Valid filename test', () => {
|
||||
expect(replaceIllegalFileNameCharactersInString('what?is\\this:')).toBe('whatisthis -');
|
||||
});
|
||||
|
|
@ -28,7 +28,7 @@ export class MediaTypeManager {
|
|||
mediaTemplateMap: Map<MediaType, string>;
|
||||
mediaFolderMap: Map<MediaType, string>;
|
||||
|
||||
constructor() { }
|
||||
constructor() {}
|
||||
|
||||
updateTemplates(settings: MediaDbPluginSettings): void {
|
||||
this.mediaFileNameTemplateMap = new Map<MediaType, string>();
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import { MediaDbSearchResultModal } from '../modals/MediaDbSearchResultModal';
|
|||
import { Notice } from 'obsidian';
|
||||
import MediaDbPlugin from '../main';
|
||||
import { MediaDbPreviewModal } from 'src/modals/MediaDbPreviewModal';
|
||||
import { CreateNoteOptions } from './Utils';
|
||||
import { MediaDbSearchModal } from '../modals/MediaDbSearchModal';
|
||||
import { MediaType } from './MediaType';
|
||||
|
||||
|
|
|
|||
|
|
@ -145,7 +145,9 @@ export function markdownTable(content: string[][]): string {
|
|||
return table;
|
||||
}
|
||||
|
||||
export const fragWithHTML = (html: string) => createFragment(frag => (frag.createDiv().innerHTML = html));
|
||||
export function fragWithHTML(html: string): DocumentFragment {
|
||||
return createFragment(frag => (frag.createDiv().innerHTML = html));
|
||||
}
|
||||
|
||||
export function dateToString(date: Date): string {
|
||||
return `${date.getMonth() + 1}-${date.getDate()}-${date.getFullYear()}`;
|
||||
|
|
@ -207,13 +209,13 @@ export function unCamelCase(str: string): string {
|
|||
// space before last upper in a sequence followed by lower
|
||||
.replace(/\b([A-Z]+)([A-Z])([a-z])/, '$1 $2$3')
|
||||
// uppercase the first character
|
||||
.replace(/^./, function(str) {
|
||||
.replace(/^./, function (str) {
|
||||
return str.toUpperCase();
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
export function hasTemplaterPlugin(app: App) {
|
||||
export function hasTemplaterPlugin(app: App): boolean {
|
||||
const templater = (app as any).plugins.plugins['templater-obsidian'];
|
||||
|
||||
return !!templater;
|
||||
|
|
@ -221,7 +223,7 @@ export function hasTemplaterPlugin(app: App) {
|
|||
|
||||
// Copied from https://github.com/anpigon/obsidian-book-search-plugin
|
||||
// Licensed under the MIT license. Copyright (c) 2020 Jake Runzer
|
||||
export async function useTemplaterPluginInFile(app: App, file: TFile) {
|
||||
export async function useTemplaterPluginInFile(app: App, file: TFile): Promise<void> {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const templater = (app as any).plugins.plugins['templater-obsidian'];
|
||||
if (templater && !templater?.settings['trigger_on_file_creation']) {
|
||||
|
|
|
|||
|
|
@ -1,43 +0,0 @@
|
|||
export class YAMLConverter {
|
||||
static toYaml(obj: any): string {
|
||||
let output = '';
|
||||
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
output += `${key}: ${YAMLConverter.toYamlString(value, 0)}\n`;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
private static toYamlString(value: any, indentation: number): string {
|
||||
if (value == null) {
|
||||
return 'null';
|
||||
}
|
||||
|
||||
if (typeof value === 'boolean') {
|
||||
return value ? 'true' : 'false';
|
||||
} else if (typeof value === 'number') {
|
||||
return value.toString();
|
||||
} else if (typeof value === 'string') {
|
||||
return '"' + value.replace('"', '\\"') + '"';
|
||||
} else if (typeof value === 'object') {
|
||||
let output = '';
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
for (const valueElement of value) {
|
||||
output += `\n${YAMLConverter.calculateSpacing(indentation)} - ${YAMLConverter.toYamlString(valueElement, indentation + 1)}`;
|
||||
}
|
||||
} else {
|
||||
for (const [objKey, objValue] of Object.entries(value)) {
|
||||
output += `\n${YAMLConverter.calculateSpacing(indentation)} ${objKey}: ${YAMLConverter.toYamlString(objValue, indentation + 1)}`;
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
private static calculateSpacing(indentation: number): string {
|
||||
return ' '.repeat(indentation * 4);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +1,6 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"types": ["svelte", "node", "jest"],
|
||||
"baseUrl": ".",
|
||||
"inlineSourceMap": true,
|
||||
"inlineSources": true,
|
||||
"module": "ESNext",
|
||||
"target": "ES6",
|
||||
"allowJs": true,
|
||||
|
|
@ -11,9 +8,9 @@
|
|||
"moduleResolution": "node",
|
||||
"importHelpers": true,
|
||||
"isolatedModules": true,
|
||||
"resolveJsonModule": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"lib": ["DOM", "ES5", "ES6", "ES7", "Es2021"]
|
||||
"lib": ["DOM", "ESNext"],
|
||||
"types": ["svelte"],
|
||||
"allowSyntheticDefaultImports": true
|
||||
},
|
||||
"include": ["**/*.ts"]
|
||||
"include": ["src/**/*.ts", "tests/**/*.ts"]
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue