obsidian-plugin-config 1.0.2

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.
Files changed (55) hide show
  1. package/.vscode/settings.json +4 -0
  2. package/README.md +45 -0
  3. package/bin/obsidian-inject.js +98 -0
  4. package/obsidian-plugin-config-1.0.2.tgz +0 -0
  5. package/package.json +88 -0
  6. package/scripts/acp.ts +71 -0
  7. package/scripts/build-npm.ts +137 -0
  8. package/scripts/esbuild.config.ts +311 -0
  9. package/scripts/help.ts +46 -0
  10. package/scripts/inject-path.ts +487 -0
  11. package/scripts/inject-prompt.ts +399 -0
  12. package/scripts/open-editor.mjs +18 -0
  13. package/scripts/release.ts +97 -0
  14. package/scripts/update-exports.js +91 -0
  15. package/scripts/update-version-config.ts +98 -0
  16. package/scripts/update-version.ts +102 -0
  17. package/scripts/utils.ts +117 -0
  18. package/src/index.ts +6 -0
  19. package/src/main_test.ts +106 -0
  20. package/src/modals/GenericConfirmModal.ts +67 -0
  21. package/src/modals/index.ts +3 -0
  22. package/src/test-centralized-utils.ts +23 -0
  23. package/src/tools/index.ts +9 -0
  24. package/src/utils/NoticeHelper.ts +102 -0
  25. package/src/utils/SettingsHelper.ts +180 -0
  26. package/src/utils/index.ts +3 -0
  27. package/templates/.vscode/settings.json +4 -0
  28. package/templates/eslint.config.ts +48 -0
  29. package/templates/help-plugin.ts +39 -0
  30. package/templates/package-versions.json +28 -0
  31. package/templates/tsconfig.json +37 -0
  32. package/test-plugin/manifest.json +10 -0
  33. package/test-plugin/package.json +38 -0
  34. package/test-plugin/scripts/acp.ts +71 -0
  35. package/test-plugin/scripts/esbuild.config.ts +165 -0
  36. package/test-plugin/scripts/help.ts +29 -0
  37. package/test-plugin/scripts/release.ts +97 -0
  38. package/test-plugin/scripts/update-version.ts +102 -0
  39. package/test-plugin/scripts/utils.ts +117 -0
  40. package/test-plugin/src/main.ts +11 -0
  41. package/test-plugin/yarn.lock +386 -0
  42. package/test-plugin-v2/main.js +5 -0
  43. package/test-plugin-v2/manifest.json +10 -0
  44. package/test-plugin-v2/package.json +40 -0
  45. package/test-plugin-v2/scripts/acp.ts +71 -0
  46. package/test-plugin-v2/scripts/esbuild.config.ts +165 -0
  47. package/test-plugin-v2/scripts/help.ts +29 -0
  48. package/test-plugin-v2/scripts/release.ts +97 -0
  49. package/test-plugin-v2/scripts/update-version.ts +102 -0
  50. package/test-plugin-v2/scripts/utils.ts +117 -0
  51. package/test-plugin-v2/src/main.ts +11 -0
  52. package/test-plugin-v2/tsconfig.json +31 -0
  53. package/test-plugin-v2/yarn.lock +1986 -0
  54. package/tsconfig.json +38 -0
  55. package/versions.json +5 -0
@@ -0,0 +1,487 @@
1
+ #!/usr/bin/env tsx
2
+
3
+ import fs from "fs";
4
+ import path from "path";
5
+ import { execSync } from "child_process";
6
+ import dotenv from "dotenv";
7
+ import {
8
+ askQuestion,
9
+ askConfirmation,
10
+ createReadlineInterface,
11
+ isValidPath
12
+ } from "./utils.js";
13
+
14
+ // Load environment variables from .env file
15
+ dotenv.config();
16
+
17
+ const rl = createReadlineInterface();
18
+
19
+ interface InjectionPlan {
20
+ targetPath: string;
21
+ isObsidianPlugin: boolean;
22
+ hasPackageJson: boolean;
23
+ hasManifest: boolean;
24
+ hasScriptsFolder: boolean;
25
+ currentDependencies: string[];
26
+ }
27
+
28
+ /**
29
+ * Analyze the target plugin directory
30
+ */
31
+ async function analyzePlugin(pluginPath: string): Promise<InjectionPlan> {
32
+ const packageJsonPath = path.join(pluginPath, "package.json");
33
+ const manifestPath = path.join(pluginPath, "manifest.json");
34
+ const scriptsPath = path.join(pluginPath, "scripts");
35
+
36
+ const plan: InjectionPlan = {
37
+ targetPath: pluginPath,
38
+ isObsidianPlugin: false,
39
+ hasPackageJson: await isValidPath(packageJsonPath),
40
+ hasManifest: await isValidPath(manifestPath),
41
+ hasScriptsFolder: await isValidPath(scriptsPath),
42
+ currentDependencies: []
43
+ };
44
+
45
+ // Check if it's an Obsidian plugin
46
+ if (plan.hasManifest) {
47
+ try {
48
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
49
+ plan.isObsidianPlugin = !!(manifest.id && manifest.name && manifest.version);
50
+ } catch (error) {
51
+ console.warn("Warning: Could not parse manifest.json");
52
+ }
53
+ }
54
+
55
+ // Get current dependencies
56
+ if (plan.hasPackageJson) {
57
+ try {
58
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
59
+ plan.currentDependencies = [
60
+ ...Object.keys(packageJson.dependencies || {}),
61
+ ...Object.keys(packageJson.devDependencies || {})
62
+ ];
63
+ } catch (error) {
64
+ console.warn("Warning: Could not parse package.json");
65
+ }
66
+ }
67
+
68
+ return plan;
69
+ }
70
+
71
+ /**
72
+ * Display injection plan and ask for confirmation
73
+ */
74
+ async function showInjectionPlan(plan: InjectionPlan, autoConfirm: boolean = false): Promise<boolean> {
75
+ console.log(`\n🎯 Injection Plan for: ${plan.targetPath}`);
76
+ console.log(`📁 Target: ${path.basename(plan.targetPath)}`);
77
+ console.log(`📦 Package.json: ${plan.hasPackageJson ? '✅' : '❌'}`);
78
+ console.log(`📋 Manifest.json: ${plan.hasManifest ? '✅' : '❌'}`);
79
+ console.log(`📂 Scripts folder: ${plan.hasScriptsFolder ? '✅ (will be updated)' : '❌ (will be created)'}`);
80
+ console.log(`🔌 Obsidian plugin: ${plan.isObsidianPlugin ? '✅' : '❌'}`);
81
+
82
+ if (!plan.isObsidianPlugin) {
83
+ console.log(`\n⚠️ Warning: This doesn't appear to be a valid Obsidian plugin`);
84
+ console.log(` Missing manifest.json or invalid structure`);
85
+ }
86
+
87
+ console.log(`\n📋 Will inject:`);
88
+ console.log(` ✅ Local scripts (utils.ts, esbuild.config.ts, acp.ts, etc.)`);
89
+ console.log(` ✅ Updated package.json scripts`);
90
+ console.log(` ✅ Required dependencies`);
91
+ console.log(` 🔍 Analyze centralized imports (manual commenting may be needed)`);
92
+
93
+ if (autoConfirm) {
94
+ console.log(`\n✅ Auto-confirming injection...`);
95
+ return true;
96
+ }
97
+
98
+ return await askConfirmation(`\nProceed with injection?`, rl);
99
+ }
100
+
101
+ /**
102
+ * Find plugin-config root directory
103
+ */
104
+ function findPluginConfigRoot(): string {
105
+ const envPath = process.env.PLUGIN_CONFIG_PATH?.trim();
106
+
107
+ // Option 1: local - check parent directory
108
+ if (envPath === "local") {
109
+ const parentPath = path.resolve(process.cwd(), "../obsidian-plugin-config");
110
+ if (fs.existsSync(parentPath)) {
111
+ return parentPath;
112
+ }
113
+ throw new Error("obsidian-plugin-config not found in parent directory");
114
+ }
115
+
116
+ // Option 2: prompt - skip auto-detection
117
+ if (envPath === "prompt") {
118
+ throw new Error("PROMPT_REQUIRED");
119
+ }
120
+
121
+ // Option 3: specific path
122
+ if (envPath && fs.existsSync(envPath)) {
123
+ return envPath;
124
+ }
125
+
126
+ // Option 4: auto-detect parent, fallback to current
127
+ const parentPath = path.resolve(process.cwd(), "../obsidian-plugin-config");
128
+ if (fs.existsSync(parentPath)) {
129
+ return parentPath;
130
+ }
131
+
132
+ // Fallback to current directory (original behavior)
133
+ return process.cwd();
134
+ }
135
+
136
+ /**
137
+ * Copy file content from local plugin-config directory
138
+ */
139
+ function copyFromLocal(filePath: string): string {
140
+ const configRoot = findPluginConfigRoot();
141
+ const sourcePath = path.join(configRoot, filePath);
142
+
143
+ try {
144
+ return fs.readFileSync(sourcePath, 'utf8');
145
+ } catch (error) {
146
+ throw new Error(`Failed to copy ${filePath}: ${error}`);
147
+ }
148
+ }
149
+
150
+ /**
151
+ * Inject scripts from local files
152
+ */
153
+ async function injectScripts(targetPath: string): Promise<void> {
154
+ const scriptsPath = path.join(targetPath, "scripts");
155
+
156
+ // Create scripts directory if it doesn't exist
157
+ if (!await isValidPath(scriptsPath)) {
158
+ fs.mkdirSync(scriptsPath, { recursive: true });
159
+ console.log(`📁 Created scripts directory`);
160
+ }
161
+
162
+ const scriptFiles = [
163
+ "scripts/utils.ts",
164
+ "scripts/esbuild.config.ts",
165
+ "scripts/acp.ts",
166
+ "scripts/update-version.ts",
167
+ "scripts/release.ts",
168
+ "scripts/help.ts"
169
+ ];
170
+
171
+ const configFiles = [
172
+ "templates/tsconfig.json"
173
+ ];
174
+
175
+ console.log(`\n📥 Copying scripts from local files...`);
176
+
177
+ for (const scriptFile of scriptFiles) {
178
+ try {
179
+ const content = copyFromLocal(scriptFile);
180
+ const fileName = path.basename(scriptFile);
181
+ const targetFile = path.join(scriptsPath, fileName);
182
+
183
+ fs.writeFileSync(targetFile, content, 'utf8');
184
+ console.log(` ✅ ${fileName}`);
185
+ } catch (error) {
186
+ console.error(` ❌ Failed to inject ${scriptFile}: ${error}`);
187
+ }
188
+ }
189
+
190
+ console.log(`\n📥 Copying config files from local files...`);
191
+
192
+ for (const configFile of configFiles) {
193
+ try {
194
+ const content = copyFromLocal(configFile);
195
+ const fileName = path.basename(configFile);
196
+ const targetFile = path.join(targetPath, fileName);
197
+
198
+ // Force inject tsconfig.json to ensure correct template
199
+ if (fileName === 'tsconfig.json' && await isValidPath(targetFile)) {
200
+ console.log(` 🔄 ${fileName} exists, updating with template`);
201
+ }
202
+
203
+ fs.writeFileSync(targetFile, content, 'utf8');
204
+ console.log(` ✅ ${fileName}`);
205
+ } catch (error) {
206
+ console.error(` ❌ Failed to inject ${configFile}: ${error}`);
207
+ }
208
+ }
209
+ }
210
+
211
+ /**
212
+ * Update package.json with autonomous configuration
213
+ */
214
+ async function updatePackageJson(targetPath: string): Promise<void> {
215
+ const packageJsonPath = path.join(targetPath, "package.json");
216
+
217
+ if (!await isValidPath(packageJsonPath)) {
218
+ console.log(`❌ No package.json found, skipping package.json update`);
219
+ return;
220
+ }
221
+
222
+ try {
223
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
224
+
225
+ // Update scripts
226
+ packageJson.scripts = {
227
+ ...packageJson.scripts,
228
+ "start": "yarn install && yarn dev",
229
+ "dev": "tsx scripts/esbuild.config.ts",
230
+ "build": "tsc -noEmit -skipLibCheck && tsx scripts/esbuild.config.ts production",
231
+ "real": "tsx scripts/esbuild.config.ts production real",
232
+ "acp": "tsx scripts/acp.ts",
233
+ "bacp": "tsx scripts/acp.ts -b",
234
+ "update-version": "tsx scripts/update-version.ts",
235
+ "v": "tsx scripts/update-version.ts",
236
+ "release": "tsx scripts/release.ts",
237
+ "r": "tsx scripts/release.ts",
238
+ "help": "tsx scripts/help.ts",
239
+ "h": "tsx scripts/help.ts"
240
+ };
241
+
242
+ // Remove centralized dependency
243
+ if (packageJson.dependencies && packageJson.dependencies["obsidian-plugin-config"]) {
244
+ delete packageJson.dependencies["obsidian-plugin-config"];
245
+ console.log(` 🗑️ Removed obsidian-plugin-config dependency`);
246
+ }
247
+
248
+ // Add required dependencies
249
+ if (!packageJson.devDependencies) packageJson.devDependencies = {};
250
+
251
+ const requiredDeps = {
252
+ "@types/node": "^22.15.26",
253
+ "@types/semver": "^7.7.0",
254
+ "builtin-modules": "3.3.0",
255
+ "dedent": "^1.6.0",
256
+ "dotenv": "^16.4.5",
257
+ "esbuild": "latest",
258
+ "obsidian": "*",
259
+ "obsidian-typings": "^3.9.5",
260
+ "semver": "^7.7.2",
261
+ "tsx": "^4.19.4",
262
+ "typescript": "^5.8.2"
263
+ };
264
+
265
+ // Force update TypeScript to compatible version
266
+ if (packageJson.devDependencies.typescript && packageJson.devDependencies.typescript !== "^5.8.2") {
267
+ console.log(` 🔄 Updating TypeScript from ${packageJson.devDependencies.typescript} to ^5.8.2`);
268
+ }
269
+
270
+ let addedDeps = 0;
271
+ let updatedDeps = 0;
272
+ for (const [dep, version] of Object.entries(requiredDeps)) {
273
+ if (!packageJson.devDependencies[dep]) {
274
+ packageJson.devDependencies[dep] = version;
275
+ addedDeps++;
276
+ } else if (packageJson.devDependencies[dep] !== version) {
277
+ packageJson.devDependencies[dep] = version;
278
+ updatedDeps++;
279
+ }
280
+ }
281
+
282
+ // Ensure yarn protection
283
+ if (!packageJson.engines) packageJson.engines = {};
284
+ packageJson.engines.npm = "please-use-yarn";
285
+ packageJson.engines.yarn = ">=1.22.0";
286
+
287
+ // Write updated package.json
288
+ fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2), 'utf8');
289
+ console.log(` ✅ Updated package.json (${addedDeps} new, ${updatedDeps} updated dependencies)`);
290
+
291
+ // Debug: verify package.json was written correctly
292
+ console.log(` 🔍 Package name: ${packageJson.name}`);
293
+
294
+ } catch (error) {
295
+ console.error(` ❌ Failed to update package.json: ${error}`);
296
+ }
297
+ }
298
+
299
+ /**
300
+ * Analyze centralized imports in source files (without modifying)
301
+ */
302
+ async function analyzeCentralizedImports(targetPath: string): Promise<void> {
303
+ const srcPath = path.join(targetPath, "src");
304
+
305
+ if (!await isValidPath(srcPath)) {
306
+ console.log(` ℹ️ No src directory found`);
307
+ return;
308
+ }
309
+
310
+ console.log(`\n🔍 Analyzing centralized imports...`);
311
+
312
+ try {
313
+ // Find all TypeScript files recursively
314
+ const findTsFiles = (dir: string): string[] => {
315
+ const files: string[] = [];
316
+ const items = fs.readdirSync(dir);
317
+
318
+ for (const item of items) {
319
+ const fullPath = path.join(dir, item);
320
+ const stat = fs.statSync(fullPath);
321
+
322
+ if (stat.isDirectory()) {
323
+ files.push(...findTsFiles(fullPath));
324
+ } else if (item.endsWith('.ts') || item.endsWith('.tsx')) {
325
+ files.push(fullPath);
326
+ }
327
+ }
328
+
329
+ return files;
330
+ };
331
+
332
+ const tsFiles = findTsFiles(srcPath);
333
+ let filesWithImports = 0;
334
+
335
+ for (const filePath of tsFiles) {
336
+ try {
337
+ const content = fs.readFileSync(filePath, 'utf8');
338
+
339
+ // Check for imports from obsidian-plugin-config
340
+ const importRegex = /import\s+.*from\s+["']obsidian-plugin-config[^"']*["']/g;
341
+ if (importRegex.test(content)) {
342
+ filesWithImports++;
343
+ console.log(` ⚠️ ${path.relative(targetPath, filePath)} - contains centralized imports`);
344
+ }
345
+ } catch (error) {
346
+ console.warn(` ⚠️ Could not analyze ${path.relative(targetPath, filePath)}: ${error}`);
347
+ }
348
+ }
349
+
350
+ if (filesWithImports === 0) {
351
+ console.log(` ✅ No centralized imports found`);
352
+ } else {
353
+ console.log(` ⚠️ Found ${filesWithImports} files with centralized imports`);
354
+ console.log(` 💡 You may need to manually comment these imports for the plugin to work`);
355
+ }
356
+
357
+ } catch (error) {
358
+ console.error(` ❌ Failed to analyze imports: ${error}`);
359
+ }
360
+ }
361
+
362
+ /**
363
+ * Create required directories
364
+ */
365
+ async function createRequiredDirectories(targetPath: string): Promise<void> {
366
+ const directories = [
367
+ path.join(targetPath, ".github", "workflows")
368
+ ];
369
+
370
+ for (const dir of directories) {
371
+ if (!await isValidPath(dir)) {
372
+ fs.mkdirSync(dir, { recursive: true });
373
+ console.log(` 📁 Created ${path.relative(targetPath, dir)}`);
374
+ }
375
+ }
376
+ }
377
+
378
+ /**
379
+ * Run yarn install in target directory
380
+ */
381
+ async function runYarnInstall(targetPath: string): Promise<void> {
382
+ console.log(`\n📦 Installing dependencies...`);
383
+
384
+ try {
385
+ execSync('yarn install', {
386
+ cwd: targetPath,
387
+ stdio: 'inherit'
388
+ });
389
+ console.log(` ✅ Dependencies installed successfully`);
390
+ } catch (error) {
391
+ console.error(` ❌ Failed to install dependencies: ${error}`);
392
+ console.log(` 💡 You may need to run 'yarn install' manually in the target directory`);
393
+ }
394
+ }
395
+
396
+ /**
397
+ * Main injection function
398
+ */
399
+ export async function performInjection(targetPath: string): Promise<void> {
400
+ console.log(`\n🚀 Starting injection process...`);
401
+
402
+ try {
403
+ // Step 1: Inject scripts
404
+ await injectScripts(targetPath);
405
+
406
+ // Step 2: Update package.json
407
+ console.log(`\n📦 Updating package.json...`);
408
+ await updatePackageJson(targetPath);
409
+
410
+ // Step 3: Analyze centralized imports (without modifying)
411
+ await analyzeCentralizedImports(targetPath);
412
+
413
+ // Step 4: Create required directories
414
+ console.log(`\n📁 Creating required directories...`);
415
+ await createRequiredDirectories(targetPath);
416
+
417
+ // Step 5: Install dependencies
418
+ await runYarnInstall(targetPath);
419
+
420
+ console.log(`\n✅ Injection completed successfully!`);
421
+ console.log(`\n📋 Next steps:`);
422
+ console.log(` 1. cd ${targetPath}`);
423
+ console.log(` 2. yarn build # Test the build`);
424
+ console.log(` 3. yarn start # Test development mode`);
425
+ console.log(` 4. yarn acp # Commit changes (or yarn bacp for build+commit)`);
426
+
427
+ } catch (error) {
428
+ console.error(`\n❌ Injection failed: ${error}`);
429
+ throw error;
430
+ }
431
+ }
432
+
433
+ /**
434
+ * Main function
435
+ */
436
+ async function main(): Promise<void> {
437
+ try {
438
+ console.log(`🎯 Obsidian Plugin Config - Local Injection Tool`);
439
+ console.log(`📥 Inject autonomous configuration from local files\n`);
440
+
441
+ // Parse command line arguments
442
+ const args = process.argv.slice(2);
443
+ const autoConfirm = args.includes('--yes') || args.includes('-y');
444
+ const targetPath = args.find(arg => !arg.startsWith('-'));
445
+
446
+ if (!targetPath) {
447
+ console.error(`❌ Usage: yarn inject-path <plugin-directory> [--yes]`);
448
+ console.error(` Example: yarn inject-path ../my-obsidian-plugin`);
449
+ console.error(` Options: --yes, -y Auto-confirm injection`);
450
+ process.exit(1);
451
+ }
452
+
453
+ // Resolve and validate path
454
+ const resolvedPath = path.resolve(targetPath);
455
+
456
+ if (!await isValidPath(resolvedPath)) {
457
+ console.error(`❌ Directory not found: ${resolvedPath}`);
458
+ process.exit(1);
459
+ }
460
+
461
+ console.log(`📁 Target directory: ${resolvedPath}`);
462
+
463
+ // Analyze the plugin
464
+ console.log(`\n🔍 Analyzing plugin...`);
465
+ const plan = await analyzePlugin(resolvedPath);
466
+
467
+ // Show plan and ask for confirmation
468
+ const confirmed = await showInjectionPlan(plan, autoConfirm);
469
+
470
+ if (!confirmed) {
471
+ console.log(`❌ Injection cancelled by user`);
472
+ process.exit(0);
473
+ }
474
+
475
+ // Perform injection
476
+ await performInjection(resolvedPath);
477
+
478
+ } catch (error) {
479
+ console.error(`💥 Error: ${error instanceof Error ? error.message : String(error)}`);
480
+ process.exit(1);
481
+ } finally {
482
+ rl.close();
483
+ }
484
+ }
485
+
486
+ // Run the script
487
+ main().catch(console.error);