obsidian-plugin-config 1.6.9 ā 1.6.11
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 +13 -0
- package/bin/obsidian-inject.js +1 -1
- package/package.json +1 -1
- package/scripts/inject-core.ts +180 -10
package/README.md
CHANGED
|
@@ -139,6 +139,19 @@ yarn build # Production build
|
|
|
139
139
|
yarn real # Build to production vault
|
|
140
140
|
```
|
|
141
141
|
|
|
142
|
+
### VSCode Tasks (Ctrl+Shift+P ā "Run Task")
|
|
143
|
+
|
|
144
|
+
After injection, VSCode tasks are available for quick access:
|
|
145
|
+
|
|
146
|
+
- **Build** - Production build
|
|
147
|
+
- **Lint** / **Lint: Fix** - ESLint check/fix
|
|
148
|
+
- **Prettier: Check** / **Prettier: Fix** - Format check/fix
|
|
149
|
+
- **Obsidian Inject** - Re-inject configuration (with confirmation)
|
|
150
|
+
- **Obsidian Inject (no confirm)** - Re-inject without confirmation
|
|
151
|
+
- **Cleanup: Lint + Prettier + Build** - Full cleanup sequence
|
|
152
|
+
|
|
153
|
+
š” **Tip**: Use `Ctrl+Shift+B` (Windows/Linux) or `Cmd+Shift+B` (Mac) for the default Build task.
|
|
154
|
+
|
|
142
155
|
### Version & Release
|
|
143
156
|
|
|
144
157
|
```bash
|
package/bin/obsidian-inject.js
CHANGED
package/package.json
CHANGED
package/scripts/inject-core.ts
CHANGED
|
@@ -195,7 +195,10 @@ export async function showInjectionPlan(
|
|
|
195
195
|
/**
|
|
196
196
|
* Clean old script files
|
|
197
197
|
*/
|
|
198
|
-
export async function cleanOldScripts(
|
|
198
|
+
export async function cleanOldScripts(
|
|
199
|
+
scriptsPath: string,
|
|
200
|
+
approvedDests: Set<string>
|
|
201
|
+
): Promise<void> {
|
|
199
202
|
const scriptNames = [
|
|
200
203
|
'utils',
|
|
201
204
|
'esbuild.config',
|
|
@@ -210,10 +213,10 @@ export async function cleanOldScripts(scriptsPath: string): Promise<void> {
|
|
|
210
213
|
for (const ext of extensions) {
|
|
211
214
|
const scriptFile = path.join(scriptsPath, `${scriptName}${ext}`);
|
|
212
215
|
if (await isValidPath(scriptFile)) {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
`šļø Removed existing ${scriptName}${ext} (will be replaced)`
|
|
216
|
-
|
|
216
|
+
if (approvedDests.has(scriptFile)) {
|
|
217
|
+
fs.unlinkSync(scriptFile);
|
|
218
|
+
console.log(`šļø Removed existing ${scriptName}${ext} (will be replaced)`);
|
|
219
|
+
}
|
|
217
220
|
}
|
|
218
221
|
}
|
|
219
222
|
}
|
|
@@ -270,12 +273,166 @@ export async function cleanOldLintFiles(targetPath: string): Promise<void> {
|
|
|
270
273
|
}
|
|
271
274
|
}
|
|
272
275
|
|
|
276
|
+
interface FileEntry {
|
|
277
|
+
src: string; // path relative to configRoot
|
|
278
|
+
dest: string; // absolute path in target plugin
|
|
279
|
+
optionKey: keyof InjectionOptions | null; // null = always inject
|
|
280
|
+
mergeEnv?: boolean; // special .env merge logic
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Build the full list of files to inject, with source and destination paths
|
|
285
|
+
*/
|
|
286
|
+
function buildFileList(targetPath: string, options: InjectionOptions): FileEntry[] {
|
|
287
|
+
const scriptsPath = path.join(targetPath, 'scripts');
|
|
288
|
+
const entries: FileEntry[] = [];
|
|
289
|
+
|
|
290
|
+
// Scripts
|
|
291
|
+
const scriptFiles = [
|
|
292
|
+
'templates/scripts/utils.ts',
|
|
293
|
+
'templates/scripts/esbuild.config.ts',
|
|
294
|
+
'templates/scripts/acp.ts',
|
|
295
|
+
'templates/scripts/update-version.ts',
|
|
296
|
+
'templates/scripts/release.ts',
|
|
297
|
+
'templates/scripts/help.ts'
|
|
298
|
+
];
|
|
299
|
+
for (const src of scriptFiles) {
|
|
300
|
+
entries.push({
|
|
301
|
+
src,
|
|
302
|
+
dest: path.join(scriptsPath, path.basename(src)),
|
|
303
|
+
optionKey: 'scripts'
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Root config files
|
|
308
|
+
const configFileMap: Array<[string, string, keyof InjectionOptions | null, boolean?]> =
|
|
309
|
+
[
|
|
310
|
+
['templates/tsconfig.json', 'tsconfig.json', 'tsconfig'],
|
|
311
|
+
['templates/gitignore.template', '.gitignore', 'gitignore'],
|
|
312
|
+
['templates/eslint.config.mts', 'eslint.config.mts', 'eslint'],
|
|
313
|
+
['templates/.editorconfig', '.editorconfig', 'editorconfig'],
|
|
314
|
+
['templates/.prettierrc', '.prettierrc', 'prettier'],
|
|
315
|
+
['templates/npmrc.template', '.npmrc', null],
|
|
316
|
+
['templates/env.template', '.env', 'env', true]
|
|
317
|
+
];
|
|
318
|
+
for (const [src, destName, optionKey, mergeEnv] of configFileMap) {
|
|
319
|
+
entries.push({
|
|
320
|
+
src,
|
|
321
|
+
dest: path.join(targetPath, destName),
|
|
322
|
+
optionKey,
|
|
323
|
+
mergeEnv: !!mergeEnv
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// VSCode config files
|
|
328
|
+
const configVscodeMap: Array<[string, string]> = [
|
|
329
|
+
['templates/.vscode/settings.json', '.vscode/settings.json'],
|
|
330
|
+
['templates/.vscode/tasks.json', '.vscode/tasks.json']
|
|
331
|
+
];
|
|
332
|
+
for (const [src, destName] of configVscodeMap) {
|
|
333
|
+
entries.push({
|
|
334
|
+
src,
|
|
335
|
+
dest: path.join(targetPath, destName),
|
|
336
|
+
optionKey: 'vscode'
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// GitHub workflow files
|
|
341
|
+
const workflowFiles = [
|
|
342
|
+
'templates/.github/workflows/release.yml',
|
|
343
|
+
'templates/.github/workflows/release-body.md'
|
|
344
|
+
];
|
|
345
|
+
for (const src of workflowFiles) {
|
|
346
|
+
entries.push({
|
|
347
|
+
src,
|
|
348
|
+
dest: path.join(targetPath, src.replace('templates/', '')),
|
|
349
|
+
optionKey: 'github'
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
return entries;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Compare source templates with existing target files.
|
|
358
|
+
* Prompt user only when content differs and file already exists.
|
|
359
|
+
* Returns the Set of dest paths approved for injection.
|
|
360
|
+
*/
|
|
361
|
+
export async function diffAndPromptFiles(
|
|
362
|
+
targetPath: string,
|
|
363
|
+
options: InjectionOptions
|
|
364
|
+
): Promise<Set<string>> {
|
|
365
|
+
const { askConfirmation, createReadlineInterface } = await import('./utils.js');
|
|
366
|
+
const rl = createReadlineInterface();
|
|
367
|
+
const configRoot = findPluginConfigRoot();
|
|
368
|
+
const entries = buildFileList(targetPath, options);
|
|
369
|
+
const approved = new Set<string>();
|
|
370
|
+
|
|
371
|
+
console.log(`\nš Comparing files with existing content...`);
|
|
372
|
+
|
|
373
|
+
let hasChanges = false;
|
|
374
|
+
|
|
375
|
+
for (const entry of entries) {
|
|
376
|
+
// Skip if disabled by options
|
|
377
|
+
if (entry.optionKey !== null && !options[entry.optionKey]) continue;
|
|
378
|
+
// Skip .env merge (always approved, merge logic handled separately)
|
|
379
|
+
if (entry.mergeEnv) {
|
|
380
|
+
approved.add(entry.dest);
|
|
381
|
+
continue;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
const srcPath = path.join(configRoot, entry.src);
|
|
385
|
+
let srcContent: string;
|
|
386
|
+
try {
|
|
387
|
+
srcContent = fs.readFileSync(srcPath, 'utf8');
|
|
388
|
+
} catch {
|
|
389
|
+
// Source doesn't exist, skip
|
|
390
|
+
continue;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// Target doesn't exist yet ā inject without prompting
|
|
394
|
+
if (!fs.existsSync(entry.dest)) {
|
|
395
|
+
approved.add(entry.dest);
|
|
396
|
+
continue;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
const destContent = fs.readFileSync(entry.dest, 'utf8');
|
|
400
|
+
|
|
401
|
+
// Identical ā skip silently
|
|
402
|
+
if (srcContent === destContent) {
|
|
403
|
+
console.log(` ā
${path.relative(targetPath, entry.dest)} (unchanged)`);
|
|
404
|
+
continue;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// Different ā ask user
|
|
408
|
+
hasChanges = true;
|
|
409
|
+
const relDest = path.relative(targetPath, entry.dest);
|
|
410
|
+
const update = await askConfirmation(
|
|
411
|
+
` Update ${relDest}? (content differs)`,
|
|
412
|
+
rl
|
|
413
|
+
);
|
|
414
|
+
if (update) {
|
|
415
|
+
approved.add(entry.dest);
|
|
416
|
+
} else {
|
|
417
|
+
console.log(` āļø Kept existing ${relDest}`);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
if (!hasChanges) {
|
|
422
|
+
console.log(` ā
All existing files are up to date`);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
rl.close();
|
|
426
|
+
return approved;
|
|
427
|
+
}
|
|
428
|
+
|
|
273
429
|
/**
|
|
274
430
|
* Inject scripts and config files
|
|
275
431
|
*/
|
|
276
432
|
export async function injectScripts(
|
|
277
433
|
targetPath: string,
|
|
278
|
-
options: InjectionOptions
|
|
434
|
+
options: InjectionOptions,
|
|
435
|
+
approvedDests: Set<string>
|
|
279
436
|
): Promise<void> {
|
|
280
437
|
const scriptsPath = path.join(targetPath, 'scripts');
|
|
281
438
|
|
|
@@ -284,7 +441,7 @@ export async function injectScripts(
|
|
|
284
441
|
console.log(`š Created scripts directory`);
|
|
285
442
|
}
|
|
286
443
|
|
|
287
|
-
await cleanOldScripts(scriptsPath);
|
|
444
|
+
await cleanOldScripts(scriptsPath, approvedDests);
|
|
288
445
|
await cleanOldLintFiles(targetPath);
|
|
289
446
|
|
|
290
447
|
const scriptFiles = [
|
|
@@ -327,9 +484,13 @@ export async function injectScripts(
|
|
|
327
484
|
if (options.scripts) {
|
|
328
485
|
for (const scriptFile of scriptFiles) {
|
|
329
486
|
try {
|
|
330
|
-
const content = copyFromLocal(scriptFile);
|
|
331
487
|
const fileName = path.basename(scriptFile);
|
|
332
488
|
const targetFile = path.join(scriptsPath, fileName);
|
|
489
|
+
if (!approvedDests.has(targetFile)) {
|
|
490
|
+
console.log(` āļø Skipped ${fileName} (kept existing)`);
|
|
491
|
+
continue;
|
|
492
|
+
}
|
|
493
|
+
const content = copyFromLocal(scriptFile);
|
|
333
494
|
fs.writeFileSync(targetFile, content, 'utf8');
|
|
334
495
|
console.log(` ā
${fileName}`);
|
|
335
496
|
} catch (error) {
|
|
@@ -359,6 +520,12 @@ export async function injectScripts(
|
|
|
359
520
|
continue;
|
|
360
521
|
}
|
|
361
522
|
|
|
523
|
+
// Skip if not approved by diff step
|
|
524
|
+
const targetFile = path.join(targetPath, destName);
|
|
525
|
+
if (!approvedDests.has(targetFile)) {
|
|
526
|
+
continue; // already logged during diff step
|
|
527
|
+
}
|
|
528
|
+
|
|
362
529
|
try {
|
|
363
530
|
const targetFile = path.join(targetPath, destName);
|
|
364
531
|
const templateContent = copyFromLocal(src);
|
|
@@ -401,8 +568,9 @@ export async function injectScripts(
|
|
|
401
568
|
if (options.vscode) {
|
|
402
569
|
for (const [src, destName] of Object.entries(configVscodeMap)) {
|
|
403
570
|
try {
|
|
404
|
-
const content = copyFromLocal(src);
|
|
405
571
|
const targetFile = path.join(targetPath, destName);
|
|
572
|
+
if (!approvedDests.has(targetFile)) continue;
|
|
573
|
+
const content = copyFromLocal(src);
|
|
406
574
|
const targetDir = path.dirname(targetFile);
|
|
407
575
|
if (!(await isValidPath(targetDir))) {
|
|
408
576
|
fs.mkdirSync(targetDir, { recursive: true });
|
|
@@ -425,6 +593,7 @@ export async function injectScripts(
|
|
|
425
593
|
const content = copyFromLocal(workflowFile);
|
|
426
594
|
const relativePath = workflowFile.replace('templates/', '');
|
|
427
595
|
const targetFile = path.join(targetPath, relativePath);
|
|
596
|
+
if (!approvedDests.has(targetFile)) continue;
|
|
428
597
|
const targetDir = path.dirname(targetFile);
|
|
429
598
|
|
|
430
599
|
if (!(await isValidPath(targetDir))) {
|
|
@@ -766,9 +935,10 @@ export async function performInjection(
|
|
|
766
935
|
console.log(`\nš Starting injection process...`);
|
|
767
936
|
|
|
768
937
|
try {
|
|
938
|
+
const approvedDests = await diffAndPromptFiles(targetPath, options);
|
|
769
939
|
await cleanNpmArtifactsIfNeeded(targetPath);
|
|
770
940
|
await ensureTsxInstalled(targetPath);
|
|
771
|
-
await injectScripts(targetPath, options);
|
|
941
|
+
await injectScripts(targetPath, options, approvedDests);
|
|
772
942
|
|
|
773
943
|
console.log(`\nš¦ Updating package.json...`);
|
|
774
944
|
await updatePackageJson(targetPath, options, useSass);
|