├── .gitignore
├── README.md
├── dist
├── main.js
├── manifest.json
└── styles.css
├── manifest.json
├── package.json
├── rollup.config.js
├── screenshots
└── 01.png
├── src
├── DataMuseProvider.ts
├── POSProvider.ts
├── PowerThesaurusProvider.ts
├── main.ts
├── popover.css
├── popover.ts
└── types.ts
├── tsconfig.json
├── versions.json
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | # Intellij
2 | *.iml
3 | .idea
4 |
5 | # npm
6 | node_modules
7 | package-lock.json
8 |
9 | # build
10 | *.js.map
11 |
12 | # obsidian
13 | data.json
14 | .env
15 | .DS_Store
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Obsidian Power Thesaurus Plugin
2 |
3 | Look up synonyms for highlighted english words using [https://www.powerthesaurus.org/](https://www.powerthesaurus.org/).
4 |
5 |
--------------------------------------------------------------------------------
/dist/main.js:
--------------------------------------------------------------------------------
1 | /*
2 | THIS IS A GENERATED/BUNDLED FILE BY ROLLUP
3 | if you want to view the source visit the plugins github repository
4 | */
5 |
6 | 'use strict';
7 |
8 | var obsidian = require('obsidian');
9 |
10 | /*! *****************************************************************************
11 | Copyright (c) Microsoft Corporation.
12 |
13 | Permission to use, copy, modify, and/or distribute this software for any
14 | purpose with or without fee is hereby granted.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
17 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
18 | AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
19 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
20 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
21 | OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
22 | PERFORMANCE OF THIS SOFTWARE.
23 | ***************************************************************************** */
24 |
25 | function __awaiter(thisArg, _arguments, P, generator) {
26 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
27 | return new (P || (P = Promise))(function (resolve, reject) {
28 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
29 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
30 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
31 | step((generator = generator.apply(thisArg, _arguments || [])).next());
32 | });
33 | }
34 |
35 | /**
36 | * Transform the case in `value` (`string`) to match that of `base` (`string`).
37 | *
38 | * @param {string} value
39 | * @param {string} base
40 | * @returns {string}
41 | */
42 | function matchCasing(value, base) {
43 | var index = -1;
44 | /** @type {string} */
45 | var char;
46 | /** @type {string} */
47 | var rest;
48 | /** @type {boolean} */
49 | var cap;
50 |
51 | if (base.toUpperCase() === base) {
52 | return value.toUpperCase()
53 | }
54 |
55 | if (base.toLowerCase() === base) {
56 | return value.toLowerCase()
57 | }
58 |
59 | while (++index < base.length) {
60 | char = base.charAt(index);
61 |
62 | if (char.toUpperCase() !== char.toLowerCase()) {
63 | rest = base.slice(index + 1);
64 | cap = char === char.toUpperCase() && rest === rest.toLowerCase();
65 | break
66 | }
67 | }
68 |
69 | if (cap) {
70 | index = -1;
71 |
72 | while (++index < value.length) {
73 | char = value.charAt(index).toUpperCase();
74 |
75 | if (char !== char.toLowerCase()) {
76 | return (
77 | value.slice(0, index) + char + value.slice(index + 1).toLowerCase()
78 | )
79 | }
80 | }
81 | }
82 |
83 | return value
84 | }
85 |
86 | function repositionWidget(widget, coords) {
87 | const height = widget.clientHeight;
88 | const width = widget.clientWidth;
89 | if (coords.bottom + height > window.innerHeight) {
90 | widget.style.setProperty("top", `${coords.top - height}px`);
91 | }
92 | if (coords.left + width > window.innerWidth) {
93 | widget.style.setProperty("left", `${window.innerWidth - width - 15}px`);
94 | }
95 | }
96 | function constructThesaurusPopover({ list, editor, selection, }) {
97 | let isDestroyed = false;
98 | const widget = createDiv({ cls: "pt-select" }, (div) => {
99 | list.forEach((synonym, i) => {
100 | div.createDiv({ cls: "pt-select-option" }, (option) => {
101 | var _a;
102 | option.dataset.index = i.toString();
103 | option.createDiv({ cls: "pt-select-label" }, (label) => {
104 | var _a;
105 | label.createDiv({ cls: "pt-term", text: synonym.term });
106 | if ((_a = synonym.partsOfSpeech) === null || _a === void 0 ? void 0 : _a.length) {
107 | label.createDiv({
108 | cls: "pt-meta-pos",
109 | text: synonym.partsOfSpeech.join(", "),
110 | });
111 | }
112 | });
113 | if ((_a = synonym.tags) === null || _a === void 0 ? void 0 : _a.length) {
114 | option.createDiv({
115 | cls: "pt-meta-tags",
116 | text: synonym.tags.join(", "),
117 | });
118 | }
119 | });
120 | });
121 | });
122 | const clickOutsideHandler = (e) => {
123 | const isTargetInWidget = e.target instanceof Node && widget.contains(e.target);
124 | if (!isTargetInWidget) {
125 | selfDestruct();
126 | }
127 | };
128 | const escHandler = (e) => {
129 | if (e.key === "Escape") {
130 | selfDestruct();
131 | }
132 | };
133 | const selfDestruct = () => {
134 | if (isDestroyed)
135 | return;
136 | isDestroyed = true;
137 | widget.remove();
138 | document.body.removeEventListener("pointerdown", clickOutsideHandler);
139 | document.removeEventListener("keydown", escHandler);
140 | };
141 | let coords;
142 | if (editor.cursorCoords) {
143 | coords = editor.cursorCoords(true, "window");
144 | }
145 | else if (editor.coordsAtPos) {
146 | const offset = editor.posToOffset(editor.getCursor("from"));
147 | coords = editor.coordsAtPos(offset);
148 | }
149 | else {
150 | return;
151 | }
152 | widget.on("click", ".pt-select-option", (e, el) => {
153 | editor.replaceSelection(matchCasing(list[parseInt(el.dataset.index)].term, selection));
154 | selfDestruct();
155 | });
156 | widget.style.left = `${coords.left}px`;
157 | widget.style.top = `${coords.bottom}px`;
158 | document.body.appendChild(widget);
159 | repositionWidget(widget, coords);
160 | document.body.addEventListener("pointerdown", clickOutsideHandler);
161 | document.addEventListener("keydown", escHandler);
162 | return selfDestruct;
163 | }
164 |
165 | const TEMP_LANG = 'en';
166 | function getURL(sentence, lang) {
167 | return `https://systran-systran-platform-for-language-processing-v1.p.rapidapi.com/nlp/morphology/extract/pos?input=${encodeURIComponent(sentence)}&lang=${lang}`;
168 | }
169 | function getPOS(line, termIndex) {
170 | var _a;
171 | return __awaiter(this, void 0, void 0, function* () {
172 | const sentences = line.split(/[.!?]/g);
173 | let seen = 0;
174 | for (let sentence of sentences) {
175 | if (seen <= termIndex && termIndex <= seen + sentence.length) {
176 | const result = yield fetch(getURL(sentence, TEMP_LANG), {
177 | "method": "GET",
178 | "headers": {
179 | "x-rapidapi-key": "5e1aff4a8dmsh29e6efbd0a66e0ep1c9b55jsn31b784e15a17",
180 | "x-rapidapi-host": "systran-systran-platform-for-language-processing-v1.p.rapidapi.com"
181 | }
182 | });
183 | const json = yield result.json();
184 | const wordIndex = termIndex - seen;
185 | const match = (_a = json.partsOfSpeech) === null || _a === void 0 ? void 0 : _a.find(pos => pos.start === wordIndex);
186 | if (match) {
187 | const posStr = match.pos.split('/')[1];
188 | if (posStr.startsWith('noun'))
189 | return 2;
190 | if (posStr.startsWith('verb'))
191 | return 6;
192 | if (posStr.startsWith('adj'))
193 | return 1;
194 | if (posStr.startsWith('adv'))
195 | return 4;
196 | }
197 | break;
198 | }
199 | seen += sentence.length + 1;
200 | }
201 | return null;
202 | });
203 | }
204 |
205 | const apiUrl = "https://api.powerthesaurus.org/";
206 | const POS_MAP = {
207 | 1: "adj",
208 | 2: "n",
209 | 4: "adv",
210 | 6: "v",
211 | 7: "int",
212 | 9: "conj",
213 | 10: "prep",
214 | };
215 | class PowerThesaurusProvider {
216 | static appendAttribution(containerEl, selection) {
217 | containerEl.createDiv({ cls: "pt-link-option" }, (div) => {
218 | div.createEl("a", {
219 | cls: "external-link",
220 | text: "See more results",
221 | href: `https://www.powerthesaurus.org/${selection.toLowerCase()}/synonyms`,
222 | }, (a) => {
223 | a.target = "_blank";
224 | });
225 | });
226 | containerEl.createDiv({ cls: "pt-link-option" }, (div) => {
227 | div.createEl("a", {
228 | cls: "external-link pt-powered-by",
229 | text: "Powered by power thesaurus",
230 | href: `https://www.powerthesaurus.org/`,
231 | }, (a) => {
232 | a.target = "_blank";
233 | });
234 | });
235 | }
236 | getSynonyms(term, line, termIndex) {
237 | var _a, _b, _c, _d;
238 | return __awaiter(this, void 0, void 0, function* () {
239 | try {
240 | const pos = yield getPOS(line, termIndex);
241 | const termIdResult = yield getTermId(term);
242 | const terms = (_b = (_a = termIdResult === null || termIdResult === void 0 ? void 0 : termIdResult.data) === null || _a === void 0 ? void 0 : _a.search) === null || _b === void 0 ? void 0 : _b.terms;
243 | if (pos && terms && terms.length) {
244 | const termId = terms[0].id;
245 | const searchResult = yield getRelatedWords(termId, pos);
246 | const edges = (_d = (_c = searchResult === null || searchResult === void 0 ? void 0 : searchResult.data) === null || _c === void 0 ? void 0 : _c.thesauruses) === null || _d === void 0 ? void 0 : _d.edges;
247 | if (edges && edges.length) {
248 | const list = edges.map((edge) => {
249 | var _a, _b, _c, _d, _e, _f;
250 | const tags = (_b = (_a = edge === null || edge === void 0 ? void 0 : edge.node) === null || _a === void 0 ? void 0 : _a.relations) === null || _b === void 0 ? void 0 : _b.tags;
251 | const pos = (_d = (_c = edge === null || edge === void 0 ? void 0 : edge.node) === null || _c === void 0 ? void 0 : _c.relations) === null || _d === void 0 ? void 0 : _d.parts_of_speech.map((p) => POS_MAP[p]);
252 | const term = (_f = (_e = edge === null || edge === void 0 ? void 0 : edge.node) === null || _e === void 0 ? void 0 : _e.targetTerm) === null || _f === void 0 ? void 0 : _f.name;
253 | return {
254 | term,
255 | pos,
256 | tags,
257 | };
258 | });
259 | return list;
260 | }
261 | }
262 | }
263 | catch (e) {
264 | console.error(e);
265 | return null;
266 | }
267 | });
268 | }
269 | }
270 | const SEARCH_QUERY = `query SEARCH_QUERY($query: String!) {
271 | search(query: $query) {
272 | terms {
273 | id
274 | name
275 | slug
276 | counters
277 | __typename
278 | }
279 | list
280 | correctedFrom
281 | __typename
282 | }
283 | }`;
284 | function getTermId(term) {
285 | return __awaiter(this, void 0, void 0, function* () {
286 | const response = yield fetch(apiUrl, {
287 | method: "POST",
288 | // mode: 'no-cors',
289 | headers: {
290 | "Content-Type": "application/json",
291 | "User-Agent": "Obsidian-PowerThesaurus/0.0.1",
292 | },
293 | body: JSON.stringify({
294 | operationName: "SEARCH_QUERY",
295 | variables: { query: term },
296 | query: SEARCH_QUERY,
297 | }),
298 | });
299 | return response.json();
300 | });
301 | }
302 | const THESAURUSES_QUERY = `query THESAURUSES_QUERY($after: String, $first: Int, $before: String, $last: Int, $termID: ID!, $list: List!, $sort: ThesaurusSorting!, $tagID: Int, $posID: Int, $syllables: Int, $type: Type) {
303 | thesauruses(
304 | termId: $termID
305 | sort: $sort
306 | list: $list
307 | after: $after
308 | first: $first
309 | before: $before
310 | last: $last
311 | tagId: $tagID
312 | partOfSpeechId: $posID
313 | syllables: $syllables
314 | type: $type
315 | ) {
316 | pageInfo {
317 | hasNextPage
318 | hasPreviousPage
319 | startCursor
320 | endCursor
321 | __typename
322 | }
323 | edges {
324 | node {
325 | _type
326 | id
327 | isPinned
328 | targetTerm {
329 | id
330 | name
331 | slug
332 | __typename
333 | }
334 | relations
335 | rating
336 | vote {
337 | voteType
338 | id
339 | __typename
340 | }
341 | votes
342 | __typename
343 | }
344 | __typename
345 | }
346 | __typename
347 | }
348 | }`;
349 | function getRelatedWords(termId, pos = null) {
350 | return __awaiter(this, void 0, void 0, function* () {
351 | const response = yield fetch(apiUrl, {
352 | method: "POST",
353 | // mode: 'no-cors',
354 | headers: {
355 | "Content-Type": "application/json",
356 | "User-Agent": "Obsidian-PowerThesaurus/0.0.1",
357 | },
358 | body: JSON.stringify({
359 | operationName: "THESAURUSES_QUERY",
360 | variables: {
361 | list: "SYNONYM",
362 | termID: termId,
363 | sort: { field: "RATING", direction: "DESC" },
364 | tagID: null,
365 | limit: 20,
366 | syllables: null,
367 | query: null,
368 | posID: null,
369 | first: 20,
370 | after: "",
371 | },
372 | query: THESAURUSES_QUERY,
373 | }),
374 | });
375 | return response.json();
376 | });
377 | }
378 |
379 | const spaceRegEx = /\s/;
380 | class PowerThesaurusPlugin extends obsidian.Plugin {
381 | constructor() {
382 | super(...arguments);
383 | this.destroyPopover = null;
384 | this.isPopoverLoading = false;
385 | this.handlePointerUp = obsidian.debounce(() => {
386 | const activeLeaf = this.app.workspace.activeLeaf;
387 | if (activeLeaf && activeLeaf.view instanceof obsidian.MarkdownView) {
388 | const view = activeLeaf.view;
389 | if (view.getMode() === "source") {
390 | const selection = view.editor.getSelection();
391 | if (!selection || spaceRegEx.test(selection))
392 | return;
393 | const cursor = view.editor.getCursor("from");
394 | const line = view.editor.getLine(cursor.line);
395 | this.isPopoverLoading = true;
396 | new PowerThesaurusProvider()
397 | .getSynonyms(selection, line, cursor.ch)
398 | .then((list) => {
399 | if ((list === null || list === void 0 ? void 0 : list.length) && this.isPopoverLoading) {
400 | this.isPopoverLoading = false;
401 | this.destroyPopover = constructThesaurusPopover({
402 | list,
403 | selection,
404 | editor: view.editor,
405 | });
406 | }
407 | })
408 | .catch((e) => {
409 | console.error(e);
410 | });
411 | }
412 | }
413 | }, 300, true);
414 | }
415 | onload() {
416 | return __awaiter(this, void 0, void 0, function* () {
417 | document.on("pointerup", ".CodeMirror-line", this.handlePointerUp);
418 | this.registerDomEvent(window, "keydown", () => {
419 | if (this.isPopoverLoading) {
420 | this.isPopoverLoading = false;
421 | }
422 | if (this.destroyPopover) {
423 | this.destroyPopover();
424 | }
425 | });
426 | });
427 | }
428 | onunload() {
429 | document.off("pointerup", ".CodeMirror-line", this.handlePointerUp);
430 | if (this.isPopoverLoading) {
431 | this.isPopoverLoading = false;
432 | }
433 | if (this.destroyPopover) {
434 | this.destroyPopover();
435 | this.destroyPopover = null;
436 | }
437 | }
438 | }
439 |
440 | module.exports = PowerThesaurusPlugin;
441 | //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFpbi5qcyIsInNvdXJjZXMiOlsiLi4vbm9kZV9tb2R1bGVzL3RzbGliL3RzbGliLmVzNi5qcyIsIi4uL25vZGVfbW9kdWxlcy9tYXRjaC1jYXNpbmcvaW5kZXguanMiLCIuLi9zcmMvcG9wb3Zlci50cyIsIi4uL3NyYy9QT1NQcm92aWRlci50cyIsIi4uL3NyYy9Qb3dlclRoZXNhdXJ1c1Byb3ZpZGVyLnRzIiwiLi4vc3JjL21haW4udHMiXSwic291cmNlc0NvbnRlbnQiOm51bGwsIm5hbWVzIjpbIlBsdWdpbiIsImRlYm91bmNlIiwiTWFya2Rvd25WaWV3Il0sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7QUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBdURBO0FBQ08sU0FBUyxTQUFTLENBQUMsT0FBTyxFQUFFLFVBQVUsRUFBRSxDQUFDLEVBQUUsU0FBUyxFQUFFO0FBQzdELElBQUksU0FBUyxLQUFLLENBQUMsS0FBSyxFQUFFLEVBQUUsT0FBTyxLQUFLLFlBQVksQ0FBQyxHQUFHLEtBQUssR0FBRyxJQUFJLENBQUMsQ0FBQyxVQUFVLE9BQU8sRUFBRSxFQUFFLE9BQU8sQ0FBQyxLQUFLLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxFQUFFO0FBQ2hILElBQUksT0FBTyxLQUFLLENBQUMsS0FBSyxDQUFDLEdBQUcsT0FBTyxDQUFDLEVBQUUsVUFBVSxPQUFPLEVBQUUsTUFBTSxFQUFFO0FBQy9ELFFBQVEsU0FBUyxTQUFTLENBQUMsS0FBSyxFQUFFLEVBQUUsSUFBSSxFQUFFLElBQUksQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLE9BQU8sQ0FBQyxFQUFFLEVBQUUsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRTtBQUNuRyxRQUFRLFNBQVMsUUFBUSxDQUFDLEtBQUssRUFBRSxFQUFFLElBQUksRUFBRSxJQUFJLENB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
442 |
--------------------------------------------------------------------------------
/dist/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "id": "obsidian-powerthesaurus",
3 | "name": "Power Thesaurus",
4 | "version": "1.0.1",
5 | "minAppVersion": "0.9.12",
6 | "description": "Look up synonyms for selected english words using powerthesaurus.org",
7 | "author": "mgmeyers",
8 | "authorUrl": "https://github.com/mgmeyers/obsidian-powerthesaurus",
9 | "isDesktopOnly": true
10 | }
11 |
--------------------------------------------------------------------------------
/dist/styles.css:
--------------------------------------------------------------------------------
1 | .pt-select {
2 | min-width: 210px;
3 | max-width: 250px;
4 | max-height: 200px;
5 | background-color: var(--background-primary);
6 | border: 1px solid var(--background-modifier-border);
7 | position: absolute;
8 | z-index: var(--layer-popover);
9 | border-radius: 5px;
10 | box-shadow: 0px 15px 25px rgba(0, 0, 0, 0.2);
11 | font-size: 14px;
12 | overflow-y: auto;
13 | overflow-x: hidden;
14 | line-height: 1.4;
15 | }
16 |
17 | .pt-select-option {
18 | cursor: pointer;
19 | padding: 10px;
20 | border-bottom: 1px solid var(--background-modifier-border);
21 | }
22 |
23 | .pt-link-option:hover,
24 | .pt-select-option:hover {
25 | background-color: var(--background-secondary);
26 | }
27 |
28 | .pt-link-option {
29 | cursor: pointer;
30 | border-bottom: 1px solid var(--background-modifier-border);
31 | }
32 |
33 | .pt-link-option > a {
34 | color: var(--text-normal);
35 | text-decoration: none;
36 | background-position: center right 10px;
37 | display: block;
38 | padding: 10px;
39 | }
40 |
41 | .pt-select > .pt-link-option:last-child,
42 | .pt-select > .pt-select-option:last-child {
43 | border-bottom: none;
44 | }
45 |
46 | .pt-select-label {
47 | display: flex;
48 | justify-content: space-between;
49 | align-items: center;
50 | }
51 |
52 | .pt-meta-tags,
53 | .pt-meta-pos {
54 | font-size: 12px;
55 | color: var(--text-muted);
56 | }
57 |
58 | .pt-meta-pos {
59 | display: inline-block;
60 | margin-left: 10px;
61 | }
62 |
63 | .pt-link-option > a.pt-powered-by {
64 | color: var(--text-accent);
65 | padding: 6px 10px;
66 | font-size: 11px;
67 | }
68 |
--------------------------------------------------------------------------------
/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "id": "obsidian-powerthesaurus",
3 | "name": "Power Thesaurus",
4 | "version": "1.0.1",
5 | "minAppVersion": "0.9.12",
6 | "description": "Look up synonyms for selected english words using powerthesaurus.org",
7 | "author": "mgmeyers",
8 | "authorUrl": "https://github.com/mgmeyers/obsidian-powerthesaurus",
9 | "isDesktopOnly": true
10 | }
11 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "obsidian-sample-plugin",
3 | "version": "0.12.0",
4 | "description": "This is a sample plugin for Obsidian (https://obsidian.md)",
5 | "main": "main.js",
6 | "scripts": {
7 | "dev": "rollup --config rollup.config.js -w",
8 | "build": "rollup --config rollup.config.js --environment BUILD:production"
9 | },
10 | "keywords": [],
11 | "author": "",
12 | "license": "MIT",
13 | "devDependencies": {
14 | "@rollup/plugin-commonjs": "^18.0.0",
15 | "@rollup/plugin-node-resolve": "^11.2.1",
16 | "@rollup/plugin-typescript": "^8.2.1",
17 | "@types/node": "^14.14.37",
18 | "obsidian": "^0.12.0",
19 | "rollup": "^2.32.1",
20 | "tslib": "^2.2.0",
21 | "typescript": "^4.2.4"
22 | },
23 | "dependencies": {
24 | "dotenv": "^9.0.2",
25 | "match-casing": "^2.0.0",
26 | "rollup-plugin-copy": "^3.4.0",
27 | "rollup-plugin-css-only": "^3.1.0"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | import dotenv from "dotenv";
2 | import typescript from "@rollup/plugin-typescript";
3 | import { nodeResolve } from "@rollup/plugin-node-resolve";
4 | import commonjs from "@rollup/plugin-commonjs";
5 | import copy from "rollup-plugin-copy";
6 | import css from "rollup-plugin-css-only";
7 |
8 | dotenv.config();
9 |
10 | const isProd = process.env.BUILD === "production";
11 |
12 | const banner = `/*
13 | THIS IS A GENERATED/BUNDLED FILE BY ROLLUP
14 | if you want to view the source visit the plugins github repository
15 | */
16 | `;
17 |
18 | const output = [
19 | {
20 | input: "./src/main.ts",
21 | output: {
22 | dir: "./dist",
23 | sourcemap: "inline",
24 | sourcemapExcludeSources: isProd,
25 | format: "cjs",
26 | exports: "default",
27 | banner,
28 | },
29 | external: ["obsidian"],
30 | plugins: [
31 | css({ output: "styles.css" }),
32 | typescript(),
33 | nodeResolve({ browser: true }),
34 | commonjs(),
35 | copy({
36 | targets: [{ src: "./manifest.json", dest: "./dist" }],
37 | }),
38 | ],
39 | },
40 | ];
41 |
42 | if (process.env.PLUGIN_DEST) {
43 | output.push({
44 | input: "./src/main.ts",
45 | output: {
46 | dir: process.env.PLUGIN_DEST,
47 | sourcemap: "inline",
48 | sourcemapExcludeSources: isProd,
49 | format: "cjs",
50 | exports: "default",
51 | banner,
52 | },
53 | external: ["obsidian"],
54 | plugins: [
55 | css({ output: "styles.css" }),
56 | typescript(),
57 | nodeResolve({ browser: true }),
58 | commonjs(),
59 | copy({
60 | targets: [{ src: "./manifest.json", dest: process.env.PLUGIN_DEST }],
61 | }),
62 | ],
63 | });
64 | }
65 |
66 | export default output;
67 |
--------------------------------------------------------------------------------
/screenshots/01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mgmeyers/obsidian-powerthesaurus/74bb3f4f2fe24dccb62a88fc2aff6237c17758bf/screenshots/01.png
--------------------------------------------------------------------------------
/src/DataMuseProvider.ts:
--------------------------------------------------------------------------------
1 | import { Synonym, SynonymProvider } from "./types";
2 |
3 | const apiUrl = "https://api.datamuse.com/words?md=p&rel_syn=";
4 |
5 | async function getDefinition(term: string, lc?: string, rc?: string) {
6 | let url = `https://api.datamuse.com/words?sp=${term}&md=pd&max=1`;
7 |
8 | if (lc) {
9 | url += `&lc=${lc}`;
10 | }
11 |
12 | if (rc) {
13 | url += `&rc=${rc}`;
14 | }
15 |
16 | const res = await fetch(url);
17 | const json = (await res.json()) as Array<{
18 | word: string;
19 | tags: string[];
20 | defs: string[];
21 | }>;
22 |
23 | if (json.length) {
24 | return json[0].defs.map((def) => {
25 | const split = def.split(/\t/);
26 | return {
27 | pos: split[0],
28 | def: split[1],
29 | };
30 | });
31 | }
32 |
33 | return null;
34 | }
35 |
36 | async function getMeansLike(def: string, pos: string): Promise {
37 | let url = `https://api.datamuse.com/words?md=p&ml=${def.replace(/\s/g, "+")}`;
38 |
39 | const res = await fetch(url);
40 | const json = (await res.json()) as Array<{ word: string; tags: string[] }>;
41 |
42 | return json
43 | .filter((r) => r.tags?.contains("syn") || r.tags?.contains(pos))
44 | .map((r) => {
45 | return {
46 | term: r.word,
47 | tags: [],
48 | partsOfSpeech: r.tags,
49 | };
50 | })
51 | .slice(0, 10);
52 | }
53 |
54 | export class DataMuseProvider implements SynonymProvider {
55 | async getSynonyms(
56 | term: string,
57 | line: string,
58 | termIndex: number
59 | ): Promise {
60 | try {
61 | const sentences = line.split(/(?:\.!;:\(\)\[\]\?)/g);
62 |
63 | let seen = 0;
64 | let lc: string;
65 | let rc: string;
66 |
67 | for (let sentence of sentences) {
68 | if (seen <= termIndex && termIndex <= seen + sentence.length) {
69 | const targetSentence = sentence.trim();
70 | const beforeWord = targetSentence
71 | .substring(0, termIndex - seen - 1)
72 | .trim();
73 | const afterWord = targetSentence
74 | .substr(termIndex + term.length)
75 | .trim();
76 |
77 | if (beforeWord) {
78 | lc = beforeWord.split(/\s+/g).pop();
79 | }
80 |
81 | if (afterWord) {
82 | rc = afterWord.split(/\s+/g).shift();
83 | }
84 |
85 | break;
86 | }
87 |
88 | seen += sentence.length + 1;
89 | }
90 |
91 | let url = apiUrl + term;
92 |
93 | if (lc) {
94 | url += `&lc=${lc}`;
95 | }
96 |
97 | if (rc) {
98 | url += `&rc=${rc}`;
99 | }
100 |
101 | const response = await fetch(url);
102 | const data = (await response.json()) as Array<{
103 | word: string;
104 | tags: string[];
105 | }>;
106 |
107 | return data.map((d) => {
108 | return {
109 | term: d.word,
110 | tags: [],
111 | partsOfSpeech: d.tags,
112 | };
113 | });
114 | } catch (e) {
115 | console.error(e);
116 | return null;
117 | }
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/src/POSProvider.ts:
--------------------------------------------------------------------------------
1 | const TEMP_LANG = 'en';
2 |
3 | function getURL(sentence: string, lang: string) {
4 | return `https://systran-systran-platform-for-language-processing-v1.p.rapidapi.com/nlp/morphology/extract/pos?input=${encodeURIComponent(sentence)}&lang=${lang}`
5 | }
6 |
7 | interface POSResponse {
8 | partsOfSpeech: Array<{
9 | start: number,
10 | end: number,
11 | text: string,
12 | pos: string
13 | }>
14 | }
15 |
16 | export async function getPOS(line: string, termIndex: number) {
17 | const sentences = line.split(/[.!?]/g);
18 | let seen = 0;
19 |
20 | for (let sentence of sentences) {
21 | if (seen <= termIndex && termIndex <= seen + sentence.length) {
22 | const result = await fetch(getURL(sentence, TEMP_LANG), {
23 | "method": "GET",
24 | "headers": {
25 | "x-rapidapi-key": "5e1aff4a8dmsh29e6efbd0a66e0ep1c9b55jsn31b784e15a17",
26 | "x-rapidapi-host": "systran-systran-platform-for-language-processing-v1.p.rapidapi.com"
27 | }
28 | })
29 |
30 | const json = await result.json() as POSResponse;
31 |
32 | const wordIndex = termIndex - seen;
33 | const match = json.partsOfSpeech?.find(pos => pos.start === wordIndex);
34 |
35 | if (match) {
36 | const posStr = match.pos.split('/')[1];
37 |
38 | if (posStr.startsWith('noun')) return 2;
39 | if (posStr.startsWith('verb')) return 6;
40 | if (posStr.startsWith('adj')) return 1;
41 | if (posStr.startsWith('adv')) return 4;
42 | }
43 |
44 | break;
45 | }
46 |
47 | seen += sentence.length + 1;
48 | }
49 |
50 | return null
51 | }
--------------------------------------------------------------------------------
/src/PowerThesaurusProvider.ts:
--------------------------------------------------------------------------------
1 | import { getPOS } from "./POSProvider";
2 | import { Synonym, SynonymProvider } from "./types";
3 |
4 | const apiUrl = "https://api.powerthesaurus.org/";
5 |
6 | const POS_MAP: { [k: number]: string } = {
7 | 1: "adj",
8 | 2: "n",
9 | 4: "adv",
10 | 6: "v",
11 | 7: "int",
12 | 9: "conj",
13 | 10: "prep",
14 | };
15 |
16 | export class PowerThesaurusProvider implements SynonymProvider {
17 | static appendAttribution(containerEl: HTMLElement, selection: string) {
18 | containerEl.createDiv({ cls: "pt-link-option" }, (div) => {
19 | div.createEl(
20 | "a",
21 | {
22 | cls: "external-link",
23 | text: "See more results",
24 | href: `https://www.powerthesaurus.org/${selection.toLowerCase()}/synonyms`,
25 | },
26 | (a) => {
27 | a.target = "_blank";
28 | }
29 | );
30 | });
31 |
32 | containerEl.createDiv({ cls: "pt-link-option" }, (div) => {
33 | div.createEl(
34 | "a",
35 | {
36 | cls: "external-link pt-powered-by",
37 | text: "Powered by power thesaurus",
38 | href: `https://www.powerthesaurus.org/`,
39 | },
40 | (a) => {
41 | a.target = "_blank";
42 | }
43 | );
44 | });
45 | }
46 | async getSynonyms(term: string, line: string, termIndex: number): Promise {
47 | try {
48 | const pos = await getPOS(line, termIndex);
49 | const termIdResult = await getTermId(term);
50 | const terms = termIdResult?.data?.search?.terms;
51 |
52 | if (pos && terms && terms.length) {
53 | const termId = terms[0].id;
54 | const searchResult = await getRelatedWords(termId, pos);
55 |
56 | const edges = searchResult?.data?.thesauruses?.edges;
57 |
58 | if (edges && edges.length) {
59 | const list = edges.map((edge: any) => {
60 | const tags = edge?.node?.relations?.tags;
61 | const pos = edge?.node?.relations?.parts_of_speech.map((p: number) => POS_MAP[p]);
62 | const term = edge?.node?.targetTerm?.name;
63 |
64 | return {
65 | term,
66 | pos,
67 | tags,
68 | };
69 | });
70 |
71 | return list;
72 | }
73 | }
74 | } catch (e) {
75 | console.error(e);
76 | return null;
77 | }
78 | }
79 | }
80 |
81 |
82 | export const SEARCH_QUERY = `query SEARCH_QUERY($query: String!) {
83 | search(query: $query) {
84 | terms {
85 | id
86 | name
87 | slug
88 | counters
89 | __typename
90 | }
91 | list
92 | correctedFrom
93 | __typename
94 | }
95 | }`;
96 |
97 | async function getTermId(term: string) {
98 | const response = await fetch(apiUrl, {
99 | method: "POST",
100 | // mode: 'no-cors',
101 | headers: {
102 | "Content-Type": "application/json",
103 | "User-Agent": "Obsidian-PowerThesaurus/0.0.1",
104 | },
105 | body: JSON.stringify({
106 | operationName: "SEARCH_QUERY",
107 | variables: { query: term },
108 | query: SEARCH_QUERY,
109 | }),
110 | });
111 |
112 | return response.json();
113 | }
114 |
115 | export const THESAURUSES_QUERY = `query THESAURUSES_QUERY($after: String, $first: Int, $before: String, $last: Int, $termID: ID!, $list: List!, $sort: ThesaurusSorting!, $tagID: Int, $posID: Int, $syllables: Int, $type: Type) {
116 | thesauruses(
117 | termId: $termID
118 | sort: $sort
119 | list: $list
120 | after: $after
121 | first: $first
122 | before: $before
123 | last: $last
124 | tagId: $tagID
125 | partOfSpeechId: $posID
126 | syllables: $syllables
127 | type: $type
128 | ) {
129 | pageInfo {
130 | hasNextPage
131 | hasPreviousPage
132 | startCursor
133 | endCursor
134 | __typename
135 | }
136 | edges {
137 | node {
138 | _type
139 | id
140 | isPinned
141 | targetTerm {
142 | id
143 | name
144 | slug
145 | __typename
146 | }
147 | relations
148 | rating
149 | vote {
150 | voteType
151 | id
152 | __typename
153 | }
154 | votes
155 | __typename
156 | }
157 | __typename
158 | }
159 | __typename
160 | }
161 | }`;
162 |
163 | async function getRelatedWords(termId: string, pos: number = null) {
164 | const response = await fetch(apiUrl, {
165 | method: "POST",
166 | // mode: 'no-cors',
167 | headers: {
168 | "Content-Type": "application/json",
169 | "User-Agent": "Obsidian-PowerThesaurus/0.0.1",
170 | },
171 | body: JSON.stringify({
172 | operationName: "THESAURUSES_QUERY",
173 | variables: {
174 | list: "SYNONYM",
175 | termID: termId,
176 | sort: { field: "RATING", direction: "DESC" },
177 | tagID: null,
178 | limit: 20,
179 | syllables: null,
180 | query: null,
181 | posID: null,
182 | first: 20,
183 | after: "",
184 | },
185 | query: THESAURUSES_QUERY,
186 | }),
187 | });
188 |
189 | return response.json();
190 | }
191 |
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | import { Plugin, debounce, MarkdownView } from "obsidian";
2 |
3 | import { constructThesaurusPopover } from "./popover";
4 | import { PowerThesaurusProvider } from "./PowerThesaurusProvider";
5 |
6 | const spaceRegEx = /\s/;
7 |
8 | export default class PowerThesaurusPlugin extends Plugin {
9 | destroyPopover: (() => void) | null = null;
10 | isPopoverLoading = false;
11 |
12 | handlePointerUp = debounce(
13 | () => {
14 | const activeLeaf = this.app.workspace.activeLeaf;
15 |
16 | if (activeLeaf && activeLeaf.view instanceof MarkdownView) {
17 | const view = activeLeaf.view;
18 |
19 | if (view.getMode() === "source") {
20 | const selection = view.editor.getSelection();
21 |
22 | if (!selection || spaceRegEx.test(selection)) return;
23 |
24 | const cursor = view.editor.getCursor("from");
25 | const line = view.editor.getLine(cursor.line);
26 |
27 | this.isPopoverLoading = true;
28 |
29 | new PowerThesaurusProvider()
30 | .getSynonyms(selection, line, cursor.ch)
31 | .then((list) => {
32 | if (list?.length && this.isPopoverLoading) {
33 | this.isPopoverLoading = false;
34 | this.destroyPopover = constructThesaurusPopover({
35 | list,
36 | selection,
37 | editor: view.editor,
38 | });
39 | }
40 | })
41 | .catch((e) => {
42 | console.error(e);
43 | });
44 | }
45 | }
46 | },
47 | 300,
48 | true
49 | );
50 |
51 | async onload() {
52 | document.on("pointerup", ".CodeMirror-line", this.handlePointerUp);
53 |
54 | this.registerDomEvent(window, "keydown", () => {
55 | if (this.isPopoverLoading) {
56 | this.isPopoverLoading = false;
57 | }
58 |
59 | if (this.destroyPopover) {
60 | this.destroyPopover();
61 | }
62 | });
63 | }
64 |
65 | onunload() {
66 | document.off("pointerup", ".CodeMirror-line", this.handlePointerUp);
67 |
68 | if (this.isPopoverLoading) {
69 | this.isPopoverLoading = false;
70 | }
71 |
72 | if (this.destroyPopover) {
73 | this.destroyPopover();
74 | this.destroyPopover = null;
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/popover.css:
--------------------------------------------------------------------------------
1 | .pt-select {
2 | min-width: 210px;
3 | max-width: 250px;
4 | max-height: 200px;
5 | background-color: var(--background-primary);
6 | border: 1px solid var(--background-modifier-border);
7 | position: absolute;
8 | z-index: var(--layer-popover);
9 | border-radius: 5px;
10 | box-shadow: 0px 15px 25px rgba(0, 0, 0, 0.2);
11 | font-size: 14px;
12 | overflow-y: auto;
13 | overflow-x: hidden;
14 | line-height: 1.4;
15 | }
16 |
17 | .pt-select-option {
18 | cursor: pointer;
19 | padding: 10px;
20 | border-bottom: 1px solid var(--background-modifier-border);
21 | }
22 |
23 | .pt-link-option:hover,
24 | .pt-select-option:hover {
25 | background-color: var(--background-secondary);
26 | }
27 |
28 | .pt-link-option {
29 | cursor: pointer;
30 | border-bottom: 1px solid var(--background-modifier-border);
31 | }
32 |
33 | .pt-link-option > a {
34 | color: var(--text-normal);
35 | text-decoration: none;
36 | background-position: center right 10px;
37 | display: block;
38 | padding: 10px;
39 | }
40 |
41 | .pt-select > .pt-link-option:last-child,
42 | .pt-select > .pt-select-option:last-child {
43 | border-bottom: none;
44 | }
45 |
46 | .pt-select-label {
47 | display: flex;
48 | justify-content: space-between;
49 | align-items: center;
50 | }
51 |
52 | .pt-meta-tags,
53 | .pt-meta-pos {
54 | font-size: 12px;
55 | color: var(--text-muted);
56 | }
57 |
58 | .pt-meta-pos {
59 | display: inline-block;
60 | margin-left: 10px;
61 | }
62 |
63 | .pt-link-option > a.pt-powered-by {
64 | color: var(--text-accent);
65 | padding: 6px 10px;
66 | font-size: 11px;
67 | }
68 |
--------------------------------------------------------------------------------
/src/popover.ts:
--------------------------------------------------------------------------------
1 | import { Editor } from "obsidian";
2 | import { Synonym } from "./types";
3 | import { matchCasing } from "match-casing";
4 |
5 | import "./popover.css";
6 |
7 | function repositionWidget(
8 | widget: HTMLDivElement,
9 | coords: { left: number; top: number; bottom: number }
10 | ) {
11 | const height = widget.clientHeight;
12 | const width = widget.clientWidth;
13 |
14 | if (coords.bottom + height > window.innerHeight) {
15 | widget.style.setProperty("top", `${coords.top - height}px`);
16 | }
17 |
18 | if (coords.left + width > window.innerWidth) {
19 | widget.style.setProperty("left", `${window.innerWidth - width - 15}px`);
20 | }
21 | }
22 |
23 | interface ConstructThesaurusPopoverParams {
24 | list: Synonym[];
25 | editor: Editor;
26 | selection: string;
27 | }
28 |
29 | export function constructThesaurusPopover({
30 | list,
31 | editor,
32 | selection,
33 | }: ConstructThesaurusPopoverParams) {
34 | let isDestroyed = false;
35 |
36 | const widget = createDiv({ cls: "pt-select" }, (div) => {
37 | list.forEach((synonym, i) => {
38 | div.createDiv({ cls: "pt-select-option" }, (option) => {
39 | option.dataset.index = i.toString();
40 |
41 | option.createDiv({ cls: "pt-select-label" }, (label) => {
42 | label.createDiv({ cls: "pt-term", text: synonym.term });
43 | if (synonym.partsOfSpeech?.length) {
44 | label.createDiv({
45 | cls: "pt-meta-pos",
46 | text: synonym.partsOfSpeech.join(", "),
47 | });
48 | }
49 | });
50 |
51 | if (synonym.tags?.length) {
52 | option.createDiv({
53 | cls: "pt-meta-tags",
54 | text: synonym.tags.join(", "),
55 | });
56 | }
57 | });
58 | });
59 | });
60 |
61 | const clickOutsideHandler = (e: MouseEvent) => {
62 | const isTargetInWidget =
63 | e.target instanceof Node && widget.contains(e.target);
64 |
65 | if (!isTargetInWidget) {
66 | selfDestruct();
67 | }
68 | };
69 |
70 | const escHandler = (e: KeyboardEvent) => {
71 | if (e.key === "Escape") {
72 | selfDestruct();
73 | }
74 | };
75 |
76 | const selfDestruct = () => {
77 | if (isDestroyed) return;
78 |
79 | isDestroyed = true;
80 | widget.remove();
81 | document.body.removeEventListener("pointerdown", clickOutsideHandler);
82 | document.removeEventListener("keydown", escHandler);
83 | };
84 |
85 | let coords: { top: number; left: number; bottom: number };
86 |
87 | if ((editor as any).cursorCoords) {
88 | coords = (editor as any).cursorCoords(true, "window");
89 | } else if ((editor as any).coordsAtPos) {
90 | const offset = editor.posToOffset(editor.getCursor("from"));
91 | coords = (editor as any).coordsAtPos(offset);
92 | } else {
93 | return;
94 | }
95 |
96 | widget.on("click", ".pt-select-option", (e, el) => {
97 | editor.replaceSelection(
98 | matchCasing(list[parseInt(el.dataset.index)].term, selection)
99 | );
100 | selfDestruct();
101 | });
102 |
103 | widget.style.left = `${coords.left}px`;
104 | widget.style.top = `${coords.bottom}px`;
105 |
106 | document.body.appendChild(widget);
107 |
108 | repositionWidget(widget, coords);
109 |
110 | document.body.addEventListener("pointerdown", clickOutsideHandler);
111 | document.addEventListener("keydown", escHandler);
112 |
113 | return selfDestruct;
114 | }
115 |
--------------------------------------------------------------------------------
/src/types.ts:
--------------------------------------------------------------------------------
1 | export interface Synonym {
2 | term: string;
3 | partsOfSpeech: string[];
4 | tags: string[];
5 | }
6 |
7 | export interface Definition {
8 | term: string;
9 | partOfSpeech: string;
10 | tags: string[];
11 | definition: string;
12 | pronunciation: string;
13 | }
14 |
15 |
16 | export interface SynonymProvider {
17 | getSynonyms(word: string, line: string, wordIndex: number): Promise
18 | }
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": ".",
4 | "inlineSourceMap": true,
5 | "inlineSources": true,
6 | "module": "ESNext",
7 | "target": "es6",
8 | "allowJs": true,
9 | "noImplicitAny": true,
10 | "moduleResolution": "node",
11 | "importHelpers": true,
12 | "lib": [
13 | "dom",
14 | "es5",
15 | "scripthost",
16 | "es2015"
17 | ]
18 | },
19 | "include": [
20 | "**/*.ts"
21 | ]
22 | }
23 |
--------------------------------------------------------------------------------
/versions.json:
--------------------------------------------------------------------------------
1 | {
2 | "1.0.1": "0.9.12",
3 | "1.0.0": "0.9.12"
4 | }
5 |
--------------------------------------------------------------------------------
/yarn.lock:
--------------------------------------------------------------------------------
1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2 | # yarn lockfile v1
3 |
4 |
5 | "@nodelib/fs.scandir@2.1.4":
6 | version "2.1.4"
7 | resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz#d4b3549a5db5de2683e0c1071ab4f140904bbf69"
8 | integrity sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA==
9 | dependencies:
10 | "@nodelib/fs.stat" "2.0.4"
11 | run-parallel "^1.1.9"
12 |
13 | "@nodelib/fs.stat@2.0.4", "@nodelib/fs.stat@^2.0.2":
14 | version "2.0.4"
15 | resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz#a3f2dd61bab43b8db8fa108a121cfffe4c676655"
16 | integrity sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q==
17 |
18 | "@nodelib/fs.walk@^1.2.3":
19 | version "1.2.6"
20 | resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz#cce9396b30aa5afe9e3756608f5831adcb53d063"
21 | integrity sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow==
22 | dependencies:
23 | "@nodelib/fs.scandir" "2.1.4"
24 | fastq "^1.6.0"
25 |
26 | "@rollup/plugin-commonjs@^18.0.0":
27 | version "18.1.0"
28 | resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-18.1.0.tgz#5a760d757af168a50727c0ae080251fbfcc5eb02"
29 | integrity sha512-h3e6T9rUxVMAQswpDIobfUHn/doMzM9sgkMrsMWCFLmB84PSoC8mV8tOloAJjSRwdqhXBqstlX2BwBpHJvbhxg==
30 | dependencies:
31 | "@rollup/pluginutils" "^3.1.0"
32 | commondir "^1.0.1"
33 | estree-walker "^2.0.1"
34 | glob "^7.1.6"
35 | is-reference "^1.2.1"
36 | magic-string "^0.25.7"
37 | resolve "^1.17.0"
38 |
39 | "@rollup/plugin-node-resolve@^11.2.1":
40 | version "11.2.1"
41 | resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.2.1.tgz#82aa59397a29cd4e13248b106e6a4a1880362a60"
42 | integrity sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg==
43 | dependencies:
44 | "@rollup/pluginutils" "^3.1.0"
45 | "@types/resolve" "1.17.1"
46 | builtin-modules "^3.1.0"
47 | deepmerge "^4.2.2"
48 | is-module "^1.0.0"
49 | resolve "^1.19.0"
50 |
51 | "@rollup/plugin-typescript@^8.2.1":
52 | version "8.2.1"
53 | resolved "https://registry.yarnpkg.com/@rollup/plugin-typescript/-/plugin-typescript-8.2.1.tgz#f1a32d4030cc83432ce36a80a922280f0f0b5d44"
54 | integrity sha512-Qd2E1pleDR4bwyFxqbjt4eJf+wB0UKVMLc7/BAFDGVdAXQMCsD4DUv5/7/ww47BZCYxWtJqe1Lo0KVNswBJlRw==
55 | dependencies:
56 | "@rollup/pluginutils" "^3.1.0"
57 | resolve "^1.17.0"
58 |
59 | "@rollup/pluginutils@4":
60 | version "4.1.0"
61 | resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.1.0.tgz#0dcc61c780e39257554feb7f77207dceca13c838"
62 | integrity sha512-TrBhfJkFxA+ER+ew2U2/fHbebhLT/l/2pRk0hfj9KusXUuRXd2v0R58AfaZK9VXDQ4TogOSEmICVrQAA3zFnHQ==
63 | dependencies:
64 | estree-walker "^2.0.1"
65 | picomatch "^2.2.2"
66 |
67 | "@rollup/pluginutils@^3.1.0":
68 | version "3.1.0"
69 | resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz#706b4524ee6dc8b103b3c995533e5ad680c02b9b"
70 | integrity sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==
71 | dependencies:
72 | "@types/estree" "0.0.39"
73 | estree-walker "^1.0.1"
74 | picomatch "^2.2.2"
75 |
76 | "@types/codemirror@0.0.108":
77 | version "0.0.108"
78 | resolved "https://registry.yarnpkg.com/@types/codemirror/-/codemirror-0.0.108.tgz#e640422b666bf49251b384c390cdeb2362585bde"
79 | integrity sha512-3FGFcus0P7C2UOGCNUVENqObEb4SFk+S8Dnxq7K6aIsLVs/vDtlangl3PEO0ykaKXyK56swVF6Nho7VsA44uhw==
80 | dependencies:
81 | "@types/tern" "*"
82 |
83 | "@types/estree@*":
84 | version "0.0.47"
85 | resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.47.tgz#d7a51db20f0650efec24cd04994f523d93172ed4"
86 | integrity sha512-c5ciR06jK8u9BstrmJyO97m+klJrrhCf9u3rLu3DEAJBirxRqSCvDQoYKmxuYwQI5SZChAWu+tq9oVlGRuzPAg==
87 |
88 | "@types/estree@0.0.39":
89 | version "0.0.39"
90 | resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f"
91 | integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==
92 |
93 | "@types/fs-extra@^8.0.1":
94 | version "8.1.1"
95 | resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-8.1.1.tgz#1e49f22d09aa46e19b51c0b013cb63d0d923a068"
96 | integrity sha512-TcUlBem321DFQzBNuz8p0CLLKp0VvF/XH9E4KHNmgwyp4E3AfgI5cjiIVZWlbfThBop2qxFIh4+LeY6hVWWZ2w==
97 | dependencies:
98 | "@types/node" "*"
99 |
100 | "@types/glob@^7.1.1":
101 | version "7.1.3"
102 | resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.3.tgz#e6ba80f36b7daad2c685acd9266382e68985c183"
103 | integrity sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w==
104 | dependencies:
105 | "@types/minimatch" "*"
106 | "@types/node" "*"
107 |
108 | "@types/minimatch@*":
109 | version "3.0.4"
110 | resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.4.tgz#f0ec25dbf2f0e4b18647313ac031134ca5b24b21"
111 | integrity sha512-1z8k4wzFnNjVK/tlxvrWuK5WMt6mydWWP7+zvH5eFep4oj+UkrfiJTRtjCeBXNpwaA/FYqqtb4/QS4ianFpIRA==
112 |
113 | "@types/node@*":
114 | version "15.3.0"
115 | resolved "https://registry.yarnpkg.com/@types/node/-/node-15.3.0.tgz#d6fed7d6bc6854306da3dea1af9f874b00783e26"
116 | integrity sha512-8/bnjSZD86ZfpBsDlCIkNXIvm+h6wi9g7IqL+kmFkQ+Wvu3JrasgLElfiPgoo8V8vVfnEi0QVS12gbl94h9YsQ==
117 |
118 | "@types/node@^14.14.37":
119 | version "14.14.45"
120 | resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.45.tgz#ec2dfb5566ff814d061aef7e141575aedba245cf"
121 | integrity sha512-DssMqTV9UnnoxDWu959sDLZzfvqCF0qDNRjaWeYSui9xkFe61kKo4l1TWNTQONpuXEm+gLMRvdlzvNHBamzmEw==
122 |
123 | "@types/resolve@1.17.1":
124 | version "1.17.1"
125 | resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.17.1.tgz#3afd6ad8967c77e4376c598a82ddd58f46ec45d6"
126 | integrity sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==
127 | dependencies:
128 | "@types/node" "*"
129 |
130 | "@types/tern@*":
131 | version "0.23.3"
132 | resolved "https://registry.yarnpkg.com/@types/tern/-/tern-0.23.3.tgz#4b54538f04a88c9ff79de1f6f94f575a7f339460"
133 | integrity sha512-imDtS4TAoTcXk0g7u4kkWqedB3E4qpjXzCpD2LU5M5NAXHzCDsypyvXSaG7mM8DKYkCRa7tFp4tS/lp/Wo7Q3w==
134 | dependencies:
135 | "@types/estree" "*"
136 |
137 | array-union@^2.1.0:
138 | version "2.1.0"
139 | resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d"
140 | integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==
141 |
142 | balanced-match@^1.0.0:
143 | version "1.0.2"
144 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
145 | integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
146 |
147 | brace-expansion@^1.1.7:
148 | version "1.1.11"
149 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
150 | integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
151 | dependencies:
152 | balanced-match "^1.0.0"
153 | concat-map "0.0.1"
154 |
155 | braces@^3.0.1:
156 | version "3.0.2"
157 | resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
158 | integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
159 | dependencies:
160 | fill-range "^7.0.1"
161 |
162 | builtin-modules@^3.1.0:
163 | version "3.2.0"
164 | resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.2.0.tgz#45d5db99e7ee5e6bc4f362e008bf917ab5049887"
165 | integrity sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA==
166 |
167 | colorette@^1.1.0:
168 | version "1.2.2"
169 | resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94"
170 | integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==
171 |
172 | commondir@^1.0.1:
173 | version "1.0.1"
174 | resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
175 | integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=
176 |
177 | concat-map@0.0.1:
178 | version "0.0.1"
179 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
180 | integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
181 |
182 | deepmerge@^4.2.2:
183 | version "4.2.2"
184 | resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955"
185 | integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==
186 |
187 | dir-glob@^3.0.1:
188 | version "3.0.1"
189 | resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f"
190 | integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==
191 | dependencies:
192 | path-type "^4.0.0"
193 |
194 | dotenv@^9.0.2:
195 | version "9.0.2"
196 | resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-9.0.2.tgz#dacc20160935a37dea6364aa1bef819fb9b6ab05"
197 | integrity sha512-I9OvvrHp4pIARv4+x9iuewrWycX6CcZtoAu1XrzPxc5UygMJXJZYmBsynku8IkrJwgypE5DGNjDPmPRhDCptUg==
198 |
199 | estree-walker@^1.0.1:
200 | version "1.0.1"
201 | resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-1.0.1.tgz#31bc5d612c96b704106b477e6dd5d8aa138cb700"
202 | integrity sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==
203 |
204 | estree-walker@^2.0.1:
205 | version "2.0.2"
206 | resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac"
207 | integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==
208 |
209 | fast-glob@^3.0.3:
210 | version "3.2.5"
211 | resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.5.tgz#7939af2a656de79a4f1901903ee8adcaa7cb9661"
212 | integrity sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==
213 | dependencies:
214 | "@nodelib/fs.stat" "^2.0.2"
215 | "@nodelib/fs.walk" "^1.2.3"
216 | glob-parent "^5.1.0"
217 | merge2 "^1.3.0"
218 | micromatch "^4.0.2"
219 | picomatch "^2.2.1"
220 |
221 | fastq@^1.6.0:
222 | version "1.11.0"
223 | resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.11.0.tgz#bb9fb955a07130a918eb63c1f5161cc32a5d0858"
224 | integrity sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g==
225 | dependencies:
226 | reusify "^1.0.4"
227 |
228 | fill-range@^7.0.1:
229 | version "7.0.1"
230 | resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
231 | integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
232 | dependencies:
233 | to-regex-range "^5.0.1"
234 |
235 | fs-extra@^8.1.0:
236 | version "8.1.0"
237 | resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0"
238 | integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==
239 | dependencies:
240 | graceful-fs "^4.2.0"
241 | jsonfile "^4.0.0"
242 | universalify "^0.1.0"
243 |
244 | fs.realpath@^1.0.0:
245 | version "1.0.0"
246 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
247 | integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
248 |
249 | fsevents@~2.3.1:
250 | version "2.3.2"
251 | resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
252 | integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
253 |
254 | function-bind@^1.1.1:
255 | version "1.1.1"
256 | resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
257 | integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
258 |
259 | glob-parent@^5.1.0:
260 | version "5.1.2"
261 | resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
262 | integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
263 | dependencies:
264 | is-glob "^4.0.1"
265 |
266 | glob@^7.1.3, glob@^7.1.6:
267 | version "7.1.7"
268 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90"
269 | integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==
270 | dependencies:
271 | fs.realpath "^1.0.0"
272 | inflight "^1.0.4"
273 | inherits "2"
274 | minimatch "^3.0.4"
275 | once "^1.3.0"
276 | path-is-absolute "^1.0.0"
277 |
278 | globby@10.0.1:
279 | version "10.0.1"
280 | resolved "https://registry.yarnpkg.com/globby/-/globby-10.0.1.tgz#4782c34cb75dd683351335c5829cc3420e606b22"
281 | integrity sha512-sSs4inE1FB2YQiymcmTv6NWENryABjUNPeWhOvmn4SjtKybglsyPZxFB3U1/+L1bYi0rNZDqCLlHyLYDl1Pq5A==
282 | dependencies:
283 | "@types/glob" "^7.1.1"
284 | array-union "^2.1.0"
285 | dir-glob "^3.0.1"
286 | fast-glob "^3.0.3"
287 | glob "^7.1.3"
288 | ignore "^5.1.1"
289 | merge2 "^1.2.3"
290 | slash "^3.0.0"
291 |
292 | graceful-fs@^4.1.6, graceful-fs@^4.2.0:
293 | version "4.2.6"
294 | resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee"
295 | integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==
296 |
297 | has@^1.0.3:
298 | version "1.0.3"
299 | resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
300 | integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==
301 | dependencies:
302 | function-bind "^1.1.1"
303 |
304 | ignore@^5.1.1:
305 | version "5.1.8"
306 | resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57"
307 | integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==
308 |
309 | inflight@^1.0.4:
310 | version "1.0.6"
311 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
312 | integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=
313 | dependencies:
314 | once "^1.3.0"
315 | wrappy "1"
316 |
317 | inherits@2:
318 | version "2.0.4"
319 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
320 | integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
321 |
322 | is-core-module@^2.2.0:
323 | version "2.4.0"
324 | resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.4.0.tgz#8e9fc8e15027b011418026e98f0e6f4d86305cc1"
325 | integrity sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==
326 | dependencies:
327 | has "^1.0.3"
328 |
329 | is-extglob@^2.1.1:
330 | version "2.1.1"
331 | resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
332 | integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=
333 |
334 | is-glob@^4.0.1:
335 | version "4.0.1"
336 | resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc"
337 | integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==
338 | dependencies:
339 | is-extglob "^2.1.1"
340 |
341 | is-module@^1.0.0:
342 | version "1.0.0"
343 | resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591"
344 | integrity sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=
345 |
346 | is-number@^7.0.0:
347 | version "7.0.0"
348 | resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
349 | integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
350 |
351 | is-plain-object@^3.0.0:
352 | version "3.0.1"
353 | resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-3.0.1.tgz#662d92d24c0aa4302407b0d45d21f2251c85f85b"
354 | integrity sha512-Xnpx182SBMrr/aBik8y+GuR4U1L9FqMSojwDQwPMmxyC6bvEqly9UBCxhauBF5vNh2gwWJNX6oDV7O+OM4z34g==
355 |
356 | is-reference@^1.2.1:
357 | version "1.2.1"
358 | resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-1.2.1.tgz#8b2dac0b371f4bc994fdeaba9eb542d03002d0b7"
359 | integrity sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==
360 | dependencies:
361 | "@types/estree" "*"
362 |
363 | jsonfile@^4.0.0:
364 | version "4.0.0"
365 | resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
366 | integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=
367 | optionalDependencies:
368 | graceful-fs "^4.1.6"
369 |
370 | magic-string@^0.25.7:
371 | version "0.25.7"
372 | resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051"
373 | integrity sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==
374 | dependencies:
375 | sourcemap-codec "^1.4.4"
376 |
377 | match-casing@^2.0.0:
378 | version "2.0.0"
379 | resolved "https://registry.yarnpkg.com/match-casing/-/match-casing-2.0.0.tgz#76d56d1fd6ff75fc02b71f1a17c6180e12153f22"
380 | integrity sha512-yWG59YBJvFZLJC9PTtpckce1JVu3j0C4Am9/AO65qmug4iHdpMdzj2BC7FGoN5kGsjr1jS8i6zdeVy05P7SjWg==
381 |
382 | merge2@^1.2.3, merge2@^1.3.0:
383 | version "1.4.1"
384 | resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
385 | integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
386 |
387 | micromatch@^4.0.2:
388 | version "4.0.4"
389 | resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9"
390 | integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==
391 | dependencies:
392 | braces "^3.0.1"
393 | picomatch "^2.2.3"
394 |
395 | minimatch@^3.0.4:
396 | version "3.0.4"
397 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
398 | integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
399 | dependencies:
400 | brace-expansion "^1.1.7"
401 |
402 | moment@2.29.1:
403 | version "2.29.1"
404 | resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3"
405 | integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==
406 |
407 | obsidian@^0.12.0:
408 | version "0.12.2"
409 | resolved "https://registry.yarnpkg.com/obsidian/-/obsidian-0.12.2.tgz#0a4a41063c089502b5edc4a3edf5da4ba38afdba"
410 | integrity sha512-lvrJ6GEUkKrHCpbSDO6NdGdkSYQ/0ElGYizdcw4mRlMiHTOFgYjbqv+fEMkyLIneF0uB7epf9YNQEyFtRF2ESA==
411 | dependencies:
412 | "@types/codemirror" "0.0.108"
413 | moment "2.29.1"
414 |
415 | once@^1.3.0:
416 | version "1.4.0"
417 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
418 | integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
419 | dependencies:
420 | wrappy "1"
421 |
422 | path-is-absolute@^1.0.0:
423 | version "1.0.1"
424 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
425 | integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
426 |
427 | path-parse@^1.0.6:
428 | version "1.0.6"
429 | resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c"
430 | integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==
431 |
432 | path-type@^4.0.0:
433 | version "4.0.0"
434 | resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
435 | integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
436 |
437 | picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.2.3:
438 | version "2.2.3"
439 | resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.3.tgz#465547f359ccc206d3c48e46a1bcb89bf7ee619d"
440 | integrity sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg==
441 |
442 | queue-microtask@^1.2.2:
443 | version "1.2.3"
444 | resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
445 | integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
446 |
447 | resolve@^1.17.0, resolve@^1.19.0:
448 | version "1.20.0"
449 | resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975"
450 | integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==
451 | dependencies:
452 | is-core-module "^2.2.0"
453 | path-parse "^1.0.6"
454 |
455 | reusify@^1.0.4:
456 | version "1.0.4"
457 | resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
458 | integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
459 |
460 | rollup-plugin-copy@^3.4.0:
461 | version "3.4.0"
462 | resolved "https://registry.yarnpkg.com/rollup-plugin-copy/-/rollup-plugin-copy-3.4.0.tgz#f1228a3ffb66ffad8606e2f3fb7ff23141ed3286"
463 | integrity sha512-rGUmYYsYsceRJRqLVlE9FivJMxJ7X6jDlP79fmFkL8sJs7VVMSVyA2yfyL+PGyO/vJs4A87hwhgVfz61njI+uQ==
464 | dependencies:
465 | "@types/fs-extra" "^8.0.1"
466 | colorette "^1.1.0"
467 | fs-extra "^8.1.0"
468 | globby "10.0.1"
469 | is-plain-object "^3.0.0"
470 |
471 | rollup-plugin-css-only@^3.1.0:
472 | version "3.1.0"
473 | resolved "https://registry.yarnpkg.com/rollup-plugin-css-only/-/rollup-plugin-css-only-3.1.0.tgz#6a701cc5b051c6b3f0961e69b108a9a118e1b1df"
474 | integrity sha512-TYMOE5uoD76vpj+RTkQLzC9cQtbnJNktHPB507FzRWBVaofg7KhIqq1kGbcVOadARSozWF883Ho9KpSPKH8gqA==
475 | dependencies:
476 | "@rollup/pluginutils" "4"
477 |
478 | rollup@^2.32.1:
479 | version "2.48.0"
480 | resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.48.0.tgz#fceb01ed771f991f29f7bd2ff7838146e55acb74"
481 | integrity sha512-wl9ZSSSsi5579oscSDYSzGn092tCS076YB+TQrzsGuSfYyJeep8eEWj0eaRjuC5McuMNmcnR8icBqiE/FWNB1A==
482 | optionalDependencies:
483 | fsevents "~2.3.1"
484 |
485 | run-parallel@^1.1.9:
486 | version "1.2.0"
487 | resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee"
488 | integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==
489 | dependencies:
490 | queue-microtask "^1.2.2"
491 |
492 | slash@^3.0.0:
493 | version "3.0.0"
494 | resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
495 | integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
496 |
497 | sourcemap-codec@^1.4.4:
498 | version "1.4.8"
499 | resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4"
500 | integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==
501 |
502 | to-regex-range@^5.0.1:
503 | version "5.0.1"
504 | resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
505 | integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
506 | dependencies:
507 | is-number "^7.0.0"
508 |
509 | tslib@^2.2.0:
510 | version "2.2.0"
511 | resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.2.0.tgz#fb2c475977e35e241311ede2693cee1ec6698f5c"
512 | integrity sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==
513 |
514 | typescript@^4.2.4:
515 | version "4.2.4"
516 | resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.4.tgz#8610b59747de028fda898a8aef0e103f156d0961"
517 | integrity sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==
518 |
519 | universalify@^0.1.0:
520 | version "0.1.2"
521 | resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
522 | integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
523 |
524 | wrappy@1:
525 | version "1.0.2"
526 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
527 | integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
528 |
--------------------------------------------------------------------------------