obsidian-plugin-config 1.4.6 → 1.4.8
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/.editorconfig +13 -13
- package/.injection-info.json +5 -5
- package/.vscode/settings.json +11 -11
- package/.vscode/tasks.json +118 -118
- package/bin/obsidian-inject.js +1 -1
- package/eslint.config.mts +64 -64
- package/package.json +1 -1
- package/scripts/esbuild.config.ts +268 -268
- package/scripts/help.ts +152 -152
- package/scripts/utils.ts +151 -151
- package/templates/.vscode/tasks.json +8 -0
- package/templates/package.json +1 -1
- package/tsconfig.json +30 -30
- package/versions.json +3 -1
package/scripts/help.ts
CHANGED
|
@@ -1,152 +1,152 @@
|
|
|
1
|
-
#!/usr/bin/env tsx
|
|
2
|
-
|
|
3
|
-
console.log(`
|
|
4
|
-
🎯 Obsidian Plugin Config - Quick Help
|
|
5
|
-
Injection system for autonomous Obsidian plugins
|
|
6
|
-
|
|
7
|
-
═══════════════════════════════════════════════════════════════════
|
|
8
|
-
|
|
9
|
-
📋 PLUGIN CONFIG COMMANDS
|
|
10
|
-
|
|
11
|
-
INSTALLATION & SETUP:
|
|
12
|
-
yarn i, install # Install dependencies
|
|
13
|
-
yarn update-exports, ue # Update package.json exports
|
|
14
|
-
|
|
15
|
-
GIT & VERSION MANAGEMENT:
|
|
16
|
-
yarn acp # Add, commit, push
|
|
17
|
-
yarn bacp # Build + add, commit, push
|
|
18
|
-
yarn v, update-version # Update version (package.json + versions.json)
|
|
19
|
-
|
|
20
|
-
BUILD & TESTING:
|
|
21
|
-
yarn build # TypeScript check (no build needed)
|
|
22
|
-
yarn dev # Start development mode (watch)
|
|
23
|
-
yarn real # Build for production to real vault
|
|
24
|
-
yarn lint, lint:fix # ESLint verification/correction
|
|
25
|
-
|
|
26
|
-
INJECTION (Development phase):
|
|
27
|
-
yarn inject-prompt <path> # Interactive injection (recommended)
|
|
28
|
-
yarn inject-path <path> --yes # Direct injection with auto-confirm
|
|
29
|
-
yarn inject <path> --sass # Injection with SASS support
|
|
30
|
-
yarn check-plugin <path> # Verification only (dry-run)
|
|
31
|
-
|
|
32
|
-
NPM PUBLISHING (all-in-one - no acp needed before):
|
|
33
|
-
yarn npm-publish # Full workflow (7 steps):
|
|
34
|
-
# 0. NPM auth check
|
|
35
|
-
# 1. version bump
|
|
36
|
-
# 2. update exports
|
|
37
|
-
# 3. generate bin
|
|
38
|
-
# 4. verify package
|
|
39
|
-
# 5. commit + push
|
|
40
|
-
# 6. publish to NPM
|
|
41
|
-
# 7. offer global CLI update
|
|
42
|
-
yarn build-npm # Alias for npm-publish
|
|
43
|
-
|
|
44
|
-
UPGRADE:
|
|
45
|
-
yarn upgrade-all # yarn upgrade + sync template deps
|
|
46
|
-
|
|
47
|
-
HELP:
|
|
48
|
-
yarn help, h # This help
|
|
49
|
-
|
|
50
|
-
═══════════════════════════════════════════════════════════════════
|
|
51
|
-
|
|
52
|
-
📦 EXPORTS SYSTEM
|
|
53
|
-
|
|
54
|
-
The package exposes a single entry point:
|
|
55
|
-
obsidian-plugin-config # Main entry — re-exports all modules
|
|
56
|
-
|
|
57
|
-
Src modules (auto-exported via src/index.ts):
|
|
58
|
-
modals, tools, utils
|
|
59
|
-
|
|
60
|
-
After adding a new module to src/, run:
|
|
61
|
-
yarn update-exports # Regenerates src/index.ts
|
|
62
|
-
|
|
63
|
-
═══════════════════════════════════════════════════════════════════
|
|
64
|
-
|
|
65
|
-
🏗️ ARCHITECTURE
|
|
66
|
-
|
|
67
|
-
TWO DISTINCT ROLES:
|
|
68
|
-
Root (src/, scripts/) # Local plugin + NPM snippet exports
|
|
69
|
-
templates/ # Source of truth for injection
|
|
70
|
-
|
|
71
|
-
templates/ → what gets injected:
|
|
72
|
-
templates/package.json # Base deps/scripts for target plugins
|
|
73
|
-
templates/package-sass.json # Extra deps when --sass is used
|
|
74
|
-
templates/tsconfig.json # TypeScript config for target plugins
|
|
75
|
-
templates/scripts/* # Scripts copied to <target>/scripts/
|
|
76
|
-
templates/eslint.config.mts # ESLint config for target plugins
|
|
77
|
-
|
|
78
|
-
inject-core.ts — shared injection logic:
|
|
79
|
-
updatePackageJson() # Reads templates/package.json (not hardcoded)
|
|
80
|
-
injectScripts() # Copies templates/ files to target
|
|
81
|
-
performInjection() # Main orchestration function
|
|
82
|
-
|
|
83
|
-
inject-path.ts — CLI entry point:
|
|
84
|
-
Parses --yes, --dry-run, --sass flags
|
|
85
|
-
|
|
86
|
-
inject-prompt.ts — interactive entry point:
|
|
87
|
-
Prompts for target path interactively
|
|
88
|
-
|
|
89
|
-
═══════════════════════════════════════════════════════════════════
|
|
90
|
-
|
|
91
|
-
🔧 DEVELOPMENT WORKFLOWS
|
|
92
|
-
|
|
93
|
-
Plugin Config Development:
|
|
94
|
-
1. yarn i # Install dependencies
|
|
95
|
-
2. yarn update-exports # Update exports
|
|
96
|
-
3. yarn dev # Test as plugin (optional)
|
|
97
|
-
4. yarn lint:fix # Fix code issues
|
|
98
|
-
5. yarn v # Update version + commit + push
|
|
99
|
-
6. yarn npm-publish # Publish to NPM
|
|
100
|
-
|
|
101
|
-
Injection Usage:
|
|
102
|
-
Usage (from plugin directory or by path):
|
|
103
|
-
obsidian-inject # Inject in current dir
|
|
104
|
-
obsidian-inject ../my-plugin # Inject by path
|
|
105
|
-
obsidian-inject ../my-plugin --sass # With SASS
|
|
106
|
-
|
|
107
|
-
Or with local yarn commands:
|
|
108
|
-
yarn inject-prompt # Interactive
|
|
109
|
-
yarn inject-path ../my-plugin # Direct injection
|
|
110
|
-
yarn check-plugin ../my-plugin # Dry-run only
|
|
111
|
-
|
|
112
|
-
SASS Support (--sass flag):
|
|
113
|
-
What gets added to the target plugin:
|
|
114
|
-
✅ esbuild-sass-plugin dependency
|
|
115
|
-
✅ Automatic .scss file detection (src/styles.scss priority)
|
|
116
|
-
✅ CSS cleanup after compilation
|
|
117
|
-
|
|
118
|
-
The standard esbuild.config.ts handles SASS via dynamic import —
|
|
119
|
-
no separate template needed.
|
|
120
|
-
|
|
121
|
-
═══════════════════════════════════════════════════════════════════
|
|
122
|
-
|
|
123
|
-
🚀 COMPLETE WORKFLOWS
|
|
124
|
-
|
|
125
|
-
STANDARD DEVELOPMENT WORKFLOW:
|
|
126
|
-
1. yarn i # Install dependencies
|
|
127
|
-
2. Make changes to src/ or templates/
|
|
128
|
-
3. yarn lint:fix # Fix any linting issues
|
|
129
|
-
4. yarn npm-publish # Does EVERYTHING:
|
|
130
|
-
# → Ask for version bump type
|
|
131
|
-
# → Update exports
|
|
132
|
-
# → Generate bin file
|
|
133
|
-
# → Verify package
|
|
134
|
-
# → Commit + push to GitHub
|
|
135
|
-
# → Publish to NPM
|
|
136
|
-
|
|
137
|
-
Note: yarn acp is only needed for intermediate commits
|
|
138
|
-
(not required before npm-publish).
|
|
139
|
-
|
|
140
|
-
AFTER NPM PUBLISH - Update global CLI:
|
|
141
|
-
npm install -g obsidian-plugin-config@latest --force
|
|
142
|
-
obsidian-inject # Test in current plugin dir
|
|
143
|
-
obsidian-inject ../test-plugin --sass
|
|
144
|
-
|
|
145
|
-
TESTING AS PLUGIN (Optional):
|
|
146
|
-
1. Configure .env with TEST_VAULT and REAL_VAULT paths
|
|
147
|
-
2. yarn dev # Watch mode for development
|
|
148
|
-
3. yarn real # Install to real vault
|
|
149
|
-
|
|
150
|
-
═══════════════════════════════════════════════════════════════════
|
|
151
|
-
|
|
152
|
-
`);
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
|
|
3
|
+
console.log(`
|
|
4
|
+
🎯 Obsidian Plugin Config - Quick Help
|
|
5
|
+
Injection system for autonomous Obsidian plugins
|
|
6
|
+
|
|
7
|
+
═══════════════════════════════════════════════════════════════════
|
|
8
|
+
|
|
9
|
+
📋 PLUGIN CONFIG COMMANDS
|
|
10
|
+
|
|
11
|
+
INSTALLATION & SETUP:
|
|
12
|
+
yarn i, install # Install dependencies
|
|
13
|
+
yarn update-exports, ue # Update package.json exports
|
|
14
|
+
|
|
15
|
+
GIT & VERSION MANAGEMENT:
|
|
16
|
+
yarn acp # Add, commit, push
|
|
17
|
+
yarn bacp # Build + add, commit, push
|
|
18
|
+
yarn v, update-version # Update version (package.json + versions.json)
|
|
19
|
+
|
|
20
|
+
BUILD & TESTING:
|
|
21
|
+
yarn build # TypeScript check (no build needed)
|
|
22
|
+
yarn dev # Start development mode (watch)
|
|
23
|
+
yarn real # Build for production to real vault
|
|
24
|
+
yarn lint, lint:fix # ESLint verification/correction
|
|
25
|
+
|
|
26
|
+
INJECTION (Development phase):
|
|
27
|
+
yarn inject-prompt <path> # Interactive injection (recommended)
|
|
28
|
+
yarn inject-path <path> --yes # Direct injection with auto-confirm
|
|
29
|
+
yarn inject <path> --sass # Injection with SASS support
|
|
30
|
+
yarn check-plugin <path> # Verification only (dry-run)
|
|
31
|
+
|
|
32
|
+
NPM PUBLISHING (all-in-one - no acp needed before):
|
|
33
|
+
yarn npm-publish # Full workflow (7 steps):
|
|
34
|
+
# 0. NPM auth check
|
|
35
|
+
# 1. version bump
|
|
36
|
+
# 2. update exports
|
|
37
|
+
# 3. generate bin
|
|
38
|
+
# 4. verify package
|
|
39
|
+
# 5. commit + push
|
|
40
|
+
# 6. publish to NPM
|
|
41
|
+
# 7. offer global CLI update
|
|
42
|
+
yarn build-npm # Alias for npm-publish
|
|
43
|
+
|
|
44
|
+
UPGRADE:
|
|
45
|
+
yarn upgrade-all # yarn upgrade + sync template deps
|
|
46
|
+
|
|
47
|
+
HELP:
|
|
48
|
+
yarn help, h # This help
|
|
49
|
+
|
|
50
|
+
═══════════════════════════════════════════════════════════════════
|
|
51
|
+
|
|
52
|
+
📦 EXPORTS SYSTEM
|
|
53
|
+
|
|
54
|
+
The package exposes a single entry point:
|
|
55
|
+
obsidian-plugin-config # Main entry — re-exports all modules
|
|
56
|
+
|
|
57
|
+
Src modules (auto-exported via src/index.ts):
|
|
58
|
+
modals, tools, utils
|
|
59
|
+
|
|
60
|
+
After adding a new module to src/, run:
|
|
61
|
+
yarn update-exports # Regenerates src/index.ts
|
|
62
|
+
|
|
63
|
+
═══════════════════════════════════════════════════════════════════
|
|
64
|
+
|
|
65
|
+
🏗️ ARCHITECTURE
|
|
66
|
+
|
|
67
|
+
TWO DISTINCT ROLES:
|
|
68
|
+
Root (src/, scripts/) # Local plugin + NPM snippet exports
|
|
69
|
+
templates/ # Source of truth for injection
|
|
70
|
+
|
|
71
|
+
templates/ → what gets injected:
|
|
72
|
+
templates/package.json # Base deps/scripts for target plugins
|
|
73
|
+
templates/package-sass.json # Extra deps when --sass is used
|
|
74
|
+
templates/tsconfig.json # TypeScript config for target plugins
|
|
75
|
+
templates/scripts/* # Scripts copied to <target>/scripts/
|
|
76
|
+
templates/eslint.config.mts # ESLint config for target plugins
|
|
77
|
+
|
|
78
|
+
inject-core.ts — shared injection logic:
|
|
79
|
+
updatePackageJson() # Reads templates/package.json (not hardcoded)
|
|
80
|
+
injectScripts() # Copies templates/ files to target
|
|
81
|
+
performInjection() # Main orchestration function
|
|
82
|
+
|
|
83
|
+
inject-path.ts — CLI entry point:
|
|
84
|
+
Parses --yes, --dry-run, --sass flags
|
|
85
|
+
|
|
86
|
+
inject-prompt.ts — interactive entry point:
|
|
87
|
+
Prompts for target path interactively
|
|
88
|
+
|
|
89
|
+
═══════════════════════════════════════════════════════════════════
|
|
90
|
+
|
|
91
|
+
🔧 DEVELOPMENT WORKFLOWS
|
|
92
|
+
|
|
93
|
+
Plugin Config Development:
|
|
94
|
+
1. yarn i # Install dependencies
|
|
95
|
+
2. yarn update-exports # Update exports
|
|
96
|
+
3. yarn dev # Test as plugin (optional)
|
|
97
|
+
4. yarn lint:fix # Fix code issues
|
|
98
|
+
5. yarn v # Update version + commit + push
|
|
99
|
+
6. yarn npm-publish # Publish to NPM
|
|
100
|
+
|
|
101
|
+
Injection Usage:
|
|
102
|
+
Usage (from plugin directory or by path):
|
|
103
|
+
obsidian-inject # Inject in current dir
|
|
104
|
+
obsidian-inject ../my-plugin # Inject by path
|
|
105
|
+
obsidian-inject ../my-plugin --sass # With SASS
|
|
106
|
+
|
|
107
|
+
Or with local yarn commands:
|
|
108
|
+
yarn inject-prompt # Interactive
|
|
109
|
+
yarn inject-path ../my-plugin # Direct injection
|
|
110
|
+
yarn check-plugin ../my-plugin # Dry-run only
|
|
111
|
+
|
|
112
|
+
SASS Support (--sass flag):
|
|
113
|
+
What gets added to the target plugin:
|
|
114
|
+
✅ esbuild-sass-plugin dependency
|
|
115
|
+
✅ Automatic .scss file detection (src/styles.scss priority)
|
|
116
|
+
✅ CSS cleanup after compilation
|
|
117
|
+
|
|
118
|
+
The standard esbuild.config.ts handles SASS via dynamic import —
|
|
119
|
+
no separate template needed.
|
|
120
|
+
|
|
121
|
+
═══════════════════════════════════════════════════════════════════
|
|
122
|
+
|
|
123
|
+
🚀 COMPLETE WORKFLOWS
|
|
124
|
+
|
|
125
|
+
STANDARD DEVELOPMENT WORKFLOW:
|
|
126
|
+
1. yarn i # Install dependencies
|
|
127
|
+
2. Make changes to src/ or templates/
|
|
128
|
+
3. yarn lint:fix # Fix any linting issues
|
|
129
|
+
4. yarn npm-publish # Does EVERYTHING:
|
|
130
|
+
# → Ask for version bump type
|
|
131
|
+
# → Update exports
|
|
132
|
+
# → Generate bin file
|
|
133
|
+
# → Verify package
|
|
134
|
+
# → Commit + push to GitHub
|
|
135
|
+
# → Publish to NPM
|
|
136
|
+
|
|
137
|
+
Note: yarn acp is only needed for intermediate commits
|
|
138
|
+
(not required before npm-publish).
|
|
139
|
+
|
|
140
|
+
AFTER NPM PUBLISH - Update global CLI:
|
|
141
|
+
npm install -g obsidian-plugin-config@latest --force
|
|
142
|
+
obsidian-inject # Test in current plugin dir
|
|
143
|
+
obsidian-inject ../test-plugin --sass
|
|
144
|
+
|
|
145
|
+
TESTING AS PLUGIN (Optional):
|
|
146
|
+
1. Configure .env with TEST_VAULT and REAL_VAULT paths
|
|
147
|
+
2. yarn dev # Watch mode for development
|
|
148
|
+
3. yarn real # Install to real vault
|
|
149
|
+
|
|
150
|
+
═══════════════════════════════════════════════════════════════════
|
|
151
|
+
|
|
152
|
+
`);
|
package/scripts/utils.ts
CHANGED
|
@@ -1,151 +1,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
|
-
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
|
+
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
|
+
}
|
|
@@ -41,6 +41,14 @@
|
|
|
41
41
|
"presentation": { "reveal": "always", "panel": "shared" },
|
|
42
42
|
"problemMatcher": ["$tsc"]
|
|
43
43
|
},
|
|
44
|
+
{
|
|
45
|
+
"label": "Obsidian Inject",
|
|
46
|
+
"type": "shell",
|
|
47
|
+
"command": "obsidian-inject",
|
|
48
|
+
"group": "build",
|
|
49
|
+
"presentation": { "reveal": "always", "panel": "shared" },
|
|
50
|
+
"problemMatcher": []
|
|
51
|
+
},
|
|
44
52
|
{
|
|
45
53
|
"label": "Cleanup: Lint + Prettier + Build",
|
|
46
54
|
"dependsOrder": "sequence",
|