obsidian-plugin-config 1.7.2 → 1.7.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/scripts/utils.ts CHANGED
@@ -1,151 +1,173 @@
1
- import { access, mkdir, copyFile, rm } from 'fs/promises';
2
- import path from 'path';
3
- import * as readline from 'readline';
4
- import { execSync } from 'child_process';
5
-
6
- export function createReadlineInterface(): readline.Interface {
7
- return readline.createInterface({
8
- input: process.stdin as NodeJS.ReadableStream,
9
- output: process.stdout as NodeJS.WritableStream
10
- });
11
- }
12
-
13
- export const askQuestion = async (
14
- question: string,
15
- rl: readline.Interface
16
- ): Promise<string> => {
17
- try {
18
- return await new Promise((resolve) =>
19
- rl.question(question, (input) => resolve(input.trim()))
20
- );
21
- } catch (error) {
22
- console.error('Error asking question:', error);
23
- throw error;
24
- }
25
- };
26
-
27
- /**
28
- * Ask a yes/no confirmation question with standardized logic
29
- * Accepts: y, yes, Y, YES, or empty (default to yes)
30
- * Rejects: n, no, N, NO
31
- * Invalid input defaults to no for safety
32
- */
33
- export const askConfirmation = async (
34
- question: string,
35
- rl: readline.Interface
36
- ): Promise<boolean> => {
37
- const answer = await askQuestion(`${question} [Y/n]: `, rl);
38
- const response = answer.toLowerCase();
39
-
40
- // Accept: y, yes, Y, YES, or empty (default to yes)
41
- // Reject: n, no, N, NO
42
- const isYes = response === '' || response === 'y' || response === 'yes';
43
- const isNo = response === 'n' || response === 'no';
44
-
45
- if (isNo) {
46
- return false;
47
- } else if (isYes) {
48
- return true;
49
- } else {
50
- console.log('Please answer Y (yes) or n (no). Defaulting to no for safety.');
51
- return false;
52
- }
53
- };
54
-
55
- export const cleanInput = (inputStr: string): string => {
56
- if (!inputStr) return '';
57
- return inputStr.trim().replace(/["`]/g, "'").replace(/\r\n/g, '\n');
58
- };
59
-
60
- export const isValidPath = async (pathToCheck: string): Promise<boolean> => {
61
- if (!pathToCheck) return false;
62
-
63
- try {
64
- // Using async fs.access is preferred over synchronous existsSync
65
- // as it doesn't block the main thread/event loop
66
- await access(pathToCheck.trim());
67
- return true;
68
- } catch {
69
- return false;
70
- }
71
- };
72
-
73
- export async function copyFilesToTargetDir(buildPath: string): Promise<void> {
74
- const pluginDir = process.cwd();
75
- const manifestSrc = path.join(pluginDir, 'manifest.json');
76
- const manifestDest = path.join(buildPath, 'manifest.json');
77
- const cssDest = path.join(buildPath, 'styles.css');
78
- const folderToRemove = path.join(buildPath, '_.._');
79
-
80
- try {
81
- await mkdir(buildPath, { recursive: true });
82
- } catch (error: unknown) {
83
- if ((error as NodeJS.ErrnoException).code !== 'EEXIST') {
84
- console.error(`Error creating directory: ${(error as Error).message}`);
85
- }
86
- }
87
-
88
- // Copy manifest
89
- try {
90
- await copyFile(manifestSrc, manifestDest);
91
- } catch (error: unknown) {
92
- console.error(`Error copying manifest: ${(error as Error).message}`);
93
- }
94
-
95
- // Copy CSS
96
- try {
97
- const srcStylesPath = path.join(pluginDir, 'src/styles.css');
98
- const rootStylesPath = path.join(pluginDir, 'styles.css');
99
-
100
- // First check if CSS exists in src/styles.css
101
- if (await isValidPath(srcStylesPath)) {
102
- await copyFile(srcStylesPath, cssDest);
103
- }
104
- // Otherwise, check if it exists in the root
105
- else if (await isValidPath(rootStylesPath)) {
106
- await copyFile(rootStylesPath, cssDest);
107
- if (await isValidPath(folderToRemove)) {
108
- await rm(folderToRemove, { recursive: true });
109
- }
110
- } else {
111
- return;
112
- }
113
- } catch (error: unknown) {
114
- console.error(`Error copying CSS: ${(error as Error).message}`);
115
- }
116
- }
117
-
118
- export function gitExec(command: string): void {
119
- try {
120
- execSync(command, { stdio: 'inherit' });
121
- } catch (error: unknown) {
122
- console.error(`Error executing '${command}':`, (error as Error).message);
123
- throw error;
124
- }
125
- }
126
-
127
- /**
128
- * Ensure Git repository is synchronized with remote before pushing
129
- */
130
- export async function ensureGitSync(): Promise<void> {
131
- try {
132
- console.log('🔄 Checking Git synchronization...');
133
-
134
- // Fetch latest changes from remote
135
- execSync('git fetch origin', { stdio: 'pipe' });
136
-
137
- // Check if branch is behind remote
138
- const status = execSync('git status --porcelain -b', { encoding: 'utf8' });
139
-
140
- if (status.includes('behind')) {
141
- console.log('📥 Branch behind remote. Pulling changes...');
142
- execSync('git pull', { stdio: 'inherit' });
143
- console.log(' Successfully pulled remote changes');
144
- } else {
145
- console.log('✅ Repository is synchronized with remote');
146
- }
147
- } catch (error: unknown) {
148
- console.error(`❌ Git sync failed: ${(error as Error).message}`);
149
- throw error;
150
- }
151
- }
1
+ import { access, mkdir, copyFile, rm } from 'fs/promises';
2
+ import path from 'path';
3
+ import * as readline from 'readline';
4
+ import { execSync } from 'child_process';
5
+
6
+ export function createReadlineInterface(): readline.Interface {
7
+ return readline.createInterface({
8
+ input: process.stdin as NodeJS.ReadableStream,
9
+ output: process.stdout as NodeJS.WritableStream
10
+ });
11
+ }
12
+
13
+ export const askQuestion = async (
14
+ question: string,
15
+ rl: readline.Interface
16
+ ): Promise<string> => {
17
+ try {
18
+ return await new Promise((resolve) =>
19
+ rl.question(question, (input) => resolve(input.trim()))
20
+ );
21
+ } catch (error) {
22
+ console.error('Error asking question:', error);
23
+ throw error;
24
+ }
25
+ };
26
+
27
+ /**
28
+ * Ask a yes/no confirmation question with standardized logic
29
+ * Accepts: y, yes, Y, YES, or empty (default to yes)
30
+ * Rejects: n, no, N, NO
31
+ * Invalid input defaults to no for safety
32
+ */
33
+ export const askConfirmation = async (
34
+ question: string,
35
+ rl: readline.Interface
36
+ ): Promise<boolean> => {
37
+ const answer = await askQuestion(`${question} [Y/n]: `, rl);
38
+ const response = answer.toLowerCase();
39
+
40
+ // Accept: y, yes, Y, YES, or empty (default to yes)
41
+ // Reject: n, no, N, NO
42
+ const isYes = response === '' || response === 'y' || response === 'yes';
43
+ const isNo = response === 'n' || response === 'no';
44
+
45
+ if (isNo) {
46
+ return false;
47
+ } else if (isYes) {
48
+ return true;
49
+ } else {
50
+ console.log('Please answer Y (yes) or n (no). Defaulting to no for safety.');
51
+ return false;
52
+ }
53
+ };
54
+
55
+ export const cleanInput = (inputStr: string): string => {
56
+ if (!inputStr) return '';
57
+ return inputStr.trim().replace(/["`]/g, "'").replace(/\r\n/g, '\n');
58
+ };
59
+
60
+ export const isValidPath = async (pathToCheck: string): Promise<boolean> => {
61
+ if (!pathToCheck) return false;
62
+
63
+ try {
64
+ // Using async fs.access is preferred over synchronous existsSync
65
+ // as it doesn't block the main thread/event loop
66
+ await access(pathToCheck.trim());
67
+ return true;
68
+ } catch {
69
+ return false;
70
+ }
71
+ };
72
+
73
+ export async function copyFilesToTargetDir(buildPath: string): Promise<void> {
74
+ const pluginDir = process.cwd();
75
+ const manifestSrc = path.join(pluginDir, 'manifest.json');
76
+ const manifestDest = path.join(buildPath, 'manifest.json');
77
+ const cssDest = path.join(buildPath, 'styles.css');
78
+ const folderToRemove = path.join(buildPath, '_.._');
79
+
80
+ try {
81
+ await mkdir(buildPath, { recursive: true });
82
+ } catch (error: unknown) {
83
+ if ((error as NodeJS.ErrnoException).code !== 'EEXIST') {
84
+ console.error(`Error creating directory: ${(error as Error).message}`);
85
+ }
86
+ }
87
+
88
+ // Copy manifest
89
+ try {
90
+ await copyFile(manifestSrc, manifestDest);
91
+ } catch (error: unknown) {
92
+ console.error(`Error copying manifest: ${(error as Error).message}`);
93
+ }
94
+
95
+ // Copy CSS
96
+ try {
97
+ const srcStylesPath = path.join(pluginDir, 'src/styles.css');
98
+ const rootStylesPath = path.join(pluginDir, 'styles.css');
99
+
100
+ // First check if CSS exists in src/styles.css
101
+ if (await isValidPath(srcStylesPath)) {
102
+ await copyFile(srcStylesPath, cssDest);
103
+ }
104
+ // Otherwise, check if it exists in the root
105
+ else if (await isValidPath(rootStylesPath)) {
106
+ await copyFile(rootStylesPath, cssDest);
107
+ if (await isValidPath(folderToRemove)) {
108
+ await rm(folderToRemove, { recursive: true });
109
+ }
110
+ } else {
111
+ return;
112
+ }
113
+ } catch (error: unknown) {
114
+ console.error(`Error copying CSS: ${(error as Error).message}`);
115
+ }
116
+ }
117
+
118
+ /**
119
+ * Execute a shell command.
120
+ * Uses shell spawning for cross-platform compatibility (Windows + Linux).
121
+ * @param pipe - set to true to suppress stdout/stderr output
122
+ */
123
+ export function gitExec(command: string, cwd?: string, pipe = false): void {
124
+ try {
125
+ execSync(command, {
126
+ stdio: pipe ? 'pipe' : 'inherit',
127
+ shell: process.platform === 'win32' ? 'cmd.exe' : '/bin/sh',
128
+ cwd
129
+ });
130
+ } catch (error: unknown) {
131
+ console.error(`Error executing '${command}':`, (error as Error).message);
132
+ throw error;
133
+ }
134
+ }
135
+
136
+ /**
137
+ * Execute a shell command and return its stdout as a trimmed string.
138
+ * Uses shell spawning for cross-platform compatibility (Windows + Linux).
139
+ */
140
+ export function gitOutput(command: string, cwd?: string): string {
141
+ const result = execSync(command, {
142
+ encoding: 'utf8',
143
+ shell: process.platform === 'win32' ? 'cmd.exe' : '/bin/sh',
144
+ cwd
145
+ });
146
+ return result.trim();
147
+ }
148
+
149
+ /**
150
+ * Ensure Git repository is synchronized with remote before pushing
151
+ */
152
+ export async function ensureGitSync(): Promise<void> {
153
+ try {
154
+ console.log('🔄 Checking Git synchronization...');
155
+
156
+ // Fetch latest changes from remote
157
+ gitExec('git fetch origin');
158
+
159
+ // Check if branch is behind remote
160
+ const status = gitOutput('git status --porcelain -b');
161
+
162
+ if (status.includes('behind')) {
163
+ console.log('📥 Branch behind remote. Pulling changes...');
164
+ gitExec('git pull');
165
+ console.log('✅ Successfully pulled remote changes');
166
+ } else {
167
+ console.log('✅ Repository is synchronized with remote');
168
+ }
169
+ } catch (error: unknown) {
170
+ console.error(`❌ Git sync failed: ${(error as Error).message}`);
171
+ throw error;
172
+ }
173
+ }
@@ -12,9 +12,9 @@ jobs:
12
12
  contents: write
13
13
 
14
14
  steps:
15
- - uses: actions/checkout@v3
15
+ - uses: actions/checkout@v4
16
16
  - name: Use Node.js
17
- uses: actions/setup-node@v3
17
+ uses: actions/setup-node@v4
18
18
  with:
19
19
  node-version: "22.x"
20
20
 
@@ -60,7 +60,7 @@
60
60
  {
61
61
  "label": "Obsidian Inject (no confirm)",
62
62
  "type": "shell",
63
- "command": "obsidian-inject --no",
63
+ "command": "obsidian-inject --yes",
64
64
  "group": "build",
65
65
  "presentation": { "reveal": "always", "panel": "shared" },
66
66
  "problemMatcher": []
@@ -25,7 +25,7 @@
25
25
  },
26
26
  "devDependencies": {
27
27
  "@types/eslint": "latest",
28
- "@types/node": "latest",
28
+ "@types/node": "^25.9.2",
29
29
  "@typescript-eslint/eslint-plugin": "latest",
30
30
  "@typescript-eslint/parser": "latest",
31
31
  "builtin-modules": "latest",
@@ -1,9 +1,9 @@
1
- import { execSync } from 'child_process';
2
1
  import {
3
2
  askQuestion,
4
3
  cleanInput,
5
4
  createReadlineInterface,
6
5
  gitExec,
6
+ gitOutput,
7
7
  ensureGitSync
8
8
  } from './utils.ts';
9
9
 
@@ -30,7 +30,7 @@ async function main(): Promise<void> {
30
30
  }
31
31
 
32
32
  // get current branch name
33
- const currentBranch = execSync('git rev-parse --abbrev-ref HEAD').toString().trim();
33
+ const currentBranch = gitOutput('git rev-parse --abbrev-ref HEAD');
34
34
 
35
35
  // Ensure Git is synchronized before pushing
36
36
  await ensureGitSync();
@@ -1,6 +1,5 @@
1
1
  import path from 'path';
2
- import { readFileSync } from 'fs';
3
- import fs from 'fs';
2
+ import { readFile } from 'fs/promises';
4
3
  import { type Interface } from 'readline';
5
4
  import {
6
5
  isValidPath,
@@ -18,11 +17,11 @@ interface Manifest {
18
17
 
19
18
  export type { Manifest };
20
19
 
21
- export function checkManifest(pluginDir: string): Manifest {
20
+ export async function checkManifest(pluginDir: string): Promise<Manifest> {
22
21
  const manifestPath = path.join(pluginDir, 'manifest.json');
23
22
 
24
23
  // Check if manifest exists (for plugin-config itself, it might not exist)
25
- if (!fs.existsSync(manifestPath)) {
24
+ if (!(await isValidPath(manifestPath))) {
26
25
  console.log(
27
26
  '⚠️ No manifest.json found - this script is designed for Obsidian plugins'
28
27
  );
@@ -30,7 +29,7 @@ export function checkManifest(pluginDir: string): Manifest {
30
29
  process.exit(0);
31
30
  }
32
31
 
33
- return JSON.parse(readFileSync(manifestPath, 'utf-8'));
32
+ return JSON.parse(await readFile(manifestPath, 'utf-8'));
34
33
  }
35
34
 
36
35
  /**
@@ -16,7 +16,7 @@ import {
16
16
  copyFilesToTargetDir,
17
17
  createReadlineInterface,
18
18
  isValidPath,
19
- removeMainCss
19
+ renameMainCss
20
20
  } from './utils.ts';
21
21
  import { reloadObsidian } from './reload.ts';
22
22
 
@@ -26,7 +26,7 @@ const pluginDir = process.cwd();
26
26
  // Create readline interface for prompts
27
27
  const rl: Interface = createReadlineInterface();
28
28
 
29
- const manifest: Manifest = checkManifest(pluginDir);
29
+ const manifest: Manifest = await checkManifest(pluginDir);
30
30
 
31
31
  config();
32
32
 
@@ -56,11 +56,11 @@ async function createBuildContext(
56
56
  }
57
57
  })(),
58
58
  {
59
- name: 'remove-main-css',
59
+ name: 'rename-main-css',
60
60
  setup(build: esbuild.PluginBuild): void {
61
61
  build.onEnd(async (result) => {
62
62
  if (result.errors.length === 0) {
63
- await removeMainCss(buildPath);
63
+ await renameMainCss(buildPath);
64
64
  }
65
65
  });
66
66
  }
@@ -154,4 +154,3 @@ async function main(): Promise<void> {
154
154
  }
155
155
 
156
156
  main().catch(console.error);
157
-
@@ -1,11 +1,12 @@
1
1
  import { writeFile, stat } from 'fs/promises';
2
- import { execSync } from 'child_process';
3
2
  import dedent from 'dedent';
4
3
  import { join } from 'path';
5
4
  import {
6
5
  askConfirmation,
7
6
  askQuestion,
8
7
  createReadlineInterface,
8
+ gitExec,
9
+ gitOutput,
9
10
  ensureGitSync
10
11
  } from './utils.ts';
11
12
 
@@ -31,9 +32,7 @@ async function handleExistingTag(tag: string): Promise<boolean> {
31
32
  // Get the existing tag message to show to the user
32
33
  let existingMessage = '';
33
34
  try {
34
- existingMessage = execSync(`git tag -l -n999 ${tag}`, {
35
- encoding: 'utf8'
36
- }).trim();
35
+ existingMessage = gitOutput(`git tag -l -n999 ${tag}`);
37
36
  } catch {
38
37
  // If we can't get the message, continue anyway
39
38
  }
@@ -51,8 +50,8 @@ async function handleExistingTag(tag: string): Promise<boolean> {
51
50
  return false;
52
51
  }
53
52
 
54
- execSync(`git tag -d ${tag}`);
55
- execSync(`git push origin :refs/tags/${tag}`);
53
+ gitExec(`git tag -d ${tag}`);
54
+ gitExec(`git push origin :refs/tags/${tag}`);
56
55
  console.log(`Deleted existing tag ${tag} locally and remotely.`);
57
56
  return true;
58
57
  }
@@ -62,9 +61,7 @@ async function createTag(): Promise<void> {
62
61
  const tag = `${currentVersion}`;
63
62
 
64
63
  await checkOrCreateFile(body);
65
- const exists = execSync('git ls-remote --tags origin')
66
- .toString()
67
- .includes(`refs/tags/${tag}`);
64
+ const exists = gitOutput('git ls-remote --tags origin').includes(`refs/tags/${tag}`);
68
65
 
69
66
  if (exists && !(await handleExistingTag(tag))) {
70
67
  rl.close();
@@ -89,28 +86,28 @@ async function doNextSteps(message: string, tag: string): Promise<void> {
89
86
  const tagMessage = messages.map((m) => `-m "${m}"`).join(' ');
90
87
 
91
88
  try {
92
- execSync('git add -A');
93
- execSync('git commit -m "update tag description"');
89
+ gitExec('git add -A');
90
+ gitExec('git commit -m "update tag description"');
94
91
 
95
92
  // Ensure Git is synchronized before pushing
96
93
  await ensureGitSync();
97
94
 
98
- execSync('git push');
95
+ gitExec('git push');
99
96
  } catch (error: unknown) {
100
97
  console.error('Error:', error instanceof Error ? error.message : String(error));
101
98
  }
102
99
  try {
103
- execSync(`git tag -a ${tag} ${tagMessage}`);
100
+ gitExec(`git tag -a ${tag} ${tagMessage}`);
104
101
  } catch {
105
- execSync(`git tag -d ${tag}`);
106
- execSync(`git push origin :refs/tags/${tag}`);
102
+ gitExec(`git tag -d ${tag}`);
103
+ gitExec(`git push origin :refs/tags/${tag}`);
107
104
  console.log('Fixed');
108
- execSync(`git tag -a ${tag} ${tagMessage}`);
105
+ gitExec(`git tag -a ${tag} ${tagMessage}`);
109
106
  }
110
107
  // Ensure Git is synchronized before pushing tag
111
108
  await ensureGitSync();
112
109
 
113
- execSync(`git push origin ${tag}`);
110
+ gitExec(`git push origin ${tag}`);
114
111
  console.log(`Release ${tag} pushed to repo.`);
115
112
  console.log(dedent`
116
113
  with message:
@@ -1,5 +1,4 @@
1
- import { access, mkdir, copyFile, rm, writeFile } from 'fs/promises';
2
- import { existsSync, readFileSync } from 'fs';
1
+ import { access, mkdir, copyFile, rm, writeFile, rename, readFile } from 'fs/promises';
3
2
  import path from 'path';
4
3
  import * as readline from 'readline';
5
4
  import { execSync } from 'child_process';
@@ -122,9 +121,18 @@ export async function copyFilesToTargetDir(buildPath: string): Promise<void> {
122
121
  }
123
122
  }
124
123
 
125
- export function gitExec(command: string): void {
124
+ /**
125
+ * Execute a shell command.
126
+ * Uses shell spawning for cross-platform compatibility (Windows + Linux).
127
+ * @param pipe - set to true to suppress stdout/stderr output
128
+ */
129
+ export function gitExec(command: string, cwd?: string, pipe = false): void {
126
130
  try {
127
- execSync(command, { stdio: 'inherit' });
131
+ execSync(command, {
132
+ stdio: pipe ? 'pipe' : 'inherit',
133
+ shell: process.platform === 'win32' ? 'cmd.exe' : '/bin/sh',
134
+ cwd
135
+ });
128
136
  } catch (error: unknown) {
129
137
  console.error(
130
138
  `Error executing '${command}':`,
@@ -134,6 +142,19 @@ export function gitExec(command: string): void {
134
142
  }
135
143
  }
136
144
 
145
+ /**
146
+ * Execute a shell command and return its stdout as a trimmed string.
147
+ * Uses shell spawning for cross-platform compatibility (Windows + Linux).
148
+ */
149
+ export function gitOutput(command: string, cwd?: string): string {
150
+ const result = execSync(command, {
151
+ encoding: 'utf8',
152
+ shell: process.platform === 'win32' ? 'cmd.exe' : '/bin/sh',
153
+ cwd
154
+ });
155
+ return result.trim();
156
+ }
157
+
137
158
  /**
138
159
  * Ensure Git repository is synchronized with remote before pushing
139
160
  */
@@ -142,14 +163,14 @@ export async function ensureGitSync(): Promise<void> {
142
163
  console.log('🔄 Checking Git synchronization...');
143
164
 
144
165
  // Fetch latest changes from remote
145
- execSync('git fetch origin', { stdio: 'pipe' });
166
+ gitExec('git fetch origin');
146
167
 
147
168
  // Check if branch is behind remote
148
- const status = execSync('git status --porcelain -b', { encoding: 'utf8' });
169
+ const status = gitOutput('git status --porcelain -b');
149
170
 
150
171
  if (status.includes('behind')) {
151
172
  console.log('📥 Branch behind remote. Pulling changes...');
152
- execSync('git pull', { stdio: 'inherit' });
173
+ gitExec('git pull');
153
174
  console.log('✅ Successfully pulled remote changes');
154
175
  } else {
155
176
  console.log('✅ Repository is synchronized with remote');
@@ -163,19 +184,20 @@ export async function ensureGitSync(): Promise<void> {
163
184
  }
164
185
 
165
186
  /**
166
- * Remove main.css file generated by esbuild when compiling SCSS
167
- * This prevents the unwanted main.css from being included in the plugin
187
+ * Rename main.css file generated by esbuild when compiling SCSS to styles.css
188
+ * This ensures the compiled CSS is correctly recognized by Obsidian
168
189
  */
169
- export async function removeMainCss(outdir: string): Promise<void> {
190
+ export async function renameMainCss(outdir: string): Promise<void> {
170
191
  const mainCssPath = path.join(outdir, 'main.css');
192
+ const stylesCssPath = path.join(outdir, 'styles.css');
171
193
  try {
172
- await rm(mainCssPath);
173
- } catch (error: unknown) {
174
- if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {
175
- console.warn(
176
- `Warning: Could not remove main.css: ${error instanceof Error ? error.message : String(error)}`
177
- );
194
+ if (await isValidPath(mainCssPath)) {
195
+ await rename(mainCssPath, stylesCssPath);
178
196
  }
197
+ } catch (error: unknown) {
198
+ console.warn(
199
+ `Warning: Could not rename main.css to styles.css: ${error instanceof Error ? error.message : String(error)}`
200
+ );
179
201
  }
180
202
  }
181
203
 
@@ -185,22 +207,17 @@ export function isInPluginsFolder(currentPath: string): boolean {
185
207
  }
186
208
 
187
209
  /** Validates that a path points to an Obsidian vault with a plugins directory */
188
- export function validateVaultPath(vaultPath: string): boolean {
210
+ export async function validateVaultPath(vaultPath: string): Promise<boolean> {
189
211
  // Normalize path to handle both forward and backward slashes
190
212
  const normalizedPath = path.normalize(vaultPath);
191
213
  return (
192
- existsSync(path.join(normalizedPath, '.obsidian')) &&
193
- existsSync(path.join(normalizedPath, '.obsidian', 'plugins'))
214
+ (await isValidPath(path.join(normalizedPath, '.obsidian'))) &&
215
+ (await isValidPath(path.join(normalizedPath, '.obsidian', 'plugins')))
194
216
  );
195
217
  }
196
218
 
197
219
  /** Resolves the full plugin install path from a vault path */
198
220
  export function getVaultPath(vaultPath: string, pluginId: string): string {
199
- if (!validateVaultPath(vaultPath)) {
200
- console.error(`❌ Invalid vault path: ${vaultPath}`);
201
- console.error(` The path must contain a .obsidian/plugins directory`);
202
- process.exit(1);
203
- }
204
221
  const pluginsPath = path.join('.obsidian', 'plugins');
205
222
  return vaultPath.includes(pluginsPath)
206
223
  ? path.join(vaultPath, pluginId)
@@ -215,7 +232,7 @@ export async function updateEnvFile(
215
232
  ): Promise<void> {
216
233
  let envContent = '';
217
234
  try {
218
- envContent = readFileSync(envPath, 'utf8');
235
+ envContent = await readFile(envPath, 'utf8');
219
236
  } catch {
220
237
  /* file doesn't exist yet */
221
238
  }
package/tsconfig.json CHANGED
@@ -19,6 +19,6 @@
19
19
  "resolveJsonModule": true,
20
20
  "lib": ["DOM", "ES2024"]
21
21
  },
22
- "include": ["./scripts/**/*.ts"],
23
- "exclude": ["node_modules", "eslint.config.ts", "templates"]
22
+ "include": ["./scripts/**/*.ts", "templates/**/*.ts"],
23
+ "exclude": ["node_modules", "eslint.config.ts"]
24
24
  }