diff --git a/.editorconfig b/.editorconfig
index 29f5cb3..24155f6 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -9,6 +9,10 @@ indent_style = tab
indent_size = 4
tab_width = 4
+[*.yml]
+indent_style = space
+indent_size = 2
+
# 2 space indentation
[*.{ts,js}]
indent_style = space
diff --git a/.eslintignore b/.eslintignore
index 32909b2..dd87e2d 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -1,2 +1,2 @@
-npm node_modules
-build
\ No newline at end of file
+node_modules
+build
diff --git a/.eslintrc b/.eslintrc
index 766e9cf..58a795d 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -1,24 +1,22 @@
{
- "root": true,
- "parser": "@typescript-eslint/parser",
- "env": { "node": true },
- "plugins": [
- "@typescript-eslint"
- ],
- "extends": [
- "eslint:recommended",
- "plugin:@typescript-eslint/eslint-recommended",
- "plugin:@typescript-eslint/recommended"
- ],
- "parserOptions": {
- "sourceType": "module"
- },
- "rules": {
- "no-unused-vars": "off",
- "@typescript-eslint/no-unused-vars": ["error", { "args": "none" }],
- "@typescript-eslint/ban-ts-comment": "off",
- "no-prototype-builtins": "off",
- "@typescript-eslint/no-empty-function": "off",
- "semi": ["error", "always"]
- }
- }
+ "root": true,
+ "parser": "@typescript-eslint/parser",
+ "env": { "node": true },
+ "plugins": ["@typescript-eslint"],
+ "extends": [
+ "eslint:recommended",
+ "plugin:@typescript-eslint/eslint-recommended",
+ "plugin:@typescript-eslint/recommended"
+ ],
+ "parserOptions": {
+ "sourceType": "module"
+ },
+ "rules": {
+ "no-unused-vars": "off",
+ "@typescript-eslint/no-unused-vars": ["error", { "args": "none" }],
+ "@typescript-eslint/ban-ts-comment": "off",
+ "no-prototype-builtins": "off",
+ "@typescript-eslint/no-empty-function": "off",
+ "semi": [2, "never"]
+ }
+}
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 22a15f8..7d3e8f9 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -5,28 +5,29 @@
name: Node.js CI
on:
- push:
- branches: [ master, main, develop ]
- pull_request:
- branches: [ master, main, develop ]
+ push:
+ branches: [ master, main, develop ]
+ pull_request:
+ branches: [ master, main, develop ]
jobs:
- build:
- runs-on: ${{ matrix.os }}
- continue-on-error: true
- strategy:
- matrix:
- os: [ ubuntu-latest, macos-latest, windows-latest ]
- node-version: [ 14.x, 16.x, 18.x, 19.x ]
- max-parallel: 24
- steps:
- - uses: actions/checkout@v3
- - name: Use Node.js ${{ matrix.node-version }}
- uses: actions/setup-node@v2
- with:
- node-version: ${{ matrix.node-version }}
- - run: yarn
- # before the first 'run' each "run" is a script from your project:
- # - run: npm run prettier
- # - run: npm run linter
- - run: yarn test
+ build:
+ runs-on: ${{ matrix.os }}
+ continue-on-error: true
+ strategy:
+ matrix:
+ os: [ ubuntu-latest, macos-latest, windows-latest ]
+ node-version: [ 14.x, 16.x, 18.x, 19.x ]
+ max-parallel: 24
+ steps:
+ - uses: actions/checkout@v3
+ - name: Use Node.js ${{ matrix.node-version }}
+ uses: actions/setup-node@v2
+ with:
+ node-version: ${{ matrix.node-version }}
+ - name: Install dependencies
+ run: yarn
+ - name: Lint
+ run: yarn lint
+ - name: Test
+ run: yarn test
diff --git a/.prettierrc b/.prettierrc
index 8d95c2d..1ccc975 100644
--- a/.prettierrc
+++ b/.prettierrc
@@ -1,4 +1,4 @@
{
- "semi": true,
- "singleQuote": false
+ "semi": false,
+ "singleQuote": true
}
diff --git a/package.json b/package.json
index 99202ec..8f5f266 100644
--- a/package.json
+++ b/package.json
@@ -7,6 +7,9 @@
"dev": "node esbuild.config.mjs",
"build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production",
"version": "node version-bump.mjs && git add manifest.json versions.json",
+ "lint": "eslint ./src --ext .ts",
+ "lint:fix": "eslint ./src --ext .ts --fix",
+ "format": "prettier --write ./src/**/*.ts",
"test": "jest",
"test:watch": "jest --watch",
"test:ci": "jest --ci --reporters='default' --reporters='./github-actions-reporter'",
@@ -33,6 +36,7 @@
"@typescript-eslint/parser": "5.29.0",
"builtin-modules": "3.3.0",
"esbuild": "0.14.47",
+ "eslint": "^8.53.0",
"jest": "^29.4.3",
"jest-cli": "^29.4.3",
"jest-junit-reporter": "^1.1.0",
diff --git a/src/__mocks__/obsidian.ts b/src/__mocks__/obsidian.ts
index a1ae754..8a3460f 100644
--- a/src/__mocks__/obsidian.ts
+++ b/src/__mocks__/obsidian.ts
@@ -1,7 +1,7 @@
export const mockObsidianApp = {
// Mock implementation of the App API
app: {
- platform: () => "desktop",
+ platform: () => 'desktop',
plugins: {
getPlugins: () => [],
isEnabled: () => true,
@@ -11,7 +11,7 @@ export const mockObsidianApp = {
// Mock implementation of the Workspace API
workspace: {
onLayoutReady: (callback: () => void) => {
- setTimeout(callback, 0);
+ setTimeout(callback, 0)
},
getLeavesOfType: () => [],
getConfig: () => ({}),
@@ -19,12 +19,12 @@ export const mockObsidianApp = {
// Mock implementation of the MarkdownView API
markdownView: {
- getMode: () => "source",
- getMarkdown: () => "",
+ getMode: () => 'source',
+ getMarkdown: () => '',
},
-};
+}
// Mock implementation of the Obsidian global object
export const obsidian = {
...mockObsidianApp,
-};
+}
diff --git a/src/__tests__/formatDate.spec.ts b/src/__tests__/formatDate.spec.ts
index 1090f53..22525aa 100644
--- a/src/__tests__/formatDate.spec.ts
+++ b/src/__tests__/formatDate.spec.ts
@@ -1,70 +1,70 @@
-import { DateTime } from "luxon";
-import { DEFAULT_SETTINGS } from "../settings";
-import { formatDate } from "../util";
+import { DateTime } from 'luxon'
+import { DEFAULT_SETTINGS } from '../settings'
+import { formatDate } from '../util'
-const jsDate = new Date("2023-02-18 13:02:08.169");
-const apiDate = jsDate.toISOString(); // API returns ISO 8601 date strings
+const jsDate = new Date('2023-02-18 13:02:08.169')
+const apiDate = jsDate.toISOString() // API returns ISO 8601 date strings
type testCase = {
- format: string;
- date: string;
- expected: string;
-};
+ format: string
+ date: string
+ expected: string
+}
const luxonHierarchicalFormatWithTime = {
date: apiDate,
- expected: "2023/2023-02/2023-02-18/130208",
- format: "yyyy/yyyy-MM/yyyy-MM-dd/HHmmss",
-};
+ expected: '2023/2023-02/2023-02-18/130208',
+ format: 'yyyy/yyyy-MM/yyyy-MM-dd/HHmmss',
+}
const luxonHierarchicalFormat = {
date: apiDate,
- expected: "2023/2023-02/2023-02-18",
- format: "yyyy/yyyy-MM/yyyy-MM-dd",
-};
+ expected: '2023/2023-02/2023-02-18',
+ format: 'yyyy/yyyy-MM/yyyy-MM-dd',
+}
const defaultDateHighlightedFormatTestCase: testCase = {
date: apiDate,
- expected: "2023-02-18 13:02:08",
+ expected: '2023-02-18 13:02:08',
format: DEFAULT_SETTINGS.dateHighlightedFormat,
-};
+}
const defaultDateSavedFormatTestCase: testCase = {
date: apiDate,
- expected: "2023-02-18 13:02:08",
+ expected: '2023-02-18 13:02:08',
format: DEFAULT_SETTINGS.dateSavedFormat,
-};
+}
const defaultFolderDateFormatTestCase: testCase = {
date: apiDate,
- expected: "2023-02-18",
+ expected: '2023-02-18',
format: DEFAULT_SETTINGS.folderDateFormat,
-};
+}
const testCases: testCase[] = [
defaultDateHighlightedFormatTestCase,
defaultDateSavedFormatTestCase,
defaultFolderDateFormatTestCase,
luxonHierarchicalFormat,
luxonHierarchicalFormatWithTime,
-];
-describe("ensure default formats are as expected", () => {
- test("dateHighlightedFormat", () => {
- expect(DEFAULT_SETTINGS.dateHighlightedFormat).toBe("yyyy-MM-dd HH:mm:ss");
- });
- test("dateSavedFormat", () => {
- expect(DEFAULT_SETTINGS.dateSavedFormat).toBe("yyyy-MM-dd HH:mm:ss");
- });
- test("folderDateFormat", () => {
- expect(DEFAULT_SETTINGS.folderDateFormat).toBe("yyyy-MM-dd");
- });
-});
+]
+describe('ensure default formats are as expected', () => {
+ test('dateHighlightedFormat', () => {
+ expect(DEFAULT_SETTINGS.dateHighlightedFormat).toBe('yyyy-MM-dd HH:mm:ss')
+ })
+ test('dateSavedFormat', () => {
+ expect(DEFAULT_SETTINGS.dateSavedFormat).toBe('yyyy-MM-dd HH:mm:ss')
+ })
+ test('folderDateFormat', () => {
+ expect(DEFAULT_SETTINGS.folderDateFormat).toBe('yyyy-MM-dd')
+ })
+})
-describe("formatDate on known formats", () => {
- test.each(testCases)("should correctly format %s", (testCase) => {
- const result = formatDate(testCase.date, testCase.format);
- expect(result).toBe(testCase.expected);
- });
-});
+describe('formatDate on known formats', () => {
+ test.each(testCases)('should correctly format %s', (testCase) => {
+ const result = formatDate(testCase.date, testCase.format)
+ expect(result).toBe(testCase.expected)
+ })
+})
function generateRandomISODateStrings(quantity: number): string[] {
- const randomISODateStrings: string[] = [];
- const timeZones = Intl.DateTimeFormat().resolvedOptions().timeZone.split(",");
+ const randomISODateStrings: string[] = []
+ const timeZones = Intl.DateTimeFormat().resolvedOptions().timeZone.split(',')
for (let i = 0; i < quantity; i++) {
const date = new Date(
@@ -77,80 +77,80 @@ function generateRandomISODateStrings(quantity: number): string[] {
Math.floor(Math.random() * 60),
Math.floor(Math.random() * 1000)
)
- );
+ )
// Randomly select a timezone from the available time zones
const randomTimeZone =
- timeZones[Math.floor(Math.random() * timeZones.length)];
+ timeZones[Math.floor(Math.random() * timeZones.length)]
// Convert the generated date to the randomly selected timezone
// const dateTimeWithZone = DateTime.fromJSDate(date, { zone: randomTimeZone }).toUTC();
const jsDateTimeWithZone = new Date(
- date.toLocaleString("en-US", { timeZone: randomTimeZone })
- );
- const luxonDate = DateTime.fromJSDate(jsDateTimeWithZone);
- randomISODateStrings.push(luxonDate.toISO() as string);
+ date.toLocaleString('en-US', { timeZone: randomTimeZone })
+ )
+ const luxonDate = DateTime.fromJSDate(jsDateTimeWithZone)
+ randomISODateStrings.push(luxonDate.toISO() as string)
}
- return randomISODateStrings;
+ return randomISODateStrings
}
-describe("formatDate on random dates", () => {
+describe('formatDate on random dates', () => {
test.each(generateRandomISODateStrings(100))(
- "should correctly format %s",
+ 'should correctly format %s',
(date) => {
- const result = formatDate(date, "yyyy-MM-dd HH:mm:ss");
+ const result = formatDate(date, 'yyyy-MM-dd HH:mm:ss')
// test with regex to ensure the format is correct
- expect(result).toMatch(/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/);
+ expect(result).toMatch(/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/)
}
- );
-});
+ )
+})
function getCasesWithRandomDates(
testFormats: string[],
quantity = 10
): {
- date: string;
- luxonFormat: string;
+ date: string
+ luxonFormat: string
}[] {
return testFormats.flatMap((luxonFormat) =>
generateRandomISODateStrings(quantity).map((date) => ({
date,
luxonFormat,
}))
- );
+ )
}
-describe("round trip on random dates", () => {
+describe('round trip on random dates', () => {
const testFormats = [
defaultDateHighlightedFormatTestCase.format,
defaultDateSavedFormatTestCase.format,
defaultFolderDateFormatTestCase.format,
- ];
+ ]
// generate permutations of testCases.formats and 10 generated each
- const casesWithRandomDates = getCasesWithRandomDates(testFormats);
+ const casesWithRandomDates = getCasesWithRandomDates(testFormats)
test.each(casesWithRandomDates)(
- "should be unchanged after round trip %s",
+ 'should be unchanged after round trip %s',
(testCase) => {
- const result = formatDate(testCase.date, testCase.luxonFormat);
- const result2 = formatDate(result, testCase.luxonFormat);
- expect(result2).toBe(result);
+ const result = formatDate(testCase.date, testCase.luxonFormat)
+ const result2 = formatDate(result, testCase.luxonFormat)
+ expect(result2).toBe(result)
}
- );
+ )
const atypicalFormats = [
luxonHierarchicalFormat.format,
luxonHierarchicalFormatWithTime.format,
- ];
+ ]
test.each(getCasesWithRandomDates(atypicalFormats))(
- "should be unchanged after round trip with atypical format %s",
+ 'should be unchanged after round trip with atypical format %s',
(testCase) => {
- const formattedDate = formatDate(testCase.date, testCase.luxonFormat);
+ const formattedDate = formatDate(testCase.date, testCase.luxonFormat)
const parsedDate = DateTime.fromFormat(
formattedDate,
testCase.luxonFormat
- );
- expect(parsedDate.isValid).toBe(true);
+ )
+ expect(parsedDate.isValid).toBe(true)
}
- );
-});
+ )
+})
diff --git a/src/__tests__/path_validation.spec.ts b/src/__tests__/path_validation.spec.ts
index 87c8287..1454553 100644
--- a/src/__tests__/path_validation.spec.ts
+++ b/src/__tests__/path_validation.spec.ts
@@ -1,115 +1,115 @@
-import * as fs from "fs";
+import * as fs from 'fs'
import {
ILLEGAL_CHAR_REGEX,
replaceIllegalChars,
REPLACEMENT_CHAR,
-} from "../util";
+} from '../util'
const expectedManualIllegalChars: string[] = [
- "/",
- "\\",
- "?",
- "*",
- ":",
- "|",
+ '/',
+ '\\',
+ '?',
+ '*',
+ ':',
+ '|',
'"',
- "<",
- ">",
- "\u0000",
- "\u001F",
-];
+ '<',
+ '>',
+ '\u0000',
+ '\u001F',
+]
// ZERO WIDTH JOINER and SOFT HYPHEN
-const expectedInvisibleChars: string[] = ["", ""];
+const expectedInvisibleChars: string[] = ['', '']
-describe("replaceIllegalChars() removes all expected characters", () => {
+describe('replaceIllegalChars() removes all expected characters', () => {
test.each(expectedManualIllegalChars)(
'Illegal character "%s" is removed',
(character) => {
- const input = `this${character}string`;
- const output = replaceIllegalChars(input);
- expect(output).not.toContain(character);
+ const input = `this${character}string`
+ const output = replaceIllegalChars(input)
+ expect(output).not.toContain(character)
}
- );
-});
+ )
+})
-describe("replaceIllegalChars() function replaces illegal characters with replacement char", () => {
+describe('replaceIllegalChars() function replaces illegal characters with replacement char', () => {
test.each(expectedManualIllegalChars)(
"Illegal character '%s' is replaced",
(char) => {
- const input = `this${char}string`;
- const expectedOutput = `this${REPLACEMENT_CHAR}string`;
- const output = replaceIllegalChars(input);
- expect(output).toEqual(expectedOutput);
+ const input = `this${char}string`
+ const expectedOutput = `this${REPLACEMENT_CHAR}string`
+ const output = replaceIllegalChars(input)
+ expect(output).toEqual(expectedOutput)
}
- );
-});
+ )
+})
-describe("replaceIllegalChars() function does not modify string without illegal characters", () => {
- test.each(["this_is_a_valid_string", "this is a valid string"])(
+describe('replaceIllegalChars() function does not modify string without illegal characters', () => {
+ test.each(['this_is_a_valid_string', 'this is a valid string'])(
"String '%s' is not modified",
(input) => {
- const output = replaceIllegalChars(input);
- expect(output).toEqual(input);
+ const output = replaceIllegalChars(input)
+ expect(output).toEqual(input)
}
- );
-});
+ )
+})
-describe("replaceIllegalChars() function handles empty string", () => {
- test("Empty string is not modified", () => {
- const input = "";
- const output = replaceIllegalChars(input);
- expect(output).toEqual(input);
- });
-});
+describe('replaceIllegalChars() function handles empty string', () => {
+ test('Empty string is not modified', () => {
+ const input = ''
+ const output = replaceIllegalChars(input)
+ expect(output).toEqual(input)
+ })
+})
-describe("replaceIllegalChars() function replaces all occurrences of illegal characters", () => {
+describe('replaceIllegalChars() function replaces all occurrences of illegal characters', () => {
test.each(expectedManualIllegalChars)(
"Illegal character '%s' is replaced",
(char) => {
- const input = `${char}foo${char}bar`;
- const expectedOutput = `${REPLACEMENT_CHAR}foo${REPLACEMENT_CHAR}bar`;
- const output = replaceIllegalChars(input);
- expect(output).toEqual(expectedOutput);
- expect(output.match(ILLEGAL_CHAR_REGEX)).toBeNull();
+ const input = `${char}foo${char}bar`
+ const expectedOutput = `${REPLACEMENT_CHAR}foo${REPLACEMENT_CHAR}bar`
+ const output = replaceIllegalChars(input)
+ expect(output).toEqual(expectedOutput)
+ expect(output.match(ILLEGAL_CHAR_REGEX)).toBeNull()
}
- );
-});
+ )
+})
-describe("file system behavior with non-alphanumeric characters not in the illegal character list", () => {
+describe('file system behavior with non-alphanumeric characters not in the illegal character list', () => {
const nonAlphanumericCharactersWithoutIllegal: string[] = Array.from(
{ length: 127 - 32 },
(_, i) => String.fromCharCode(i + 32)
)
.filter((char) => !/^[a-zA-Z0-9]+$/.test(char))
- .map(replaceIllegalChars);
+ .map(replaceIllegalChars)
test.each(nonAlphanumericCharactersWithoutIllegal)(
"File system allows creation of file with character '%s'",
(char) => {
- const input = `test${char}test.txt`;
+ const input = `test${char}test.txt`
// verify file does not already exist
- expect(fs.existsSync(input)).toBe(false);
- fs.writeFileSync(input, "test");
+ expect(fs.existsSync(input)).toBe(false)
+ fs.writeFileSync(input, 'test')
// verify the file exists
- expect(fs.existsSync(input)).toBe(true);
+ expect(fs.existsSync(input)).toBe(true)
// remove the file
- fs.unlinkSync(input);
+ fs.unlinkSync(input)
// verify the file has been deleted
- expect(fs.existsSync(input)).toBe(false);
+ expect(fs.existsSync(input)).toBe(false)
}
- );
-});
+ )
+})
-describe("replaceIllegalChars() function removes all occurrences of invisible characters", () => {
+describe('replaceIllegalChars() function removes all occurrences of invisible characters', () => {
test.each(expectedInvisibleChars)(
"Invisible character '%s' is replaced",
(char) => {
- const input = `${char}foo${char}bar`;
- const expectedOutput = "foobar";
- const output = replaceIllegalChars(input);
- expect(output).toEqual(expectedOutput);
- expect(output.match(ILLEGAL_CHAR_REGEX)).toBeNull();
+ const input = `${char}foo${char}bar`
+ const expectedOutput = 'foobar'
+ const output = replaceIllegalChars(input)
+ expect(output).toEqual(expectedOutput)
+ expect(output.match(ILLEGAL_CHAR_REGEX)).toBeNull()
}
- );
-});
+ )
+})
diff --git a/src/api.ts b/src/api.ts
index 4ba915b..3d5ebf2 100644
--- a/src/api.ts
+++ b/src/api.ts
@@ -1,4 +1,4 @@
-import { requestUrl } from "obsidian";
+import { requestUrl } from "obsidian"
export interface SearchResponse {
data: {
@@ -84,7 +84,7 @@ const requestHeaders = (apiKey: string) => ({
"Content-Type": "application/json",
authorization: apiKey,
"X-OmnivoreClient": "obsidian-plugin",
-});
+})
export const loadArticles = async (
endpoint: string,
@@ -163,13 +163,13 @@ export const loadArticles = async (
},
}),
method: "POST",
- });
+ })
- const jsonRes = res.json as SearchResponse;
- const articles = jsonRes.data.search.edges.map((e) => e.node);
+ const jsonRes = res.json as SearchResponse
+ const articles = jsonRes.data.search.edges.map((e) => e.node)
- return [articles, jsonRes.data.search.pageInfo.hasNextPage];
-};
+ return [articles, jsonRes.data.search.pageInfo.hasNextPage]
+}
export const deleteArticleById = async (endpoint: string, apiKey: string, articleId: string) => {
@@ -198,11 +198,11 @@ export const deleteArticleById = async (endpoint: string, apiKey: string, articl
},
}),
method: "POST",
- });
+ })
- const jsonRes = res.json as DeleteArticleResponse;
+ const jsonRes = res.json as DeleteArticleResponse
if (jsonRes.data.setBookmarkArticle.bookmarkedArticle.id === articleId) {
- return true;
+ return true
}
return false
diff --git a/src/main.ts b/src/main.ts
index e7f01c6..9348706 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -1,4 +1,4 @@
-import { DateTime } from "luxon";
+import { DateTime } from "luxon"
import {
addIcon,
App,
@@ -11,22 +11,22 @@ import {
stringifyYaml,
TFile,
TFolder,
-} from "obsidian";
-import { Article, deleteArticleById, loadArticles, PageType } from "./api";
+} from "obsidian"
+import { Article, deleteArticleById, loadArticles, PageType } from "./api"
import {
DEFAULT_SETTINGS,
Filter,
FRONT_MATTER_VARIABLES,
HighlightOrder,
OmnivoreSettings,
-} from "./settings";
-import { FolderSuggest } from "./settings/file-suggest";
+} from "./settings"
+import { FolderSuggest } from "./settings/file-suggest"
import {
preParseTemplate,
renderArticleContnet,
renderFilename,
renderFolderName,
-} from "./settings/template";
+} from "./settings/template"
import {
DATE_FORMAT,
findFrontMatterIndex,
@@ -35,42 +35,42 @@ import {
parseFrontMatterFromContent,
removeFrontMatterFromContent,
replaceIllegalChars,
-} from "./util";
+} from "./util"
export default class OmnivorePlugin extends Plugin {
- settings: OmnivoreSettings;
+ settings: OmnivoreSettings
async onload() {
- await this.loadSettings();
- await this.resetSyncingStateSetting();
+ await this.loadSettings()
+ await this.resetSyncingStateSetting()
// update version if needed
- const latestVersion = this.manifest.version;
- const currentVersion = this.settings.version;
+ const latestVersion = this.manifest.version
+ const currentVersion = this.settings.version
if (latestVersion !== currentVersion) {
- this.settings.version = latestVersion;
- this.saveSettings();
+ this.settings.version = latestVersion
+ this.saveSettings()
// show release notes
const releaseNotes = `Omnivore plugin is upgraded to ${latestVersion}.
What's new: https://github.com/omnivore-app/obsidian-omnivore/blob/main/CHANGELOG.md
- `;
- new Notice(releaseNotes, 10000);
+ `
+ new Notice(releaseNotes, 10000)
}
this.addCommand({
id: "sync",
name: "Sync",
callback: () => {
- this.fetchOmnivore();
+ this.fetchOmnivore()
},
- });
+ })
this.addCommand({
id: "deleteArticle",
name: "Delete Current Article from Omnivore",
callback: () => {
- this.deleteCurrentArticle(this.app.workspace.getActiveFile());
+ this.deleteCurrentArticle(this.app.workspace.getActiveFile())
}
})
@@ -78,89 +78,89 @@ export default class OmnivorePlugin extends Plugin {
id: "resync",
name: "Resync all articles",
callback: () => {
- this.settings.syncAt = "";
- this.saveSettings();
- new Notice("Omnivore Last Sync reset");
- this.fetchOmnivore();
+ this.settings.syncAt = ""
+ this.saveSettings()
+ new Notice("Omnivore Last Sync reset")
+ this.fetchOmnivore()
},
- });
+ })
- const iconId = "Omnivore";
+ const iconId = "Omnivore"
// add icon
addIcon(
iconId,
``
- );
+ )
// This creates an icon in the left ribbon.
this.addRibbonIcon(iconId, iconId, async (evt: MouseEvent) => {
// Called when the user clicks the icon.
- await this.fetchOmnivore();
- });
+ await this.fetchOmnivore()
+ })
// This adds a settings tab so the user can configure various aspects of the plugin
- this.addSettingTab(new OmnivoreSettingTab(this.app, this));
+ this.addSettingTab(new OmnivoreSettingTab(this.app, this))
- this.scheduleSync();
+ this.scheduleSync()
}
onunload() {}
async loadSettings() {
- this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
+ this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData())
}
async saveSettings() {
- await this.saveData(this.settings);
+ await this.saveData(this.settings)
}
scheduleSync() {
// clear previous interval
if (this.settings.intervalId > 0) {
- window.clearInterval(this.settings.intervalId);
+ window.clearInterval(this.settings.intervalId)
}
- const frequency = this.settings.frequency;
+ const frequency = this.settings.frequency
if (frequency > 0) {
// schedule new interval
const intervalId = window.setInterval(async () => {
- await this.fetchOmnivore(false);
- }, frequency * 60 * 1000);
+ await this.fetchOmnivore(false)
+ }, frequency * 60 * 1000)
// save new interval id
- this.settings.intervalId = intervalId;
- this.saveSettings();
+ this.settings.intervalId = intervalId
+ this.saveSettings()
// clear interval when plugin is unloaded
- this.registerInterval(intervalId);
+ this.registerInterval(intervalId)
}
}
async downloadFileAsAttachment(article: Article): Promise {
// download pdf from the URL to the attachment folder
- const url = article.url;
+ const url = article.url
const response = await requestUrl({
url,
contentType: "application/pdf",
- });
+ })
const folderName = normalizePath(
renderFolderName(
article,
this.settings.attachmentFolder,
this.settings.folderDateFormat
)
- );
- const folder = app.vault.getAbstractFileByPath(folderName);
+ )
+ const folder = app.vault.getAbstractFileByPath(folderName)
if (!(folder instanceof TFolder)) {
- await app.vault.createFolder(folderName);
+ await app.vault.createFolder(folderName)
}
- const fileName = normalizePath(`${folderName}/${article.id}.pdf`);
- const file = app.vault.getAbstractFileByPath(fileName);
+ const fileName = normalizePath(`${folderName}/${article.id}.pdf`)
+ const file = app.vault.getAbstractFileByPath(fileName)
if (!(file instanceof TFile)) {
const newFile = await app.vault.createBinary(
fileName,
response.arrayBuffer
- );
- return newFile.path;
+ )
+ return newFile.path
}
- return file.path;
+ return file.path
}
async fetchOmnivore(manualSync = true) {
@@ -177,38 +177,38 @@ export default class OmnivorePlugin extends Plugin {
isSingleFile,
frontMatterVariables,
frontMatterTemplate,
- } = this.settings;
+ } = this.settings
if (syncing) {
- new Notice("🐢 Already syncing ...");
- return;
+ new Notice("🐢 Already syncing ...")
+ return
}
if (!apiKey) {
- new Notice("Missing Omnivore api key");
- return;
+ new Notice("Missing Omnivore api key")
+ return
}
- this.settings.syncing = true;
- await this.saveSettings();
+ this.settings.syncing = true
+ await this.saveSettings()
try {
- console.log(`obsidian-omnivore starting sync since: '${syncAt}'`);
+ console.log(`obsidian-omnivore starting sync since: '${syncAt}'`)
- manualSync && new Notice("🚀 Fetching articles ...");
+ manualSync && new Notice("🚀 Fetching articles ...")
// pre-parse template
- frontMatterTemplate && preParseTemplate(frontMatterTemplate);
- const templateSpans = preParseTemplate(template);
+ frontMatterTemplate && preParseTemplate(frontMatterTemplate)
+ const templateSpans = preParseTemplate(template)
// check if we need to include content or file attachment
const includeContent = templateSpans.some(
(templateSpan) => templateSpan[1] === "content"
- );
+ )
const includeFileAttachment = templateSpans.some(
(templateSpan) => templateSpan[1] === "fileAttachment"
- );
+ )
- const size = 50;
+ const size = 50
for (
let hasNextPage = true, articles: Article[] = [], after = 0;
hasNextPage;
@@ -223,21 +223,21 @@ export default class OmnivorePlugin extends Plugin {
getQueryFromFilter(filter, customQuery),
includeContent,
"highlightedMarkdown"
- );
+ )
for (const article of articles) {
const folderName = normalizePath(
renderFolderName(article, folder, this.settings.folderDateFormat)
- );
+ )
const omnivoreFolder =
- this.app.vault.getAbstractFileByPath(folderName);
+ this.app.vault.getAbstractFileByPath(folderName)
if (!(omnivoreFolder instanceof TFolder)) {
- await this.app.vault.createFolder(folderName);
+ await this.app.vault.createFolder(folderName)
}
const fileAttachment =
article.pageType === PageType.File && includeFileAttachment
? await this.downloadFileAsAttachment(article)
- : undefined;
+ : undefined
const content = await renderArticleContnet(
article,
template,
@@ -248,139 +248,139 @@ export default class OmnivorePlugin extends Plugin {
frontMatterVariables,
frontMatterTemplate,
fileAttachment
- );
+ )
// use the custom filename
const customFilename = replaceIllegalChars(
renderFilename(article, filename, this.settings.filenameDateFormat)
- );
- const pageName = `${folderName}/${customFilename}.md`;
- const normalizedPath = normalizePath(pageName);
+ )
+ const pageName = `${folderName}/${customFilename}.md`
+ const normalizedPath = normalizePath(pageName)
const omnivoreFile =
- this.app.vault.getAbstractFileByPath(normalizedPath);
+ this.app.vault.getAbstractFileByPath(normalizedPath)
if (omnivoreFile instanceof TFile) {
// file exists, so we might need to update it
if (isSingleFile) {
// sync into a single file
- const existingContent = await this.app.vault.read(omnivoreFile);
+ const existingContent = await this.app.vault.read(omnivoreFile)
// we need to remove the front matter
const contentWithoutFrontmatter =
- removeFrontMatterFromContent(content);
+ removeFrontMatterFromContent(content)
const existingContentWithoutFrontmatter =
- removeFrontMatterFromContent(existingContent);
+ removeFrontMatterFromContent(existingContent)
// get front matter from content
let existingFrontMatter =
- parseFrontMatterFromContent(existingContent) || [];
+ parseFrontMatterFromContent(existingContent) || []
if (!Array.isArray(existingFrontMatter)) {
// convert front matter to array
- existingFrontMatter = [existingFrontMatter];
+ existingFrontMatter = [existingFrontMatter]
}
- const newFrontMatter = parseFrontMatterFromContent(content);
+ const newFrontMatter = parseFrontMatterFromContent(content)
if (
!newFrontMatter ||
!Array.isArray(newFrontMatter) ||
newFrontMatter.length === 0
) {
- throw new Error("Front matter does not exist in the template");
+ throw new Error("Front matter does not exist in the template")
}
- let newContentWithoutFrontMatter: string;
+ let newContentWithoutFrontMatter: string
// find the front matter with the same id
const frontMatterIdx = findFrontMatterIndex(
existingFrontMatter,
article.id
- );
+ )
if (frontMatterIdx >= 0) {
// this article already exists in the file
// we need to locate the article which is wrapped in comments
// and replace the content
- const sectionStart = `%%${article.id}_start%%`;
- const sectionEnd = `%%${article.id}_end%%`;
+ const sectionStart = `%%${article.id}_start%%`
+ const sectionEnd = `%%${article.id}_end%%`
const existingContentRegex = new RegExp(
`${sectionStart}.*?${sectionEnd}`,
"s"
- );
+ )
newContentWithoutFrontMatter =
existingContentWithoutFrontmatter.replace(
existingContentRegex,
contentWithoutFrontmatter
- );
+ )
- existingFrontMatter[frontMatterIdx] = newFrontMatter[0];
+ existingFrontMatter[frontMatterIdx] = newFrontMatter[0]
} else {
// this article doesn't exist in the file
// prepend the article
- newContentWithoutFrontMatter = `${contentWithoutFrontmatter}\n\n${existingContentWithoutFrontmatter}`;
+ newContentWithoutFrontMatter = `${contentWithoutFrontmatter}\n\n${existingContentWithoutFrontmatter}`
// prepend new front matter which is an array
- existingFrontMatter.unshift(newFrontMatter[0]);
+ existingFrontMatter.unshift(newFrontMatter[0])
}
const newFrontMatterStr = `---\n${stringifyYaml(
existingFrontMatter
- )}---`;
+ )}---`
await this.app.vault.modify(
omnivoreFile,
`${newFrontMatterStr}\n\n${newContentWithoutFrontMatter}`
- );
- continue;
+ )
+ continue
}
// sync into separate files
await this.app.fileManager.processFrontMatter(
omnivoreFile,
async (frontMatter) => {
- const id = frontMatter.id;
+ const id = frontMatter.id
if (id && id !== article.id) {
// this article has the same name but different id
- const newPageName = `${folderName}/${customFilename}-${article.id}.md`;
- const newNormalizedPath = normalizePath(newPageName);
+ const newPageName = `${folderName}/${customFilename}-${article.id}.md`
+ const newNormalizedPath = normalizePath(newPageName)
const newOmnivoreFile =
- this.app.vault.getAbstractFileByPath(newNormalizedPath);
+ this.app.vault.getAbstractFileByPath(newNormalizedPath)
if (newOmnivoreFile instanceof TFile) {
// a file with the same name and id already exists, so we need to update it
const existingContent = await this.app.vault.read(
newOmnivoreFile
- );
+ )
if (existingContent !== content) {
- await this.app.vault.modify(newOmnivoreFile, content);
+ await this.app.vault.modify(newOmnivoreFile, content)
}
- return;
+ return
}
// a file with the same name but different id already exists, so we need to create it
- await this.app.vault.create(newNormalizedPath, content);
- return;
+ await this.app.vault.create(newNormalizedPath, content)
+ return
}
// a file with the same id already exists, so we might need to update it
- const existingContent = await this.app.vault.read(omnivoreFile);
+ const existingContent = await this.app.vault.read(omnivoreFile)
if (existingContent !== content) {
- await this.app.vault.modify(omnivoreFile, content);
+ await this.app.vault.modify(omnivoreFile, content)
}
}
- );
- continue;
+ )
+ continue
}
// file doesn't exist, so we need to create it
try {
- await this.app.vault.create(normalizedPath, content);
+ await this.app.vault.create(normalizedPath, content)
} catch (error) {
if (error.toString().includes("File already exists")) {
new Notice(
`Skipping file creation: ${normalizedPath}. Please check if you have duplicated article titles and delete the file if needed.`
- );
+ )
} else {
- throw error;
+ throw error
}
}
}
}
- manualSync && new Notice("🔖 Articles fetched");
- this.settings.syncAt = DateTime.local().toFormat(DATE_FORMAT);
+ manualSync && new Notice("🔖 Articles fetched")
+ this.settings.syncAt = DateTime.local().toFormat(DATE_FORMAT)
} catch (e) {
- new Notice("Failed to fetch articles");
- console.error(e);
+ new Notice("Failed to fetch articles")
+ console.error(e)
} finally {
- this.settings.syncing = false;
- await this.saveSettings();
+ this.settings.syncing = false
+ await this.saveSettings()
}
}
@@ -391,43 +391,43 @@ export default class OmnivorePlugin extends Plugin {
//use frontmatter id to find the file
const articleId = this.app.metadataCache.getFileCache(file)?.frontmatter?.id
if (!articleId) {
- new Notice("Failed to delete article: article id not found");
+ new Notice("Failed to delete article: article id not found")
}
try{
const isDeleted = deleteArticleById(this.settings.endpoint, this.settings.apiKey, articleId)
if(!isDeleted) {
- new Notice("Failed to delete article in Omnivore");
+ new Notice("Failed to delete article in Omnivore")
}
} catch (e) {
- new Notice("Failed to delete article in Omnivore");
- console.error(e);
+ new Notice("Failed to delete article in Omnivore")
+ console.error(e)
}
await this.app.vault.delete(file)
}
private async resetSyncingStateSetting() {
- this.settings.syncing = false;
- this.settings.intervalId = 0;
- await this.saveSettings();
+ this.settings.syncing = false
+ this.settings.intervalId = 0
+ await this.saveSettings()
}
}
class OmnivoreSettingTab extends PluginSettingTab {
- plugin: OmnivorePlugin;
+ plugin: OmnivorePlugin
constructor(app: App, plugin: OmnivorePlugin) {
- super(app, plugin);
- this.plugin = plugin;
+ super(app, plugin)
+ this.plugin = plugin
}
display(): void {
- const { containerEl } = this;
+ const { containerEl } = this
- containerEl.empty();
+ containerEl.empty()
- containerEl.createEl("h2", { text: "Settings for Omnivore plugin" });
+ containerEl.createEl("h2", { text: "Settings for Omnivore plugin" })
new Setting(containerEl)
.setName("API Key")
@@ -439,7 +439,7 @@ class OmnivoreSettingTab extends PluginSettingTab {
text: "https://omnivore.app/settings/api",
href: "https://omnivore.app/settings/api",
})
- );
+ )
})
)
.addText((text) =>
@@ -447,23 +447,23 @@ class OmnivoreSettingTab extends PluginSettingTab {
.setPlaceholder("Enter your Omnivore Api Key")
.setValue(this.plugin.settings.apiKey)
.onChange(async (value) => {
- this.plugin.settings.apiKey = value;
- await this.plugin.saveSettings();
+ this.plugin.settings.apiKey = value
+ await this.plugin.saveSettings()
})
- );
+ )
new Setting(containerEl)
.setName("Filter")
.setDesc("Select an Omnivore search filter type")
.addDropdown((dropdown) => {
- dropdown.addOptions(Filter);
+ dropdown.addOptions(Filter)
dropdown
.setValue(this.plugin.settings.filter)
.onChange(async (value) => {
- this.plugin.settings.filter = value;
- await this.plugin.saveSettings();
- });
- });
+ this.plugin.settings.filter = value
+ await this.plugin.saveSettings()
+ })
+ })
new Setting(containerEl)
.setName("Custom query")
@@ -476,7 +476,7 @@ class OmnivoreSettingTab extends PluginSettingTab {
href: "https://docs.omnivore.app/using/search",
}),
" for more info on search query syntax. Make sure your Filter (in the section above) is set to advanced when using a custom query."
- );
+ )
})
)
.addText((text) =>
@@ -486,10 +486,10 @@ class OmnivoreSettingTab extends PluginSettingTab {
)
.setValue(this.plugin.settings.customQuery)
.onChange(async (value) => {
- this.plugin.settings.customQuery = value;
- await this.plugin.saveSettings();
+ this.plugin.settings.customQuery = value
+ await this.plugin.saveSettings()
})
- );
+ )
new Setting(containerEl)
.setName("Last Sync")
@@ -500,23 +500,23 @@ class OmnivoreSettingTab extends PluginSettingTab {
.setValue(this.plugin.settings.syncAt)
.setDefaultFormat("yyyy-MM-dd'T'HH:mm:ss")
.onChange(async (value) => {
- this.plugin.settings.syncAt = value;
- await this.plugin.saveSettings();
+ this.plugin.settings.syncAt = value
+ await this.plugin.saveSettings()
})
- );
+ )
new Setting(containerEl)
.setName("Highlight Order")
.setDesc("Select the order in which highlights are applied")
.addDropdown((dropdown) => {
- dropdown.addOptions(HighlightOrder);
+ dropdown.addOptions(HighlightOrder)
dropdown
.setValue(this.plugin.settings.highlightOrder)
.onChange(async (value) => {
- this.plugin.settings.highlightOrder = value;
- await this.plugin.saveSettings();
- });
- });
+ this.plugin.settings.highlightOrder = value
+ await this.plugin.saveSettings()
+ })
+ })
new Setting(containerEl)
.setName("Front Matter")
@@ -534,7 +534,7 @@ class OmnivoreSettingTab extends PluginSettingTab {
fragment.createEl("br"),
fragment.createEl("br"),
"If you want to use a custom front matter template, you can enter it below under the advanced settings"
- );
+ )
})
)
.addTextArea((text) => {
@@ -550,12 +550,12 @@ class OmnivoreSettingTab extends PluginSettingTab {
(v, i, a) =>
FRONT_MATTER_VARIABLES.includes(v.split("::")[0]) &&
a.indexOf(v) === i
- );
- await this.plugin.saveSettings();
- });
- text.inputEl.setAttr("rows", 4);
- text.inputEl.setAttr("cols", 30);
- });
+ )
+ await this.plugin.saveSettings()
+ })
+ text.inputEl.setAttr("rows", 4)
+ text.inputEl.setAttr("cols", 30)
+ })
new Setting(containerEl)
.setName("Article Template")
@@ -570,7 +570,7 @@ class OmnivoreSettingTab extends PluginSettingTab {
fragment.createEl("br"),
fragment.createEl("br"),
"If you want to use a custom front matter template, you can enter it below under the advanced settings"
- );
+ )
})
)
.addTextArea((text) => {
@@ -581,11 +581,11 @@ class OmnivoreSettingTab extends PluginSettingTab {
// if template is empty, use default template
this.plugin.settings.template = value
? value
- : DEFAULT_SETTINGS.template;
- await this.plugin.saveSettings();
- });
- text.inputEl.setAttr("rows", 25);
- text.inputEl.setAttr("cols", 50);
+ : DEFAULT_SETTINGS.template
+ await this.plugin.saveSettings()
+ })
+ text.inputEl.setAttr("rows", 25)
+ text.inputEl.setAttr("cols", 50)
})
.addExtraButton((button) => {
// add a button to reset template
@@ -593,12 +593,12 @@ class OmnivoreSettingTab extends PluginSettingTab {
.setIcon("reset")
.setTooltip("Reset template")
.onClick(async () => {
- this.plugin.settings.template = DEFAULT_SETTINGS.template;
- await this.plugin.saveSettings();
- this.display();
- new Notice("Template reset");
- });
- });
+ this.plugin.settings.template = DEFAULT_SETTINGS.template
+ await this.plugin.saveSettings()
+ this.display()
+ new Notice("Template reset")
+ })
+ })
new Setting(containerEl)
.setName("Frequency")
@@ -611,18 +611,18 @@ class OmnivoreSettingTab extends PluginSettingTab {
.setValue(this.plugin.settings.frequency.toString())
.onChange(async (value) => {
// validate frequency
- const frequency = parseInt(value);
+ const frequency = parseInt(value)
if (isNaN(frequency)) {
- new Notice("Frequency must be a positive integer");
- return;
+ new Notice("Frequency must be a positive integer")
+ return
}
// save frequency
- this.plugin.settings.frequency = frequency;
- await this.plugin.saveSettings();
+ this.plugin.settings.frequency = frequency
+ await this.plugin.saveSettings()
- this.plugin.scheduleSync();
+ this.plugin.scheduleSync()
})
- );
+ )
new Setting(containerEl)
.setName("Folder")
@@ -630,30 +630,30 @@ class OmnivoreSettingTab extends PluginSettingTab {
"Enter the folder where the data will be stored. {{{title}}}, {{{dateSaved}}} and {{{datePublished}}} could be used in the folder name"
)
.addSearch((search) => {
- new FolderSuggest(this.app, search.inputEl);
+ new FolderSuggest(this.app, search.inputEl)
search
.setPlaceholder("Enter the folder")
.setValue(this.plugin.settings.folder)
.onChange(async (value) => {
- this.plugin.settings.folder = value;
- await this.plugin.saveSettings();
- });
- });
+ this.plugin.settings.folder = value
+ await this.plugin.saveSettings()
+ })
+ })
new Setting(containerEl)
.setName("Attachment Folder")
.setDesc(
"Enter the folder where the attachment will be downloaded to. {{{title}}}, {{{dateSaved}}} and {{{datePublished}}} could be used in the folder name"
)
.addSearch((search) => {
- new FolderSuggest(this.app, search.inputEl);
+ new FolderSuggest(this.app, search.inputEl)
search
.setPlaceholder("Enter the attachment folder")
.setValue(this.plugin.settings.attachmentFolder)
.onChange(async (value) => {
- this.plugin.settings.attachmentFolder = value;
- await this.plugin.saveSettings();
- });
- });
+ this.plugin.settings.attachmentFolder = value
+ await this.plugin.saveSettings()
+ })
+ })
new Setting(containerEl)
.setName("Is Single File")
@@ -664,10 +664,10 @@ class OmnivoreSettingTab extends PluginSettingTab {
toggle
.setValue(this.plugin.settings.isSingleFile)
.onChange(async (value) => {
- this.plugin.settings.isSingleFile = value;
- await this.plugin.saveSettings();
+ this.plugin.settings.isSingleFile = value
+ await this.plugin.saveSettings()
})
- );
+ )
new Setting(containerEl)
.setName("Filename")
@@ -679,10 +679,10 @@ class OmnivoreSettingTab extends PluginSettingTab {
.setPlaceholder("Enter the filename")
.setValue(this.plugin.settings.filename)
.onChange(async (value) => {
- this.plugin.settings.filename = value;
- await this.plugin.saveSettings();
+ this.plugin.settings.filename = value
+ await this.plugin.saveSettings()
})
- );
+ )
new Setting(containerEl)
.setName("Filename Date Format")
@@ -694,7 +694,7 @@ class OmnivoreSettingTab extends PluginSettingTab {
text: "reference",
href: "https://moment.github.io/luxon/#/formatting?id=table-of-tokens",
})
- );
+ )
})
)
.addText((text) =>
@@ -702,10 +702,10 @@ class OmnivoreSettingTab extends PluginSettingTab {
.setPlaceholder("yyyy-MM-dd")
.setValue(this.plugin.settings.filenameDateFormat)
.onChange(async (value) => {
- this.plugin.settings.filenameDateFormat = value;
- await this.plugin.saveSettings();
+ this.plugin.settings.filenameDateFormat = value
+ await this.plugin.saveSettings()
})
- );
+ )
new Setting(containerEl)
.setName("Folder Date Format")
@@ -717,7 +717,7 @@ class OmnivoreSettingTab extends PluginSettingTab {
text: "reference",
href: "https://moment.github.io/luxon/#/formatting?id=table-of-tokens",
})
- );
+ )
})
)
.addText((text) =>
@@ -725,10 +725,10 @@ class OmnivoreSettingTab extends PluginSettingTab {
.setPlaceholder("yyyy-MM-dd")
.setValue(this.plugin.settings.folderDateFormat)
.onChange(async (value) => {
- this.plugin.settings.folderDateFormat = value;
- await this.plugin.saveSettings();
+ this.plugin.settings.folderDateFormat = value
+ await this.plugin.saveSettings()
})
- );
+ )
new Setting(containerEl)
.setName("Date Saved Format")
.setDesc(
@@ -739,10 +739,10 @@ class OmnivoreSettingTab extends PluginSettingTab {
.setPlaceholder("yyyy-MM-dd'T'HH:mm:ss")
.setValue(this.plugin.settings.dateSavedFormat)
.onChange(async (value) => {
- this.plugin.settings.dateSavedFormat = value;
- await this.plugin.saveSettings();
+ this.plugin.settings.dateSavedFormat = value
+ await this.plugin.saveSettings()
})
- );
+ )
new Setting(containerEl)
.setName("Date Highlighted Format")
.setDesc(
@@ -753,19 +753,19 @@ class OmnivoreSettingTab extends PluginSettingTab {
.setPlaceholder("Date Highlighted Format")
.setValue(this.plugin.settings.dateHighlightedFormat)
.onChange(async (value) => {
- this.plugin.settings.dateHighlightedFormat = value;
- await this.plugin.saveSettings();
+ this.plugin.settings.dateHighlightedFormat = value
+ await this.plugin.saveSettings()
})
- );
+ )
containerEl.createEl("h5", {
cls: "omnivore-collapsible",
text: "Advanced Settings",
- });
+ })
const advancedSettings = containerEl.createEl("div", {
cls: "omnivore-content",
- });
+ })
new Setting(advancedSettings)
.setName("API Endpoint")
@@ -775,10 +775,10 @@ class OmnivoreSettingTab extends PluginSettingTab {
.setPlaceholder("API endpoint")
.setValue(this.plugin.settings.endpoint)
.onChange(async (value) => {
- this.plugin.settings.endpoint = value;
- await this.plugin.saveSettings();
+ this.plugin.settings.endpoint = value
+ await this.plugin.saveSettings()
})
- );
+ )
new Setting(advancedSettings)
.setName("Front Matter Template")
@@ -796,7 +796,7 @@ class OmnivoreSettingTab extends PluginSettingTab {
fragment.createEl("br"),
fragment.createEl("br"),
"If this template is set, it will override the Front Matter so please make sure your template is a valid YAML."
- );
+ )
})
)
.addTextArea((text) => {
@@ -804,12 +804,12 @@ class OmnivoreSettingTab extends PluginSettingTab {
.setPlaceholder("Enter the template")
.setValue(this.plugin.settings.frontMatterTemplate)
.onChange(async (value) => {
- this.plugin.settings.frontMatterTemplate = value;
- await this.plugin.saveSettings();
- });
+ this.plugin.settings.frontMatterTemplate = value
+ await this.plugin.saveSettings()
+ })
- text.inputEl.setAttr("rows", 10);
- text.inputEl.setAttr("cols", 30);
+ text.inputEl.setAttr("rows", 10)
+ text.inputEl.setAttr("cols", 30)
})
.addExtraButton((button) => {
// add a button to reset template
@@ -818,30 +818,30 @@ class OmnivoreSettingTab extends PluginSettingTab {
.setTooltip("Reset front matter template")
.onClick(async () => {
this.plugin.settings.frontMatterTemplate =
- DEFAULT_SETTINGS.frontMatterTemplate;
- await this.plugin.saveSettings();
- this.display();
- new Notice("Front matter template reset");
- });
- });
+ DEFAULT_SETTINGS.frontMatterTemplate
+ await this.plugin.saveSettings()
+ this.display()
+ new Notice("Front matter template reset")
+ })
+ })
- const help = containerEl.createEl("p");
- help.innerHTML = `For more information, please visit our GitHub page, email us at feedback@omnivore.app or join our Discord server.`;
+ const help = containerEl.createEl("p")
+ help.innerHTML = `For more information, please visit our GitHub page, email us at feedback@omnivore.app or join our Discord server.`
// script to make collapsible sections
- const coll = document.getElementsByClassName("omnivore-collapsible");
- let i;
+ const coll = document.getElementsByClassName("omnivore-collapsible")
+ let i
for (i = 0; i < coll.length; i++) {
coll[i].addEventListener("click", function () {
- this.classList.toggle("omnivore-active");
- const content = this.nextElementSibling;
+ this.classList.toggle("omnivore-active")
+ const content = this.nextElementSibling
if (content.style.maxHeight) {
- content.style.maxHeight = null;
+ content.style.maxHeight = null
} else {
- content.style.maxHeight = "fit-content";
+ content.style.maxHeight = "fit-content"
}
- });
+ })
}
}
}
diff --git a/src/settings/file-suggest.ts b/src/settings/file-suggest.ts
index b5b5463..b697e8a 100644
--- a/src/settings/file-suggest.ts
+++ b/src/settings/file-suggest.ts
@@ -1,64 +1,64 @@
// Credits go to Liam's Periodic Notes Plugin: https://github.com/liamcain/obsidian-periodic-notes
-import { TAbstractFile, TFile, TFolder } from "obsidian";
+import { TAbstractFile, TFile, TFolder } from 'obsidian'
-import { TextInputSuggest } from "./suggest";
+import { TextInputSuggest } from './suggest'
export class FileSuggest extends TextInputSuggest {
getSuggestions(inputStr: string): TFile[] {
- const abstractFiles = this.app.vault.getAllLoadedFiles();
- const files: TFile[] = [];
- const lowerCaseInputStr = inputStr.toLowerCase();
+ const abstractFiles = this.app.vault.getAllLoadedFiles()
+ const files: TFile[] = []
+ const lowerCaseInputStr = inputStr.toLowerCase()
abstractFiles.forEach((file: TAbstractFile) => {
if (
file instanceof TFile &&
- file.extension === "md" &&
+ file.extension === 'md' &&
file.path.toLowerCase().contains(lowerCaseInputStr)
) {
- files.push(file);
+ files.push(file)
}
- });
+ })
- return files;
+ return files
}
renderSuggestion(file: TFile, el: HTMLElement): void {
- el.setText(file.path);
+ el.setText(file.path)
}
selectSuggestion(file: TFile): void {
- this.inputEl.value = file.path;
- this.inputEl.trigger("input");
- this.close();
+ this.inputEl.value = file.path
+ this.inputEl.trigger('input')
+ this.close()
}
}
export class FolderSuggest extends TextInputSuggest {
getSuggestions(inputStr: string): TFolder[] {
- const abstractFiles = this.app.vault.getAllLoadedFiles();
- const folders: TFolder[] = [];
- const lowerCaseInputStr = inputStr.toLowerCase();
+ const abstractFiles = this.app.vault.getAllLoadedFiles()
+ const folders: TFolder[] = []
+ const lowerCaseInputStr = inputStr.toLowerCase()
abstractFiles.forEach((folder: TAbstractFile) => {
if (
folder instanceof TFolder &&
folder.path.toLowerCase().contains(lowerCaseInputStr)
) {
- folders.push(folder);
+ folders.push(folder)
}
- });
+ })
- return folders;
+ return folders
}
renderSuggestion(file: TFolder, el: HTMLElement): void {
- el.setText(file.path);
+ el.setText(file.path)
}
selectSuggestion(file: TFolder): void {
- this.inputEl.value = file.path;
- this.inputEl.trigger("input");
- this.close();
+ this.inputEl.value = file.path
+ this.inputEl.trigger('input')
+ this.close()
}
}
diff --git a/src/settings/index.ts b/src/settings/index.ts
index e7452c1..5e98d5e 100644
--- a/src/settings/index.ts
+++ b/src/settings/index.ts
@@ -1,79 +1,79 @@
-import { DEFAULT_TEMPLATE } from "./template";
+import { DEFAULT_TEMPLATE } from './template'
export const FRONT_MATTER_VARIABLES = [
- "title",
- "author",
- "tags",
- "date_saved",
- "date_published",
- "omnivore_url",
- "site_name",
- "original_url",
- "description",
- "note",
- "type",
- "date_read",
- "words_count",
- "read_length",
- "state",
- "date_archived",
-];
+ 'title',
+ 'author',
+ 'tags',
+ 'date_saved',
+ 'date_published',
+ 'omnivore_url',
+ 'site_name',
+ 'original_url',
+ 'description',
+ 'note',
+ 'type',
+ 'date_read',
+ 'words_count',
+ 'read_length',
+ 'state',
+ 'date_archived',
+]
export const DEFAULT_SETTINGS: OmnivoreSettings = {
- dateHighlightedFormat: "yyyy-MM-dd HH:mm:ss",
- dateSavedFormat: "yyyy-MM-dd HH:mm:ss",
- apiKey: "",
- filter: "HIGHLIGHTS",
- syncAt: "",
- customQuery: "",
+ dateHighlightedFormat: 'yyyy-MM-dd HH:mm:ss',
+ dateSavedFormat: 'yyyy-MM-dd HH:mm:ss',
+ apiKey: '',
+ filter: 'HIGHLIGHTS',
+ syncAt: '',
+ customQuery: '',
template: DEFAULT_TEMPLATE,
- highlightOrder: "LOCATION",
+ highlightOrder: 'LOCATION',
syncing: false,
- folder: "Omnivore/{{{date}}}",
- folderDateFormat: "yyyy-MM-dd",
- endpoint: "https://api-prod.omnivore.app/api/graphql",
- filename: "{{{title}}}",
- filenameDateFormat: "yyyy-MM-dd",
- attachmentFolder: "Omnivore/attachments",
- version: "0.0.0",
+ folder: 'Omnivore/{{{date}}}',
+ folderDateFormat: 'yyyy-MM-dd',
+ endpoint: 'https://api-prod.omnivore.app/api/graphql',
+ filename: '{{{title}}}',
+ filenameDateFormat: 'yyyy-MM-dd',
+ attachmentFolder: 'Omnivore/attachments',
+ version: '0.0.0',
isSingleFile: false,
frequency: 0,
intervalId: 0,
frontMatterVariables: [],
- frontMatterTemplate: "",
-};
+ frontMatterTemplate: '',
+}
export enum Filter {
- ALL = "import all my articles",
- HIGHLIGHTS = "import just highlights",
- ADVANCED = "advanced",
+ ALL = 'import all my articles',
+ HIGHLIGHTS = 'import just highlights',
+ ADVANCED = 'advanced',
}
export enum HighlightOrder {
- LOCATION = "the location of highlights in the article",
- TIME = "the time that highlights are updated",
+ LOCATION = 'the location of highlights in the article',
+ TIME = 'the time that highlights are updated',
}
export interface OmnivoreSettings {
- apiKey: string;
- filter: string;
- syncAt: string;
- customQuery: string;
- highlightOrder: string;
- template: string;
- syncing: boolean;
- folder: string;
- folderDateFormat: string;
- endpoint: string;
- dateHighlightedFormat: string;
- dateSavedFormat: string;
- filename: string;
- attachmentFolder: string;
- version: string;
- isSingleFile: boolean;
- frequency: number;
- intervalId: number;
- frontMatterVariables: string[];
- frontMatterTemplate: string;
- filenameDateFormat: string;
+ apiKey: string
+ filter: string
+ syncAt: string
+ customQuery: string
+ highlightOrder: string
+ template: string
+ syncing: boolean
+ folder: string
+ folderDateFormat: string
+ endpoint: string
+ dateHighlightedFormat: string
+ dateSavedFormat: string
+ filename: string
+ attachmentFolder: string
+ version: string
+ isSingleFile: boolean
+ frequency: number
+ intervalId: number
+ frontMatterVariables: string[]
+ frontMatterTemplate: string
+ filenameDateFormat: string
}
diff --git a/src/settings/suggest.ts b/src/settings/suggest.ts
index fc0f29d..bceea2e 100644
--- a/src/settings/suggest.ts
+++ b/src/settings/suggest.ts
@@ -1,179 +1,187 @@
// Credits go to Liam's Periodic Notes Plugin: https://github.com/liamcain/obsidian-periodic-notes
-import { createPopper, type Instance as PopperInstance } from "@popperjs/core";
-import { App, type ISuggestOwner, Scope } from "obsidian";
-import { wrapAround } from "../util";
+import { createPopper, type Instance as PopperInstance } from '@popperjs/core'
+import { App, type ISuggestOwner, Scope } from 'obsidian'
+import { wrapAround } from '../util'
class Suggest {
- private owner: ISuggestOwner;
- private values: T[];
- private suggestions: HTMLDivElement[];
- private selectedItem: number;
- private containerEl: HTMLElement;
+ private owner: ISuggestOwner
+ private values: T[]
+ private suggestions: HTMLDivElement[]
+ private selectedItem: number
+ private containerEl: HTMLElement
constructor(owner: ISuggestOwner, containerEl: HTMLElement, scope: Scope) {
- this.owner = owner;
- this.containerEl = containerEl;
+ this.owner = owner
+ this.containerEl = containerEl
- containerEl.on("click", ".suggestion-item", this.onSuggestionClick.bind(this));
containerEl.on(
- "mousemove",
- ".suggestion-item",
+ 'click',
+ '.suggestion-item',
+ this.onSuggestionClick.bind(this)
+ )
+ containerEl.on(
+ 'mousemove',
+ '.suggestion-item',
this.onSuggestionMouseover.bind(this)
- );
+ )
- scope.register([], "ArrowUp", (event) => {
+ scope.register([], 'ArrowUp', (event) => {
if (!event.isComposing) {
- this.setSelectedItem(this.selectedItem - 1, true);
- return false;
+ this.setSelectedItem(this.selectedItem - 1, true)
+ return false
}
- });
+ })
- scope.register([], "ArrowDown", (event) => {
+ scope.register([], 'ArrowDown', (event) => {
if (!event.isComposing) {
- this.setSelectedItem(this.selectedItem + 1, true);
- return false;
+ this.setSelectedItem(this.selectedItem + 1, true)
+ return false
}
- });
+ })
- scope.register([], "Enter", (event) => {
+ scope.register([], 'Enter', (event) => {
if (!event.isComposing) {
- this.useSelectedItem(event);
- return false;
+ this.useSelectedItem(event)
+ return false
}
- });
+ })
}
onSuggestionClick(event: MouseEvent, el: HTMLDivElement): void {
- event.preventDefault();
+ event.preventDefault()
- const item = this.suggestions.indexOf(el);
- this.setSelectedItem(item, false);
- this.useSelectedItem(event);
+ const item = this.suggestions.indexOf(el)
+ this.setSelectedItem(item, false)
+ this.useSelectedItem(event)
}
onSuggestionMouseover(_event: MouseEvent, el: HTMLDivElement): void {
- const item = this.suggestions.indexOf(el);
- this.setSelectedItem(item, false);
+ const item = this.suggestions.indexOf(el)
+ this.setSelectedItem(item, false)
}
setSuggestions(values: T[]) {
- this.containerEl.empty();
- const suggestionEls: HTMLDivElement[] = [];
+ this.containerEl.empty()
+ const suggestionEls: HTMLDivElement[] = []
values.forEach((value) => {
- const suggestionEl = this.containerEl.createDiv("suggestion-item");
- this.owner.renderSuggestion(value, suggestionEl);
- suggestionEls.push(suggestionEl);
- });
-
- this.values = values;
- this.suggestions = suggestionEls;
- this.setSelectedItem(0, false);
+ const suggestionEl = this.containerEl.createDiv('suggestion-item')
+ this.owner.renderSuggestion(value, suggestionEl)
+ suggestionEls.push(suggestionEl)
+ })
+
+ this.values = values
+ this.suggestions = suggestionEls
+ this.setSelectedItem(0, false)
}
useSelectedItem(event: MouseEvent | KeyboardEvent) {
- const currentValue = this.values[this.selectedItem];
+ const currentValue = this.values[this.selectedItem]
if (currentValue) {
- this.owner.selectSuggestion(currentValue, event);
+ this.owner.selectSuggestion(currentValue, event)
}
}
setSelectedItem(selectedIndex: number, scrollIntoView: boolean) {
- const normalizedIndex = wrapAround(selectedIndex, this.suggestions.length);
- const prevSelectedSuggestion = this.suggestions[this.selectedItem];
- const selectedSuggestion = this.suggestions[normalizedIndex];
+ const normalizedIndex = wrapAround(selectedIndex, this.suggestions.length)
+ const prevSelectedSuggestion = this.suggestions[this.selectedItem]
+ const selectedSuggestion = this.suggestions[normalizedIndex]
- prevSelectedSuggestion?.removeClass("is-selected");
- selectedSuggestion?.addClass("is-selected");
+ prevSelectedSuggestion?.removeClass('is-selected')
+ selectedSuggestion?.addClass('is-selected')
- this.selectedItem = normalizedIndex;
+ this.selectedItem = normalizedIndex
if (scrollIntoView) {
- selectedSuggestion.scrollIntoView(false);
+ selectedSuggestion.scrollIntoView(false)
}
}
}
export abstract class TextInputSuggest implements ISuggestOwner {
- protected app: App;
- protected inputEl: HTMLInputElement;
+ protected app: App
+ protected inputEl: HTMLInputElement
- private popper: PopperInstance;
- private scope: Scope;
- private suggestEl: HTMLElement;
- private suggest: Suggest;
+ private popper: PopperInstance
+ private scope: Scope
+ private suggestEl: HTMLElement
+ private suggest: Suggest
constructor(app: App, inputEl: HTMLInputElement) {
- this.app = app;
- this.inputEl = inputEl;
- this.scope = new Scope();
-
- this.suggestEl = createDiv("suggestion-container");
- const suggestion = this.suggestEl.createDiv("suggestion");
- this.suggest = new Suggest(this, suggestion, this.scope);
-
- this.scope.register([], "Escape", this.close.bind(this));
-
- this.inputEl.addEventListener("input", this.onInputChanged.bind(this));
- this.inputEl.addEventListener("focus", this.onInputChanged.bind(this));
- this.inputEl.addEventListener("blur", this.close.bind(this));
- this.suggestEl.on("mousedown", ".suggestion-container", (event: MouseEvent) => {
- event.preventDefault();
- });
+ this.app = app
+ this.inputEl = inputEl
+ this.scope = new Scope()
+
+ this.suggestEl = createDiv('suggestion-container')
+ const suggestion = this.suggestEl.createDiv('suggestion')
+ this.suggest = new Suggest(this, suggestion, this.scope)
+
+ this.scope.register([], 'Escape', this.close.bind(this))
+
+ this.inputEl.addEventListener('input', this.onInputChanged.bind(this))
+ this.inputEl.addEventListener('focus', this.onInputChanged.bind(this))
+ this.inputEl.addEventListener('blur', this.close.bind(this))
+ this.suggestEl.on(
+ 'mousedown',
+ '.suggestion-container',
+ (event: MouseEvent) => {
+ event.preventDefault()
+ }
+ )
}
onInputChanged(): void {
- const inputStr = this.inputEl.value;
- const suggestions = this.getSuggestions(inputStr);
+ const inputStr = this.inputEl.value
+ const suggestions = this.getSuggestions(inputStr)
if (suggestions.length > 0) {
- this.suggest.setSuggestions(suggestions);
+ this.suggest.setSuggestions(suggestions)
// eslint-disable-next-line @typescript-eslint/no-explicit-any
- this.open((this.app).dom.appContainerEl, this.inputEl);
+ this.open((this.app).dom.appContainerEl, this.inputEl)
}
}
open(container: HTMLElement, inputEl: HTMLElement): void {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
- (this.app).keymap.pushScope(this.scope);
+ (this.app).keymap.pushScope(this.scope)
- container.appendChild(this.suggestEl);
+ container.appendChild(this.suggestEl)
this.popper = createPopper(inputEl, this.suggestEl, {
- placement: "bottom-start",
+ placement: 'bottom-start',
modifiers: [
{
- name: "sameWidth",
+ name: 'sameWidth',
enabled: true,
fn: ({ state, instance }) => {
// Note: positioning needs to be calculated twice -
// first pass - positioning it according to the width of the popper
// second pass - position it with the width bound to the reference element
// we need to early exit to avoid an infinite loop
- const targetWidth = `${state.rects.reference.width}px`;
+ const targetWidth = `${state.rects.reference.width}px`
if (state.styles.popper.width === targetWidth) {
- return;
+ return
}
- state.styles.popper.width = targetWidth;
- instance.update();
+ state.styles.popper.width = targetWidth
+ instance.update()
},
- phase: "beforeWrite",
- requires: ["computeStyles"],
+ phase: 'beforeWrite',
+ requires: ['computeStyles'],
},
],
- });
+ })
}
close(): void {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
- (this.app).keymap.popScope(this.scope);
+ (this.app).keymap.popScope(this.scope)
- this.suggest.setSuggestions([]);
- this.popper.destroy();
- this.suggestEl.detach();
+ this.suggest.setSuggestions([])
+ this.popper.destroy()
+ this.suggestEl.detach()
}
- abstract getSuggestions(inputStr: string): T[];
- abstract renderSuggestion(item: T, el: HTMLElement): void;
- abstract selectSuggestion(item: T): void;
+ abstract getSuggestions(inputStr: string): T[]
+ abstract renderSuggestion(item: T, el: HTMLElement): void
+ abstract selectSuggestion(item: T): void
}
diff --git a/src/settings/template.ts b/src/settings/template.ts
index b3411d0..4b739ac 100644
--- a/src/settings/template.ts
+++ b/src/settings/template.ts
@@ -1,7 +1,7 @@
-import { truncate } from "lodash";
-import Mustache from "mustache";
-import { parseYaml, stringifyYaml } from "obsidian";
-import { Article, HighlightType, PageType } from "../api";
+import { truncate } from 'lodash'
+import Mustache from 'mustache'
+import { parseYaml, stringifyYaml } from 'obsidian'
+import { Article, HighlightType, PageType } from '../api'
import {
compareHighlightsInFile,
formatDate,
@@ -10,14 +10,14 @@ import {
removeFrontMatterFromContent,
siteNameFromUrl,
snakeToCamelCase,
-} from "../util";
+} from '../util'
type FunctionMap = {
[key: string]: () => (
text: string,
render: (text: string) => string
- ) => string;
-};
+ ) => string
+}
export const DEFAULT_TEMPLATE = `# {{{title}}}
#Omnivore
@@ -36,99 +36,99 @@ export const DEFAULT_TEMPLATE = `# {{{title}}}
{{/note}}
{{/highlights}}
-{{/highlights.length}}`;
+{{/highlights.length}}`
export interface LabelView {
- name: string;
+ name: string
}
export interface HighlightView {
- text: string;
- highlightUrl: string;
- highlightID: string;
- dateHighlighted: string;
- note?: string;
- labels?: LabelView[];
- color: string;
- positionPercent: number;
- positionAnchorIndex: number;
+ text: string
+ highlightUrl: string
+ highlightID: string
+ dateHighlighted: string
+ note?: string
+ labels?: LabelView[]
+ color: string
+ positionPercent: number
+ positionAnchorIndex: number
}
export type ArticleView =
| {
- id: string;
- title: string;
- omnivoreUrl: string;
- siteName: string;
- originalUrl: string;
- author?: string;
- labels?: LabelView[];
- dateSaved: string;
- highlights: HighlightView[];
- content?: string;
- datePublished?: string;
- fileAttachment?: string;
- description?: string;
- note?: string;
- type: PageType;
- dateRead?: string;
- wordsCount?: number;
- readLength?: number;
- state: string;
- dateArchived?: string;
+ id: string
+ title: string
+ omnivoreUrl: string
+ siteName: string
+ originalUrl: string
+ author?: string
+ labels?: LabelView[]
+ dateSaved: string
+ highlights: HighlightView[]
+ content?: string
+ datePublished?: string
+ fileAttachment?: string
+ description?: string
+ note?: string
+ type: PageType
+ dateRead?: string
+ wordsCount?: number
+ readLength?: number
+ state: string
+ dateArchived?: string
}
- | FunctionMap;
+ | FunctionMap
enum ArticleState {
- Inbox = "INBOX",
- Reading = "READING",
- Completed = "COMPLETED",
- Archived = "ARCHIVED",
+ Inbox = 'INBOX',
+ Reading = 'READING',
+ Completed = 'COMPLETED',
+ Archived = 'ARCHIVED',
}
const getArticleState = (article: Article): string => {
if (article.isArchived) {
- return ArticleState.Archived;
+ return ArticleState.Archived
}
if (article.readingProgressPercent > 0) {
return article.readingProgressPercent === 100
? ArticleState.Completed
- : ArticleState.Reading;
+ : ArticleState.Reading
}
- return ArticleState.Inbox;
-};
+ return ArticleState.Inbox
+}
function lowerCase() {
return function (text: string, render: (text: string) => string) {
- return render(text).toLowerCase();
- };
+ return render(text).toLowerCase()
+ }
}
function upperCase() {
return function (text: string, render: (text: string) => string) {
- return render(text).toUpperCase();
- };
+ return render(text).toUpperCase()
+ }
}
function upperCaseFirst() {
return function (text: string, render: (text: string) => string) {
- const str = render(text);
- return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
- };
+ const str = render(text)
+ return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase()
+ }
}
function formatDateFunc() {
return function (text: string, render: (text: string) => string) {
// get the date and format from the text
- const [dateVariable, format] = text.split(",", 2);
- const date = render(dateVariable);
+ const [dateVariable, format] = text.split(',', 2)
+ const date = render(dateVariable)
if (!date) {
- return "";
+ return ''
}
// format the date
- return formatDate(date, format);
- };
+ return formatDate(date, format)
+ }
}
const functionMap: FunctionMap = {
@@ -136,38 +136,38 @@ const functionMap: FunctionMap = {
upperCase,
upperCaseFirst,
formatDate: formatDateFunc,
-};
+}
export const renderFilename = (
article: Article,
filename: string,
dateFormat: string
) => {
- const date = formatDate(article.savedAt, dateFormat);
+ const date = formatDate(article.savedAt, dateFormat)
const datePublished = article.publishedAt
? formatDate(article.publishedAt, dateFormat).trim()
- : undefined;
+ : undefined
const renderedFilename = Mustache.render(filename, {
title: article.title,
- author: article.author ?? "unknown-author",
+ author: article.author ?? 'unknown-author',
date,
dateSaved: date,
datePublished,
id: article.id,
- });
+ })
// truncate the filename to 100 characters
return truncate(renderedFilename, {
length: 100,
- });
-};
+ })
+}
export const renderLabels = (labels?: LabelView[]) => {
return labels?.map((l) => ({
// replace spaces with underscores because Obsidian doesn't allow spaces in tags
- name: l.name.replaceAll(" ", "_"),
- }));
-};
+ name: l.name.replaceAll(' ', '_'),
+ }))
+}
export const renderArticleContnet = async (
article: Article,
@@ -182,22 +182,22 @@ export const renderArticleContnet = async (
) => {
// filter out notes and redactions
const articleHighlights =
- article.highlights?.filter((h) => h.type === HighlightType.Highlight) || [];
+ article.highlights?.filter((h) => h.type === HighlightType.Highlight) || []
// sort highlights by location if selected in options
- if (highlightOrder === "LOCATION") {
+ if (highlightOrder === 'LOCATION') {
articleHighlights.sort((a, b) => {
try {
if (article.pageType === PageType.File) {
// sort by location in file
- return compareHighlightsInFile(a, b);
+ return compareHighlightsInFile(a, b)
}
// for web page, sort by location in the page
- return getHighlightLocation(a.patch) - getHighlightLocation(b.patch);
+ return getHighlightLocation(a.patch) - getHighlightLocation(b.patch)
} catch (e) {
- console.error(e);
- return compareHighlightsInFile(a, b);
+ console.error(e)
+ return compareHighlightsInFile(a, b)
}
- });
+ })
}
const highlights: HighlightView[] = articleHighlights.map((highlight) => {
return {
@@ -210,25 +210,25 @@ export const renderArticleContnet = async (
color: highlight.color ?? 'yellow',
positionPercent: highlight.highlightPositionPercent,
positionAnchorIndex: highlight.highlightPositionAnchorIndex + 1, // PDF page numbers start at 1
- };
- });
- const dateSaved = formatDate(article.savedAt, dateSavedFormat);
+ }
+ })
+ const dateSaved = formatDate(article.savedAt, dateSavedFormat)
const siteName =
- article.siteName || siteNameFromUrl(article.originalArticleUrl);
- const publishedAt = article.publishedAt;
+ article.siteName || siteNameFromUrl(article.originalArticleUrl)
+ const publishedAt = article.publishedAt
const datePublished = publishedAt
? formatDate(publishedAt, dateSavedFormat).trim()
- : undefined;
+ : undefined
const articleNote = article.highlights?.find(
(h) => h.type === HighlightType.Note
- );
+ )
const dateRead = article.readAt
? formatDate(article.readAt, dateSavedFormat).trim()
- : undefined;
- const wordsCount = article.wordsCount;
+ : undefined
+ const wordsCount = article.wordsCount
const readLength = wordsCount
? Math.round(Math.max(1, wordsCount / 235))
- : undefined;
+ : undefined
const articleView: ArticleView = {
id: article.id,
title: article.title,
@@ -251,101 +251,101 @@ export const renderArticleContnet = async (
state: getArticleState(article),
dateArchived: article.archivedAt,
...functionMap,
- };
+ }
let frontMatter: { [id: string]: unknown } = {
id: article.id, // id is required for deduplication
- };
+ }
// if the front matter template is set, use it
if (frontMatterTemplate) {
const frontMatterTemplateRendered = Mustache.render(
frontMatterTemplate,
articleView
- );
+ )
try {
// parse the front matter template as yaml
- const frontMatterParsed = parseYaml(frontMatterTemplateRendered);
+ const frontMatterParsed = parseYaml(frontMatterTemplateRendered)
frontMatter = {
...frontMatterParsed,
...frontMatter,
- };
+ }
} catch (error) {
// if there's an error parsing the front matter template, log it
- console.error("Error parsing front matter template", error);
+ console.error('Error parsing front matter template', error)
// and add the error to the front matter
frontMatter = {
...frontMatter,
omnivore_error:
- "There was an error parsing the front matter template. See console for details.",
- };
+ 'There was an error parsing the front matter template. See console for details.',
+ }
}
} else {
// otherwise, use the front matter variables
for (const item of frontMatterVariables) {
// split the item into variable and alias
- const aliasedVariables = item.split("::");
- const variable = aliasedVariables[0];
+ const aliasedVariables = item.split('::')
+ const variable = aliasedVariables[0]
// we use snake case for variables in the front matter
- const articleVariable = snakeToCamelCase(variable);
+ const articleVariable = snakeToCamelCase(variable)
// use alias if available, otherwise use variable
- const key = aliasedVariables[1] || variable;
+ const key = aliasedVariables[1] || variable
if (
- variable === "tags" &&
+ variable === 'tags' &&
articleView.labels &&
articleView.labels.length > 0
) {
// tags are handled separately
// use label names as tags
- frontMatter[key] = articleView.labels.map((l) => l.name);
- continue;
+ frontMatter[key] = articleView.labels.map((l) => l.name)
+ continue
}
- const value = (articleView as any)[articleVariable];
+ const value = (articleView as any)[articleVariable]
if (value) {
// if variable is in article, use it
- frontMatter[key] = value;
+ frontMatter[key] = value
}
}
}
// Build content string based on template
- const content = Mustache.render(template, articleView);
- let contentWithoutFrontMatter = removeFrontMatterFromContent(content);
- let frontMatterYaml = stringifyYaml(frontMatter);
+ const content = Mustache.render(template, articleView)
+ let contentWithoutFrontMatter = removeFrontMatterFromContent(content)
+ let frontMatterYaml = stringifyYaml(frontMatter)
if (isSingleFile) {
// wrap the content without front matter in comments
- const sectionStart = `%%${article.id}_start%%`;
- const sectionEnd = `%%${article.id}_end%%`;
- contentWithoutFrontMatter = `${sectionStart}\n${contentWithoutFrontMatter}\n${sectionEnd}`;
+ const sectionStart = `%%${article.id}_start%%`
+ const sectionEnd = `%%${article.id}_end%%`
+ contentWithoutFrontMatter = `${sectionStart}\n${contentWithoutFrontMatter}\n${sectionEnd}`
// if single file, wrap the front matter in an array
- frontMatterYaml = stringifyYaml([frontMatter]);
+ frontMatterYaml = stringifyYaml([frontMatter])
}
- const frontMatterStr = `---\n${frontMatterYaml}---`;
+ const frontMatterStr = `---\n${frontMatterYaml}---`
- return `${frontMatterStr}\n\n${contentWithoutFrontMatter}`;
-};
+ return `${frontMatterStr}\n\n${contentWithoutFrontMatter}`
+}
export const renderFolderName = (
article: Article,
template: string,
dateFormat: string
) => {
- const date = formatDate(article.savedAt, dateFormat);
+ const date = formatDate(article.savedAt, dateFormat)
const datePublished = article.publishedAt
? formatDate(article.publishedAt, dateFormat).trim()
- : undefined;
+ : undefined
return Mustache.render(template, {
date,
dateSaved: date,
datePublished,
- author: article.author ?? "unknown-author",
- });
-};
+ author: article.author ?? 'unknown-author',
+ })
+}
export const preParseTemplate = (template: string) => {
- return Mustache.parse(template);
-};
+ return Mustache.parse(template)
+}
diff --git a/src/util.ts b/src/util.ts
index 6ce8e2b..3e2e7b2 100644
--- a/src/util.ts
+++ b/src/util.ts
@@ -1,17 +1,17 @@
-import { diff_match_patch } from "diff-match-patch";
-import { DateTime } from "luxon";
-import escape from "markdown-escape";
-import { parseYaml } from "obsidian";
-import outOfCharacter from "out-of-character";
-import { Highlight } from "./api";
-
-export const DATE_FORMAT_W_OUT_SECONDS = "yyyy-MM-dd'T'HH:mm";
-export const DATE_FORMAT = `${DATE_FORMAT_W_OUT_SECONDS}:ss`;
-export const REPLACEMENT_CHAR = "-";
+import { diff_match_patch } from "diff-match-patch"
+import { DateTime } from "luxon"
+import escape from "markdown-escape"
+import { parseYaml } from "obsidian"
+import outOfCharacter from "out-of-character"
+import { Highlight } from "./api"
+
+export const DATE_FORMAT_W_OUT_SECONDS = "yyyy-MM-dd'T'HH:mm"
+export const DATE_FORMAT = `${DATE_FORMAT_W_OUT_SECONDS}:ss`
+export const REPLACEMENT_CHAR = "-"
// On Unix-like systems / is reserved and <>:"/\|?* as well as non-printable characters \u0000-\u001F on Windows
// credit: https://github.com/sindresorhus/filename-reserved-regex
// eslint-disable-next-line no-control-regex
-export const ILLEGAL_CHAR_REGEX = /[<>:"/\\|?*\u0000-\u001F]/g;
+export const ILLEGAL_CHAR_REGEX = /[<>:"/\\|?*\u0000-\u001F]/g
export interface HighlightPoint {
left: number;
@@ -20,60 +20,60 @@ export interface HighlightPoint {
export const getHighlightLocation = (patch: string | null): number => {
if (!patch) {
- return 0;
+ return 0
}
- const dmp = new diff_match_patch();
- const patches = dmp.patch_fromText(patch);
- return patches[0].start1 || 0;
-};
+ const dmp = new diff_match_patch()
+ const patches = dmp.patch_fromText(patch)
+ return patches[0].start1 || 0
+}
export const getHighlightPoint = (patch: string | null): HighlightPoint => {
if (!patch) {
- return { left: 0, top: 0 };
+ return { left: 0, top: 0 }
}
- const { bbox } = JSON.parse(patch) as { bbox: number[] };
+ const { bbox } = JSON.parse(patch) as { bbox: number[] }
if (!bbox || bbox.length !== 4) {
- return { left: 0, top: 0 };
+ return { left: 0, top: 0 }
}
- return { left: bbox[0], top: bbox[1] };
-};
+ return { left: bbox[0], top: bbox[1] }
+}
export const compareHighlightsInFile = (a: Highlight, b: Highlight): number => {
// get the position of the highlight in the file
- const highlightPointA = getHighlightPoint(a.patch);
- const highlightPointB = getHighlightPoint(b.patch);
+ const highlightPointA = getHighlightPoint(a.patch)
+ const highlightPointB = getHighlightPoint(b.patch)
if (highlightPointA.top === highlightPointB.top) {
// if top is same, sort by left
- return highlightPointA.left - highlightPointB.left;
+ return highlightPointA.left - highlightPointB.left
}
// sort by top
- return highlightPointA.top - highlightPointB.top;
-};
+ return highlightPointA.top - highlightPointB.top
+}
export const markdownEscape = (text: string): string => {
try {
- return escape(text);
+ return escape(text)
} catch (e) {
- console.error("markdownEscape error", e);
- return text;
+ console.error("markdownEscape error", e)
+ return text
}
-};
+}
export const escapeQuotationMarks = (text: string): string => {
- return text.replace(/"/g, '\\"');
-};
+ return text.replace(/"/g, '\\"')
+}
export const parseDateTime = (str: string): DateTime => {
- const res = DateTime.fromFormat(str, DATE_FORMAT);
+ const res = DateTime.fromFormat(str, DATE_FORMAT)
if (res.isValid) {
- return res;
+ return res
}
- return DateTime.fromFormat(str, DATE_FORMAT_W_OUT_SECONDS);
-};
+ return DateTime.fromFormat(str, DATE_FORMAT_W_OUT_SECONDS)
+}
export const wrapAround = (value: number, size: number): number => {
- return ((value % size) + size) % size;
-};
+ return ((value % size) + size) % size
+}
export const unicodeSlug = (str: string, savedAt: string) => {
return (
@@ -94,20 +94,20 @@ export const unicodeSlug = (str: string, savedAt: string) => {
.substring(0, 64) +
"-" +
new Date(savedAt).getTime().toString(16)
- );
-};
+ )
+}
export const replaceIllegalChars = (str: string): string => {
return removeInvisibleChars(
str.replace(ILLEGAL_CHAR_REGEX, REPLACEMENT_CHAR)
- );
-};
+ )
+}
export function formatDate(date: string, format: string): string {
if (isNaN(Date.parse(date))) {
- throw new Error(`Invalid date: ${date}`);
+ throw new Error(`Invalid date: ${date}`)
}
- return DateTime.fromJSDate(new Date(date)).toFormat(format);
+ return DateTime.fromJSDate(new Date(date)).toFormat(format)
}
export const getQueryFromFilter = (
@@ -116,68 +116,68 @@ export const getQueryFromFilter = (
): string => {
switch (filter) {
case "ALL":
- return "";
+ return ""
case "HIGHLIGHTS":
- return `has:highlights`;
+ return `has:highlights`
case "ADVANCED":
- return customQuery;
+ return customQuery
default:
- return "";
+ return ""
}
-};
+}
export const siteNameFromUrl = (originalArticleUrl: string): string => {
try {
- return new URL(originalArticleUrl).hostname.replace(/^www\./, "");
+ return new URL(originalArticleUrl).hostname.replace(/^www\./, "")
} catch {
- return "";
+ return ""
}
-};
+}
export const formatHighlightQuote = (
quote: string | null,
template: string
): string => {
if (!quote) {
- return "";
+ return ""
}
// if the template has highlights, we need to preserve paragraphs
- const regex = /{{#highlights}}(\n)*>/gm;
+ const regex = /{{#highlights}}(\n)*>/gm
if (regex.test(template)) {
// replace all empty lines with blockquote '>' to preserve paragraphs
- quote = quote.replaceAll(">", ">").replaceAll(/\n/gm, "\n> ");
+ quote = quote.replaceAll(">", ">").replaceAll(/\n/gm, "\n> ")
}
- return quote;
-};
+ return quote
+}
export const findFrontMatterIndex = (
frontMatter: any[],
id: string
): number => {
// find index of front matter with matching id
- return frontMatter.findIndex((fm) => fm.id == id);
-};
+ return frontMatter.findIndex((fm) => fm.id == id)
+}
export const parseFrontMatterFromContent = (content: string) => {
// get front matter yaml from content
- const frontMatter = content.match(/^---\n(.*?)\n---/s);
+ const frontMatter = content.match(/^---\n(.*?)\n---/s)
if (!frontMatter) {
- return undefined;
+ return undefined
}
// parse yaml
- return parseYaml(frontMatter[1]);
-};
+ return parseYaml(frontMatter[1])
+}
export const removeFrontMatterFromContent = (content: string): string => {
- const frontMatterRegex = /^---.*?---\n*/s;
+ const frontMatterRegex = /^---.*?---\n*/s
- return content.replace(frontMatterRegex, "");
-};
+ return content.replace(frontMatterRegex, "")
+}
export const snakeToCamelCase = (str: string) =>
- str.replace(/(_[a-z])/g, (group) => group.toUpperCase().replace("_", ""));
+ str.replace(/(_[a-z])/g, (group) => group.toUpperCase().replace("_", ""))
const removeInvisibleChars = (str: string): string => {
- return outOfCharacter.replace(str);
-};
+ return outOfCharacter.replace(str)
+}
diff --git a/yarn.lock b/yarn.lock
index 7a2f98c..f21bbc0 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2,6 +2,11 @@
# yarn lockfile v1
+"@aashutoshrathi/word-wrap@^1.2.3":
+ version "1.2.6"
+ resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf"
+ integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==
+
"@ampproject/remapping@^2.2.0":
version "2.2.1"
resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630"
@@ -371,11 +376,62 @@
resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9"
integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==
+"@eslint-community/eslint-utils@^4.2.0":
+ version "4.4.0"
+ resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59"
+ integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==
+ dependencies:
+ eslint-visitor-keys "^3.3.0"
+
+"@eslint-community/regexpp@^4.6.1":
+ version "4.10.0"
+ resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.10.0.tgz#548f6de556857c8bb73bbee70c35dc82a2e74d63"
+ integrity sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==
+
+"@eslint/eslintrc@^2.1.3":
+ version "2.1.3"
+ resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.3.tgz#797470a75fe0fbd5a53350ee715e85e87baff22d"
+ integrity sha512-yZzuIG+jnVu6hNSzFEN07e8BxF3uAzYtQb6uDkaYZLo6oYZDCq454c5kB8zxnzfCYyP4MIuyBn10L0DqwujTmA==
+ dependencies:
+ ajv "^6.12.4"
+ debug "^4.3.2"
+ espree "^9.6.0"
+ globals "^13.19.0"
+ ignore "^5.2.0"
+ import-fresh "^3.2.1"
+ js-yaml "^4.1.0"
+ minimatch "^3.1.2"
+ strip-json-comments "^3.1.1"
+
+"@eslint/js@8.53.0":
+ version "8.53.0"
+ resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.53.0.tgz#bea56f2ed2b5baea164348ff4d5a879f6f81f20d"
+ integrity sha512-Kn7K8dx/5U6+cT1yEhpX1w4PCSg0M+XyRILPgvwcEBjerFWCwQj5sbr3/VmxqV0JGHCBCzyd6LxypEuehypY1w==
+
"@gar/promisify@^1.1.3":
version "1.1.3"
resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6"
integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==
+"@humanwhocodes/config-array@^0.11.13":
+ version "0.11.13"
+ resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.13.tgz#075dc9684f40a531d9b26b0822153c1e832ee297"
+ integrity sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==
+ dependencies:
+ "@humanwhocodes/object-schema" "^2.0.1"
+ debug "^4.1.1"
+ minimatch "^3.0.5"
+
+"@humanwhocodes/module-importer@^1.0.1":
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c"
+ integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==
+
+"@humanwhocodes/object-schema@^2.0.1":
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz#e5211452df060fa8522b55c7b3c0c4d1981cb044"
+ integrity sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==
+
"@isaacs/cliui@^8.0.2":
version "8.0.2"
resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550"
@@ -651,7 +707,7 @@
resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b"
integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==
-"@nodelib/fs.walk@^1.2.3":
+"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8":
version "1.2.8"
resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a"
integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==
@@ -1346,6 +1402,11 @@
"@typescript-eslint/types" "5.29.0"
eslint-visitor-keys "^3.3.0"
+"@ungap/structured-clone@^1.2.0":
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406"
+ integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==
+
JSONStream@^1.0.4:
version "1.3.5"
resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0"
@@ -1359,6 +1420,16 @@ abbrev@^1.0.0, abbrev@~1.1.1:
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==
+acorn-jsx@^5.3.2:
+ version "5.3.2"
+ resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
+ integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
+
+acorn@^8.9.0:
+ version "8.11.2"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.2.tgz#ca0d78b51895be5390a5903c5b3bdcdaf78ae40b"
+ integrity sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==
+
agent-base@6, agent-base@^6.0.2:
version "6.0.2"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77"
@@ -1390,6 +1461,16 @@ aggregate-error@^3.0.0:
clean-stack "^2.0.0"
indent-string "^4.0.0"
+ajv@^6.12.4:
+ version "6.12.6"
+ resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
+ integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
+ dependencies:
+ fast-deep-equal "^3.1.1"
+ fast-json-stable-stringify "^2.0.0"
+ json-schema-traverse "^0.4.1"
+ uri-js "^4.2.2"
+
ansi-escapes@^4.2.1:
version "4.3.2"
resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e"
@@ -1476,6 +1557,11 @@ argparse@^1.0.7:
dependencies:
sprintf-js "~1.0.2"
+argparse@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
+ integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
+
argv-formatter@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/argv-formatter/-/argv-formatter-1.0.0.tgz#a0ca0cbc29a5b73e836eebe1cbf6c5e0e4eb82f9"
@@ -1988,7 +2074,7 @@ cosmiconfig@^7.0.0:
path-type "^4.0.0"
yaml "^1.10.0"
-cross-spawn@^7.0.0, cross-spawn@^7.0.3:
+cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3:
version "7.0.3"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
@@ -2012,7 +2098,7 @@ dateformat@^3.0.0:
resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae"
integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==
-debug@4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.3, debug@^4.3.4:
+debug@4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4:
version "4.3.4"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
@@ -2047,6 +2133,11 @@ deep-extend@^0.6.0:
resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac"
integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==
+deep-is@^0.1.3:
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831"
+ integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==
+
deepmerge@^4.2.2:
version "4.3.1"
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a"
@@ -2123,6 +2214,13 @@ dir-glob@^3.0.0, dir-glob@^3.0.1:
dependencies:
path-type "^4.0.0"
+doctrine@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961"
+ integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==
+ dependencies:
+ esutils "^2.0.2"
+
dot-prop@^5.1.0:
version "5.3.0"
resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88"
@@ -2336,6 +2434,11 @@ escape-string-regexp@^2.0.0:
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344"
integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==
+escape-string-regexp@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
+ integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
+
eslint-scope@^5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c"
@@ -2344,6 +2447,14 @@ eslint-scope@^5.1.1:
esrecurse "^4.3.0"
estraverse "^4.1.1"
+eslint-scope@^7.2.2:
+ version "7.2.2"
+ resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f"
+ integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==
+ dependencies:
+ esrecurse "^4.3.0"
+ estraverse "^5.2.0"
+
eslint-utils@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672"
@@ -2361,11 +2472,76 @@ eslint-visitor-keys@^3.3.0:
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz#c22c48f48942d08ca824cc526211ae400478a994"
integrity sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==
+eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3:
+ version "3.4.3"
+ resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800"
+ integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==
+
+eslint@^8.53.0:
+ version "8.53.0"
+ resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.53.0.tgz#14f2c8244298fcae1f46945459577413ba2697ce"
+ integrity sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag==
+ dependencies:
+ "@eslint-community/eslint-utils" "^4.2.0"
+ "@eslint-community/regexpp" "^4.6.1"
+ "@eslint/eslintrc" "^2.1.3"
+ "@eslint/js" "8.53.0"
+ "@humanwhocodes/config-array" "^0.11.13"
+ "@humanwhocodes/module-importer" "^1.0.1"
+ "@nodelib/fs.walk" "^1.2.8"
+ "@ungap/structured-clone" "^1.2.0"
+ ajv "^6.12.4"
+ chalk "^4.0.0"
+ cross-spawn "^7.0.2"
+ debug "^4.3.2"
+ doctrine "^3.0.0"
+ escape-string-regexp "^4.0.0"
+ eslint-scope "^7.2.2"
+ eslint-visitor-keys "^3.4.3"
+ espree "^9.6.1"
+ esquery "^1.4.2"
+ esutils "^2.0.2"
+ fast-deep-equal "^3.1.3"
+ file-entry-cache "^6.0.1"
+ find-up "^5.0.0"
+ glob-parent "^6.0.2"
+ globals "^13.19.0"
+ graphemer "^1.4.0"
+ ignore "^5.2.0"
+ imurmurhash "^0.1.4"
+ is-glob "^4.0.0"
+ is-path-inside "^3.0.3"
+ js-yaml "^4.1.0"
+ json-stable-stringify-without-jsonify "^1.0.1"
+ levn "^0.4.1"
+ lodash.merge "^4.6.2"
+ minimatch "^3.1.2"
+ natural-compare "^1.4.0"
+ optionator "^0.9.3"
+ strip-ansi "^6.0.1"
+ text-table "^0.2.0"
+
+espree@^9.6.0, espree@^9.6.1:
+ version "9.6.1"
+ resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f"
+ integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==
+ dependencies:
+ acorn "^8.9.0"
+ acorn-jsx "^5.3.2"
+ eslint-visitor-keys "^3.4.1"
+
esprima@^4.0.0, esprima@~4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
+esquery@^1.4.2:
+ version "1.5.0"
+ resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b"
+ integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==
+ dependencies:
+ estraverse "^5.1.0"
+
esrecurse@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921"
@@ -2378,11 +2554,16 @@ estraverse@^4.1.1:
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d"
integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==
-estraverse@^5.2.0:
+estraverse@^5.1.0, estraverse@^5.2.0:
version "5.3.0"
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123"
integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==
+esutils@^2.0.2:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
+ integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
+
execa@^5.0.0:
version "5.1.1"
resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd"
@@ -2419,6 +2600,11 @@ exponential-backoff@^3.1.1:
resolved "https://registry.yarnpkg.com/exponential-backoff/-/exponential-backoff-3.1.1.tgz#64ac7526fe341ab18a39016cd22c787d01e00bf6"
integrity sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==
+fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
+ version "3.1.3"
+ resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
+ integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
+
fast-glob@^3.2.9:
version "3.2.12"
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80"
@@ -2430,11 +2616,16 @@ fast-glob@^3.2.9:
merge2 "^1.3.0"
micromatch "^4.0.4"
-fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.1.0:
+fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
+fast-levenshtein@^2.0.6:
+ version "2.0.6"
+ resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
+ integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==
+
fastest-levenshtein@^1.0.12:
version "1.0.16"
resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5"
@@ -2468,6 +2659,13 @@ figures@^3.0.0:
dependencies:
escape-string-regexp "^1.0.5"
+file-entry-cache@^6.0.1:
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027"
+ integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==
+ dependencies:
+ flat-cache "^3.0.4"
+
fill-range@^7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
@@ -2490,6 +2688,14 @@ find-up@^4.0.0, find-up@^4.1.0:
locate-path "^5.0.0"
path-exists "^4.0.0"
+find-up@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc"
+ integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==
+ dependencies:
+ locate-path "^6.0.0"
+ path-exists "^4.0.0"
+
find-versions@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/find-versions/-/find-versions-4.0.0.tgz#3c57e573bf97769b8cb8df16934b627915da4965"
@@ -2497,6 +2703,20 @@ find-versions@^4.0.0:
dependencies:
semver-regex "^3.1.2"
+flat-cache@^3.0.4:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.2.0.tgz#2c0c2d5040c99b1632771a9d105725c0115363ee"
+ integrity sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==
+ dependencies:
+ flatted "^3.2.9"
+ keyv "^4.5.3"
+ rimraf "^3.0.2"
+
+flatted@^3.2.9:
+ version "3.2.9"
+ resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.9.tgz#7eb4c67ca1ba34232ca9d2d93e9886e611ad7daf"
+ integrity sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==
+
foreground-child@^3.1.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.1.1.tgz#1d173e776d75d2772fed08efe4a0de1ea1b12d0d"
@@ -2614,6 +2834,13 @@ glob-parent@^5.1.2:
dependencies:
is-glob "^4.0.1"
+glob-parent@^6.0.2:
+ version "6.0.2"
+ resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3"
+ integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==
+ dependencies:
+ is-glob "^4.0.3"
+
glob@7.1.7:
version "7.1.7"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90"
@@ -2665,6 +2892,13 @@ globals@^11.1.0:
resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
+globals@^13.19.0:
+ version "13.23.0"
+ resolved "https://registry.yarnpkg.com/globals/-/globals-13.23.0.tgz#ef31673c926a0976e1f61dab4dca57e0c0a8af02"
+ integrity sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==
+ dependencies:
+ type-fest "^0.20.2"
+
globby@^11.0.0, globby@^11.0.1, globby@^11.1.0:
version "11.1.0"
resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b"
@@ -2687,6 +2921,11 @@ graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.10,
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
+graphemer@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6"
+ integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==
+
handlebars@^4.7.7:
version "4.7.7"
resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1"
@@ -2948,7 +3187,7 @@ is-generator-fn@^2.0.0:
resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118"
integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==
-is-glob@^4.0.1, is-glob@^4.0.3:
+is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3:
version "4.0.3"
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
@@ -2975,7 +3214,7 @@ is-path-cwd@^2.2.0:
resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-2.2.0.tgz#67d43b82664a7b5191fd9119127eb300048a9fdb"
integrity sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==
-is-path-inside@^3.0.2:
+is-path-inside@^3.0.2, is-path-inside@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283"
integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==
@@ -3460,11 +3699,23 @@ js-yaml@^3.13.1:
argparse "^1.0.7"
esprima "^4.0.0"
+js-yaml@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
+ integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==
+ dependencies:
+ argparse "^2.0.1"
+
jsesc@^2.5.1:
version "2.5.2"
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4"
integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==
+json-buffer@3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13"
+ integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==
+
json-parse-better-errors@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9"
@@ -3475,6 +3726,16 @@ json-parse-even-better-errors@^2.3.0, json-parse-even-better-errors@^2.3.1:
resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d"
integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==
+json-schema-traverse@^0.4.1:
+ version "0.4.1"
+ resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
+ integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
+
+json-stable-stringify-without-jsonify@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
+ integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==
+
json-stringify-nice@^1.1.4:
version "1.1.4"
resolved "https://registry.yarnpkg.com/json-stringify-nice/-/json-stringify-nice-1.1.4.tgz#2c937962b80181d3f317dd39aa323e14f5a60a67"
@@ -3514,6 +3775,13 @@ just-diff@^5.0.1:
resolved "https://registry.yarnpkg.com/just-diff/-/just-diff-5.2.0.tgz#60dca55891cf24cd4a094e33504660692348a241"
integrity sha512-6ufhP9SHjb7jibNFrNxyFZ6od3g+An6Ai9mhGRvcYe8UJlH0prseN64M+6ZBBUoKYHZsitDP42gAJ8+eVWr3lw==
+keyv@^4.5.3:
+ version "4.5.4"
+ resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93"
+ integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==
+ dependencies:
+ json-buffer "3.0.1"
+
kind-of@^6.0.3:
version "6.0.3"
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd"
@@ -3529,6 +3797,14 @@ leven@^3.1.0:
resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2"
integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==
+levn@^0.4.1:
+ version "0.4.1"
+ resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade"
+ integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==
+ dependencies:
+ prelude-ls "^1.2.1"
+ type-check "~0.4.0"
+
libnpmaccess@^6.0.4:
version "6.0.4"
resolved "https://registry.yarnpkg.com/libnpmaccess/-/libnpmaccess-6.0.4.tgz#2dd158bd8a071817e2207d3b201d37cf1ad6ae6b"
@@ -3672,6 +3948,13 @@ locate-path@^5.0.0:
dependencies:
p-locate "^4.1.0"
+locate-path@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286"
+ integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==
+ dependencies:
+ p-locate "^5.0.0"
+
lodash.capitalize@^4.2.1:
version "4.2.1"
resolved "https://registry.yarnpkg.com/lodash.capitalize/-/lodash.capitalize-4.2.1.tgz#f826c9b4e2a8511d84e3aca29db05e1a4f3b72a9"
@@ -3702,6 +3985,11 @@ lodash.memoize@4.x:
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==
+lodash.merge@^4.6.2:
+ version "4.6.2"
+ resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
+ integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
+
lodash.uniqby@^4.7.0:
version "4.7.0"
resolved "https://registry.yarnpkg.com/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz#d99c07a669e9e6d24e1362dfe266c67616af1302"
@@ -3885,7 +4173,7 @@ min-indent@^1.0.0:
resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869"
integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==
-minimatch@^3.0.4, minimatch@^3.1.1:
+minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
@@ -4372,6 +4660,18 @@ opener@^1.5.2:
resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598"
integrity sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==
+optionator@^0.9.3:
+ version "0.9.3"
+ resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64"
+ integrity sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==
+ dependencies:
+ "@aashutoshrathi/word-wrap" "^1.2.3"
+ deep-is "^0.1.3"
+ fast-levenshtein "^2.0.6"
+ levn "^0.4.1"
+ prelude-ls "^1.2.1"
+ type-check "^0.4.0"
+
out-of-character@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/out-of-character/-/out-of-character-1.2.1.tgz#383bf683e58f654ee1f348029367edf5c79ba87d"
@@ -4411,7 +4711,7 @@ p-limit@^2.2.0:
dependencies:
p-try "^2.0.0"
-p-limit@^3.1.0:
+p-limit@^3.0.2, p-limit@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b"
integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==
@@ -4432,6 +4732,13 @@ p-locate@^4.1.0:
dependencies:
p-limit "^2.2.0"
+p-locate@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834"
+ integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==
+ dependencies:
+ p-limit "^3.0.2"
+
p-map@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175"
@@ -4601,6 +4908,11 @@ postcss-selector-parser@^6.0.10:
cssesc "^3.0.0"
util-deprecate "^1.0.2"
+prelude-ls@^1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
+ integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
+
prettier@^2.8.1:
version "2.8.8"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da"
@@ -4673,6 +4985,11 @@ proto-list@~1.2.1:
resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849"
integrity sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==
+punycode@^2.1.0:
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5"
+ integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==
+
pure-rand@^6.0.0:
version "6.0.2"
resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.0.2.tgz#a9c2ddcae9b68d736a8163036f088a2781c8b306"
@@ -5288,7 +5605,7 @@ text-extensions@^1.0.0:
resolved "https://registry.yarnpkg.com/text-extensions/-/text-extensions-1.9.0.tgz#1853e45fee39c945ce6f6c36b2d659b5aabc2a26"
integrity sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==
-text-table@~0.2.0:
+text-table@^0.2.0, text-table@~0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==
@@ -5386,6 +5703,13 @@ tsutils@^3.21.0:
dependencies:
tslib "^1.8.1"
+type-check@^0.4.0, type-check@~0.4.0:
+ version "0.4.0"
+ resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1"
+ integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==
+ dependencies:
+ prelude-ls "^1.2.1"
+
type-detect@4.0.8:
version "4.0.8"
resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c"
@@ -5401,6 +5725,11 @@ type-fest@^0.18.0:
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.18.1.tgz#db4bc151a4a2cf4eebf9add5db75508db6cc841f"
integrity sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==
+type-fest@^0.20.2:
+ version "0.20.2"
+ resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4"
+ integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==
+
type-fest@^0.21.3:
version "0.21.3"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37"
@@ -5484,6 +5813,13 @@ update-browserslist-db@^1.0.11:
escalade "^3.1.1"
picocolors "^1.0.0"
+uri-js@^4.2.2:
+ version "4.4.1"
+ resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e"
+ integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==
+ dependencies:
+ punycode "^2.1.0"
+
url-join@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/url-join/-/url-join-4.0.1.tgz#b642e21a2646808ffa178c4c5fda39844e12cde7"