Skip to content

Commit 9ef82de

Browse files
authored
docs: install eslint-plugin-jsdoc and fix all errors (#27)
1 parent 1a1f8c5 commit 9ef82de

10 files changed

+190
-10
lines changed

boundaries.config.js

+18-3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ const libDir = "lib";
33
// EXPORTS
44
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
55

6+
/** @see {@link https://github.com/javierbrea/eslint-plugin-boundaries#rules-configuration} */
67
export const ELEMENTS = [
78
{ type: "shared", pattern: "src/util", mode: "folder" },
89
{ type: "main", pattern: "src/main.tsx", mode: "full" },
@@ -28,6 +29,7 @@ export const ELEMENTS = [
2829
defineFolderScope("model"),
2930
];
3031

32+
/** @see {@link https://github.com/javierbrea/eslint-plugin-boundaries#rules-configuration} */
3133
export const ELEMENT_TYPE_RULES = [
3234
{ from: "*", allow: "shared" },
3335
{ from: "main", allow: [["lib", { lib: "obsidian" }]] },
@@ -38,18 +40,25 @@ export const ELEMENT_TYPE_RULES = [
3840
...fromScopeElementAllowTargetScopeElements("model", "index", [["model", "collection"]]),
3941
];
4042

43+
/** @see {@link https://github.com/javierbrea/eslint-plugin-boundaries#rules-configuration} */
4144
export const EXTERNAL_RULES = [
4245
{ from: "*", allow: ["aggregate-error", "assert", "lodash", "luxon", "path", "utility-types"] },
4346
{ from: "lib", allow: "${from.lib}" },
4447
];
4548

46-
// HELPERS
47-
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
48-
49+
/**
50+
* Defines a "scope" of code, or a folder that needs explicit permission to import from anywhere else.
51+
* @param {string} folderName - the name of the folder and, consequently, the scope.
52+
* @returns {object} the eslint-plugin-boundaries rule that will define the scope as its own type.
53+
*/
4954
function defineFolderScope(folderName) {
5055
return { type: folderName, pattern: `src/(${folderName})/*`, capture: ["scope", "elementName"], mode: "folder" };
5156
}
5257

58+
/**
59+
* @param {string} scope - the name of the scope.
60+
* @returns {Array} the set of rules that will allow "scopes" to import from code in the same folder.
61+
*/
5362
function fromScopeAllowItself(scope) {
5463
return [
5564
{
@@ -59,6 +68,12 @@ function fromScopeAllowItself(scope) {
5968
];
6069
}
6170

71+
/**
72+
* @param {string} fromScope - the scope where imports will be allowed from.
73+
* @param {string} fromElementName - the name of the scope-element (i.e., "src/model/task" -> "task").
74+
* @param {Array} targets - the list of scope/name pairs to allow imports from.
75+
* @returns {Array} the set of rules that will allow "scopes" to import from code in the same folder.
76+
*/
6277
function fromScopeElementAllowTargetScopeElements(fromScope, fromElementName, targets) {
6378
return [
6479
{

eslint.config.js

+20-2
Original file line numberDiff line numberDiff line change
@@ -6,34 +6,49 @@ import eslintPluginReactHooks from "eslint-plugin-react-hooks";
66
import globals from "globals";
77
import typescriptEslintParser from "@typescript-eslint/parser";
88
import typescriptEslintPlugin from "@typescript-eslint/eslint-plugin";
9+
import eslintPluginJsdoc from "eslint-plugin-jsdoc";
910
import eslintPluginTsdoc from "eslint-plugin-tsdoc";
1011
import { ELEMENTS, ELEMENT_TYPE_RULES, EXTERNAL_RULES } from "./boundaries.config.js";
12+
import eslintJs from "@eslint/js";
1113

1214
/** @type { import("eslint").Linter.Config[] } */
1315
export default [
1416
{ ignores: [".husky/", "coverage/", "docs/", "dist/", "node_modules/"] },
1517

18+
eslintJs.configs.recommended,
1619
eslintPluginImport.flatConfigs.recommended,
1720
...eslintConfigPreact.flat,
1821

1922
{
2023
files: ["*.config.js"],
21-
rules: { "import/no-named-as-default": "off", "import/no-unresolved": "off" },
24+
plugins: { jsdoc: eslintPluginJsdoc },
2225
languageOptions: { globals: { ...globals.node } },
26+
...eslintPluginJsdoc.configs["flat/recommended-error"],
27+
rules: {
28+
...eslintPluginJsdoc.configs["flat/recommended-error"].rules,
29+
"import/no-named-as-default": "off",
30+
"import/no-unresolved": "off",
31+
},
32+
settings: {
33+
"import/core-modules": ["eslint-config-preact", "eslint-plugin-boundaries", "eslint-plugin-import"],
34+
},
2335
},
2436

2537
{
2638
files: ["src/**/*.{ts,tsx}"],
2739
plugins: {
28-
"tsdoc": eslintPluginTsdoc,
2940
"@typescript-eslint": typescriptEslintPlugin,
41+
"jsdoc": eslintPluginJsdoc,
3042
"react-hooks": eslintPluginReactHooks,
43+
"tsdoc": eslintPluginTsdoc,
3144
},
3245
rules: {
3346
...typescriptEslintPlugin.configs.recommended.rules,
3447
...typescriptEslintPlugin.configs.strict.rules,
48+
...eslintPluginJsdoc.configs["flat/recommended-typescript-error"].rules,
3549
...eslintPluginReactHooks.configs.recommended.rules,
3650

51+
// https://tsdoc.org/pages/packages/eslint-plugin-tsdoc/
3752
"tsdoc/syntax": "error",
3853

3954
// TypeScript already checks for duplicates: https://archive.eslint.org/docs/rules/no-dupe-class-members
@@ -52,6 +67,9 @@ export default [
5267
"import/no-named-as-default-member": "error",
5368
"import/no-duplicates": "error",
5469
},
70+
settings: {
71+
"import/core-modules": ["obsidian"],
72+
},
5573
languageOptions: {
5674
globals: { ...globals.browser },
5775
parser: typescriptEslintParser,

package-lock.json

+117
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
"eslint-plugin-boundaries": "^5.0.1",
5656
"eslint-plugin-compat": "^6.0.2",
5757
"eslint-plugin-import": "^2.31.0",
58+
"eslint-plugin-jsdoc": "^50.6.3",
5859
"eslint-plugin-prettier": "^5.2.3",
5960
"eslint-plugin-react": "^7.37.4",
6061
"eslint-plugin-react-hooks": "^5.2.0",

src/model/index/schema.ts

-2
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,8 @@ import { Collection } from "../collection/schema";
77
export interface VaultIndex {
88
/**
99
* Register a new {@link Collection} into the index.
10-
*
1110
* All calls to this function will be treated as adding a brand new collection, regardless
1211
* of whether the input has already been registered in an earlier call.
13-
*
1412
* @param collection - the collection to register with this index.
1513
* @throws if the index cannot support the given collection.
1614
* @returns a {@link VaultCollectionIndex} that can be used to interact with the primary index.

src/model/task/lib/obsidian-dataview.ts

+4
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ import { DeepPartial } from "utility-types";
33
import { DataviewMarkdownTask } from "@/lib/obsidian-dataview/types";
44
import { Task } from "@/model/task/schema";
55

6+
/**
7+
* @param task - the {@link DataviewMarkdownTask} to extract metadata from.
8+
* @returns a {@link Task} with the extracted metadata.
9+
*/
610
export function adaptDataviewMarkdownTask(task: DataviewMarkdownTask): DeepPartial<Task> {
711
return {
812
status: {

src/model/task/lib/obsidian-tasks.ts

+18
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,19 @@ import { DeepPartial, PickByValue } from "utility-types";
55
import { Task } from "@/model/task/schema";
66
import { PathOf } from "@/util/type-utils";
77

8+
/**
9+
* Parses {@link Task} metadata from a real markdown task using the Obsidian Task plugin's syntax.
10+
* Specifically, the text is expected to find occurrences from the {@link SYMBOL_PATH_LOOKUP} and write to the
11+
* corresponding field using the proceeding text.
12+
* @example
13+
* ```
14+
* "the text at the front is assumed to be a description. ❌ cancelled date ➕ created date ✅ completed date"
15+
* ( symbol & value )(symbol & value)( symbol & value )
16+
* ```
17+
* @see {@link https://publish.obsidian.md/tasks/Reference/Task+Formats/Tasks+Emoji+Format}
18+
* @param text - the text of the task without its' markdown text.
19+
* @returns a {@link Task} with the parsed metadata.
20+
*/
821
export function parseTaskEmojiFormat(text: string): DeepPartial<Task> {
922
const matchedSymbols = [...text.matchAll(SYMBOL_REG_EXP), /$/.exec(text) as RegExpExecArray];
1023
const textBeforeAllSymbols = text.slice(0, matchedSymbols[0].index);
@@ -33,6 +46,11 @@ export function parseTaskEmojiFormat(text: string): DeepPartial<Task> {
3346
return result;
3447
}
3548

49+
/**
50+
* Parses {@link Task} metadata from a task's header.
51+
* @param headerText - the text that appears _before_ all of the symbols.
52+
* @returns the {@link Task} metadata fields parsed from the header; unparsed data will become {@link Task.description}.
53+
*/
3654
function parseTaskHeader(headerText: string): DeepPartial<Task> {
3755
const [isoString, isoStringSuffix] = headerText.split(/\s+/, 2);
3856

src/model/task/util/merge.ts

+11
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,21 @@ import { DeepPartial } from "utility-types";
55
import { DEFAULT_PRIORITY_VALUE, DEFAULT_TYPE_VALUE, TASK_WITH_DEFAULT_VALUES } from "@/model/task/constants";
66
import { Task } from "@/model/task/schema";
77

8+
/**
9+
* @param parts - the task pieces to merge.
10+
* @returns a new {@link Task} with values taken from the front-most non-default {@link parts} encountered.
11+
* @see {@link TASK_WITH_DEFAULT_VALUES}
12+
*/
813
export function mergeTaskParts(...parts: DeepPartial<Task>[]): Task {
914
return parts.reduce(replaceDefaultValues, { ...TASK_WITH_DEFAULT_VALUES });
1015
}
1116

17+
/**
18+
* Replaces any default values in {@link task} with non-default values from {@link part}.
19+
* @param task - the task to modify.
20+
* @param part - the task piece to take from.
21+
* @returns the modified {@link Task}.
22+
*/
1223
function replaceDefaultValues(task: Task, part: DeepPartial<Task>): Task {
1324
return _.mergeWith(task, part, (oldValue, newValue, propName) => {
1425
if (propName === "type" && _.isString(oldValue) && _.isString(newValue)) {

0 commit comments

Comments
 (0)