obsidian-plugin-config 1.7.3 → 1.7.6
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/README.md +7 -7
- package/bin/obsidian-inject.js +21 -13
- package/docs/INTERACTIVE_INJECTION.md +41 -50
- package/docs/SCSS-FLOW.md +92 -88
- package/package.json +1 -1
- package/scripts/acp.ts +10 -12
- package/scripts/build-npm.ts +397 -393
- package/scripts/help.ts +87 -87
- package/scripts/inject-core.ts +870 -898
- package/scripts/inject-path.ts +157 -156
- package/scripts/inject-prompt.ts +104 -104
- package/scripts/utils.ts +173 -151
- package/templates/.vscode/tasks.json +1 -1
- package/templates/scripts/acp.ts +3 -6
- package/templates/scripts/env.ts +4 -5
- package/templates/scripts/esbuild.config.ts +1 -2
- package/templates/scripts/release.ts +22 -21
- package/templates/scripts/utils.ts +33 -17
- package/docs/audit.md +0 -39
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
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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
|
+
}
|
package/templates/scripts/acp.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { execSync, spawnSync } 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
|
|
|
@@ -23,17 +23,14 @@ async function main(): Promise<void> {
|
|
|
23
23
|
|
|
24
24
|
try {
|
|
25
25
|
gitExec('git add -A');
|
|
26
|
-
|
|
27
|
-
if (result.status !== 0) {
|
|
28
|
-
throw new Error('Commit failed');
|
|
29
|
-
}
|
|
26
|
+
gitExec(`git commit -m "${cleanedInput}"`);
|
|
30
27
|
} catch {
|
|
31
28
|
console.log('Commit already exists or failed.');
|
|
32
29
|
return;
|
|
33
30
|
}
|
|
34
31
|
|
|
35
32
|
// get current branch name
|
|
36
|
-
const currentBranch =
|
|
33
|
+
const currentBranch = gitOutput('git rev-parse --abbrev-ref HEAD');
|
|
37
34
|
|
|
38
35
|
// Ensure Git is synchronized before pushing
|
|
39
36
|
await ensureGitSync();
|
package/templates/scripts/env.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
|
-
import {
|
|
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 (!
|
|
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(
|
|
32
|
+
return JSON.parse(await readFile(manifestPath, 'utf-8'));
|
|
34
33
|
}
|
|
35
34
|
|
|
36
35
|
/**
|
|
@@ -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
|
|
|
@@ -154,4 +154,3 @@ async function main(): Promise<void> {
|
|
|
154
154
|
}
|
|
155
155
|
|
|
156
156
|
main().catch(console.error);
|
|
157
|
-
|
|
@@ -1,11 +1,13 @@
|
|
|
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,
|
|
7
|
+
cleanInput,
|
|
8
8
|
createReadlineInterface,
|
|
9
|
+
gitExec,
|
|
10
|
+
gitOutput,
|
|
9
11
|
ensureGitSync
|
|
10
12
|
} from './utils.ts';
|
|
11
13
|
|
|
@@ -31,9 +33,7 @@ async function handleExistingTag(tag: string): Promise<boolean> {
|
|
|
31
33
|
// Get the existing tag message to show to the user
|
|
32
34
|
let existingMessage = '';
|
|
33
35
|
try {
|
|
34
|
-
existingMessage =
|
|
35
|
-
encoding: 'utf8'
|
|
36
|
-
}).trim();
|
|
36
|
+
existingMessage = gitOutput(`git tag -l -n999 ${tag}`);
|
|
37
37
|
} catch {
|
|
38
38
|
// If we can't get the message, continue anyway
|
|
39
39
|
}
|
|
@@ -51,8 +51,8 @@ async function handleExistingTag(tag: string): Promise<boolean> {
|
|
|
51
51
|
return false;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
gitExec(`git tag -d ${tag}`);
|
|
55
|
+
gitExec(`git push origin :refs/tags/${tag}`);
|
|
56
56
|
console.log(`Deleted existing tag ${tag} locally and remotely.`);
|
|
57
57
|
return true;
|
|
58
58
|
}
|
|
@@ -62,9 +62,7 @@ async function createTag(): Promise<void> {
|
|
|
62
62
|
const tag = `${currentVersion}`;
|
|
63
63
|
|
|
64
64
|
await checkOrCreateFile(body);
|
|
65
|
-
const exists =
|
|
66
|
-
.toString()
|
|
67
|
-
.includes(`refs/tags/${tag}`);
|
|
65
|
+
const exists = gitOutput('git ls-remote --tags origin').includes(`refs/tags/${tag}`);
|
|
68
66
|
|
|
69
67
|
if (exists && !(await handleExistingTag(tag))) {
|
|
70
68
|
rl.close();
|
|
@@ -74,12 +72,12 @@ async function createTag(): Promise<void> {
|
|
|
74
72
|
}
|
|
75
73
|
|
|
76
74
|
async function doCommit(currentVersion: string | undefined, tag: string): Promise<void> {
|
|
77
|
-
const
|
|
75
|
+
const raw = await askQuestion(
|
|
78
76
|
`Enter the commit message for version ${currentVersion}: `,
|
|
79
77
|
rl
|
|
80
78
|
);
|
|
81
79
|
rl.close();
|
|
82
|
-
await doNextSteps(
|
|
80
|
+
await doNextSteps(cleanInput(raw), tag);
|
|
83
81
|
}
|
|
84
82
|
|
|
85
83
|
async function doNextSteps(message: string, tag: string): Promise<void> {
|
|
@@ -89,28 +87,31 @@ async function doNextSteps(message: string, tag: string): Promise<void> {
|
|
|
89
87
|
const tagMessage = messages.map((m) => `-m "${m}"`).join(' ');
|
|
90
88
|
|
|
91
89
|
try {
|
|
92
|
-
|
|
93
|
-
|
|
90
|
+
gitExec('git add -A');
|
|
91
|
+
const status = gitOutput('git status --porcelain');
|
|
92
|
+
if (status) {
|
|
93
|
+
gitExec('git commit -m "update tag description"');
|
|
94
94
|
|
|
95
|
-
|
|
96
|
-
|
|
95
|
+
// Ensure Git is synchronized before pushing
|
|
96
|
+
await ensureGitSync();
|
|
97
97
|
|
|
98
|
-
|
|
98
|
+
gitExec('git push');
|
|
99
|
+
}
|
|
99
100
|
} catch (error: unknown) {
|
|
100
101
|
console.error('Error:', error instanceof Error ? error.message : String(error));
|
|
101
102
|
}
|
|
102
103
|
try {
|
|
103
|
-
|
|
104
|
+
gitExec(`git tag -a ${tag} ${tagMessage}`);
|
|
104
105
|
} catch {
|
|
105
|
-
|
|
106
|
-
|
|
106
|
+
gitExec(`git tag -d ${tag}`);
|
|
107
|
+
gitExec(`git push origin :refs/tags/${tag}`);
|
|
107
108
|
console.log('Fixed');
|
|
108
|
-
|
|
109
|
+
gitExec(`git tag -a ${tag} ${tagMessage}`);
|
|
109
110
|
}
|
|
110
111
|
// Ensure Git is synchronized before pushing tag
|
|
111
112
|
await ensureGitSync();
|
|
112
113
|
|
|
113
|
-
|
|
114
|
+
gitExec(`git push origin ${tag}`);
|
|
114
115
|
console.log(`Release ${tag} pushed to repo.`);
|
|
115
116
|
console.log(dedent`
|
|
116
117
|
with message:
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { access, mkdir, copyFile, rm, writeFile, rename } 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
|
-
|
|
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, {
|
|
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, cwd?: 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
|
-
|
|
166
|
+
gitExec('git fetch origin');
|
|
146
167
|
|
|
147
168
|
// Check if branch is behind remote
|
|
148
|
-
const status =
|
|
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
|
-
|
|
173
|
+
gitExec('git pull');
|
|
153
174
|
console.log('✅ Successfully pulled remote changes');
|
|
154
175
|
} else {
|
|
155
176
|
console.log('✅ Repository is synchronized with remote');
|
|
@@ -170,7 +191,7 @@ export async function renameMainCss(outdir: string): Promise<void> {
|
|
|
170
191
|
const mainCssPath = path.join(outdir, 'main.css');
|
|
171
192
|
const stylesCssPath = path.join(outdir, 'styles.css');
|
|
172
193
|
try {
|
|
173
|
-
if (
|
|
194
|
+
if (await isValidPath(mainCssPath)) {
|
|
174
195
|
await rename(mainCssPath, stylesCssPath);
|
|
175
196
|
}
|
|
176
197
|
} catch (error: unknown) {
|
|
@@ -186,22 +207,17 @@ export function isInPluginsFolder(currentPath: string): boolean {
|
|
|
186
207
|
}
|
|
187
208
|
|
|
188
209
|
/** Validates that a path points to an Obsidian vault with a plugins directory */
|
|
189
|
-
export function validateVaultPath(vaultPath: string): boolean {
|
|
210
|
+
export async function validateVaultPath(vaultPath: string): Promise<boolean> {
|
|
190
211
|
// Normalize path to handle both forward and backward slashes
|
|
191
212
|
const normalizedPath = path.normalize(vaultPath);
|
|
192
213
|
return (
|
|
193
|
-
|
|
194
|
-
|
|
214
|
+
(await isValidPath(path.join(normalizedPath, '.obsidian'))) &&
|
|
215
|
+
(await isValidPath(path.join(normalizedPath, '.obsidian', 'plugins')))
|
|
195
216
|
);
|
|
196
217
|
}
|
|
197
218
|
|
|
198
219
|
/** Resolves the full plugin install path from a vault path */
|
|
199
220
|
export function getVaultPath(vaultPath: string, pluginId: string): string {
|
|
200
|
-
if (!validateVaultPath(vaultPath)) {
|
|
201
|
-
console.error(`❌ Invalid vault path: ${vaultPath}`);
|
|
202
|
-
console.error(` The path must contain a .obsidian/plugins directory`);
|
|
203
|
-
process.exit(1);
|
|
204
|
-
}
|
|
205
221
|
const pluginsPath = path.join('.obsidian', 'plugins');
|
|
206
222
|
return vaultPath.includes(pluginsPath)
|
|
207
223
|
? path.join(vaultPath, pluginId)
|
|
@@ -216,7 +232,7 @@ export async function updateEnvFile(
|
|
|
216
232
|
): Promise<void> {
|
|
217
233
|
let envContent = '';
|
|
218
234
|
try {
|
|
219
|
-
envContent =
|
|
235
|
+
envContent = await readFile(envPath, 'utf8');
|
|
220
236
|
} catch {
|
|
221
237
|
/* file doesn't exist yet */
|
|
222
238
|
}
|
package/docs/audit.md
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
# Audit — obsidian-plugin-config
|
|
2
|
-
|
|
3
|
-
Audit mis à jour du repo d'injection de configuration pour plugins Obsidian.
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## 1. Bugs & problèmes fonctionnels restants ou nouveaux
|
|
8
|
-
|
|
9
|
-
### 🔴 BUG — Erreurs de compilation TypeScript dans les templates & conflits d'IDE
|
|
10
|
-
|
|
11
|
-
Le fait d'avoir un fichier `package.json` et `tsconfig.json` directement sous `templates/` perturbe les IDE et cause des erreurs de types car `node_modules` n'y est plus présent.
|
|
12
|
-
De plus, la racine du projet (`tsconfig.json` de la racine) inclut `templates/**/*.ts` mais son `package.json` ne contient pas les dépendances de développement utilisées par les templates (`dotenv`, `builtin-modules`, `esbuild`, `obsidian`, `obsidian-typings`, `tslib`). Cela produit des erreurs `Cannot find module 'dotenv'` lors de l'exécution de `tsc` à la racine.
|
|
13
|
-
|
|
14
|
-
**Actions recommandées :**
|
|
15
|
-
1. Renommer `templates/package.json` en `templates/package.json.template`.
|
|
16
|
-
2. Renommer `templates/tsconfig.json` en `templates/tsconfig.json.template`.
|
|
17
|
-
3. Installer les dépendances requises par les templates en tant que `devDependencies` dans le `package.json` racine pour que le compilateur TS de la racine résolve correctement les types sans nécessiter de dossier `node_modules` dans `templates/`.
|
|
18
|
-
4. Mettre à jour le code d'injection dans [inject-core.ts](file:///c:/Users/dd200/Documents/Mes_projets/Mes%20repo%20obsidian%20new/obsidian-plugin-config/scripts/inject-core.ts) pour copier à partir des fichiers `.template` et les restaurer sans suffixe dans la cible.
|
|
19
|
-
5. Mettre à jour la documentation et les scripts d'aide pour mentionner les nouveaux noms de templates.
|
|
20
|
-
|
|
21
|
-
---
|
|
22
|
-
|
|
23
|
-
## 2. Incohérences & améliorations de code restantes
|
|
24
|
-
|
|
25
|
-
### 🟡 INCOHÉRENCE — Flags CLI inversés dans la doc
|
|
26
|
-
|
|
27
|
-
[INTERACTIVE_INJECTION.md:42-53](file:///c:/Users/dd200/Documents/Mes_projets/Mes%20repo%20obsidian%20new/obsidian-plugin-config/docs/INTERACTIVE_INJECTION.md#L42-L53)
|
|
28
|
-
|
|
29
|
-
Le tableau indique `--no, -n` pour la CLI globale comme « auto-confirme tous les remplacements ». C'est correct dans le code (`bin/obsidian-inject.js` L100 : `--no` / `-n`). Mais le nommage est contre-intuitif : `--no` signifie « pas de confirmation » (= auto-confirme tout), ce qui peut prêter à confusion car `--no` semble dire « non ».
|
|
30
|
-
|
|
31
|
-
---
|
|
32
|
-
|
|
33
|
-
### 🟡 Améliorations possibles (non-bloquantes)
|
|
34
|
-
|
|
35
|
-
| # | Suggestion | Impact |
|
|
36
|
-
|---|-----------|--------|
|
|
37
|
-
| 1 | Unifier les flags `--yes`/`--no` entre CLI globale et scripts locaux | UX |
|
|
38
|
-
| 2 | Remplacer le mix fs sync/async dans `inject-core.ts` par une approche uniforme | Propreté |
|
|
39
|
-
| 3 | Nettoyer les numéros de lignes obsolètes dans `docs/SCSS-FLOW.md` | Doc maintenance |
|