obsidian-plugin-config 1.1.15 → 1.1.17

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 CHANGED
@@ -62,6 +62,9 @@ yarn inject-prompt "../my-plugin"
62
62
  obsidian-inject ../my-plugin --yes
63
63
  yarn inject-path ../my-plugin --yes
64
64
 
65
+ # SASS injection (includes esbuild-sass-plugin)
66
+ yarn inject-sass ../my-plugin --yes
67
+
65
68
  # Verification only
66
69
  yarn check-plugin ../my-plugin
67
70
  ```
@@ -73,6 +76,7 @@ yarn check-plugin ../my-plugin
73
76
  - ✅ **tsconfig.json template**: modern optimized TypeScript configuration
74
77
  - ✅ **Automatic installation** of dependencies with yarn
75
78
  - ✅ **Traceability file**: `.injection-info.json` (version, injection date)
79
+ - 🎨 **SASS support**: Optional SASS/SCSS compilation with `--sass` option
76
80
 
77
81
  ## Commands available after injection
78
82
 
@@ -88,6 +92,28 @@ yarn release # GitHub release
88
92
  yarn help # Full help
89
93
  ```
90
94
 
95
+ ## SASS Support
96
+
97
+ For plugins that use SASS/SCSS styling:
98
+
99
+ ```bash
100
+ # Inject with SASS support
101
+ yarn inject-sass ../my-plugin --yes
102
+
103
+ # What gets added:
104
+ # - esbuild-sass-plugin dependency
105
+ # - SASS compilation in esbuild.config.ts
106
+ # - Automatic .scss file detection
107
+ # - CSS cleanup after compilation
108
+ ```
109
+
110
+ **SASS Features:**
111
+
112
+ - ✅ **Automatic detection** of `.scss` files in `src/` directory
113
+ - ✅ **Priority order**: `src/styles.scss` > `src/styles.css` > `styles.css`
114
+ - ✅ **Clean compilation** with automatic main.css removal
115
+ - ✅ **Error handling** with helpful messages
116
+
91
117
  ## Architecture
92
118
 
93
119
  The plugin becomes **100% STANDALONE** after injection:
@@ -3,7 +3,7 @@
3
3
  /**
4
4
  * Obsidian Plugin Config - CLI Entry Point
5
5
  * Global command: obsidian-inject
6
- * Version: 1.1.15
6
+ * Version: 1.1.17
7
7
  */
8
8
 
9
9
  import { execSync } from 'child_process';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "obsidian-plugin-config",
3
- "version": "1.1.15",
3
+ "version": "1.1.17",
4
4
  "description": "Système d'injection pour plugins Obsidian autonomes",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -32,6 +32,7 @@
32
32
  "lint": "eslint . --ext .ts",
33
33
  "lint:fix": "eslint . --ext .ts --fix",
34
34
  "inject-path": "tsx scripts/inject-path.ts",
35
+ "inject-sass": "tsx scripts/inject-path.ts --sass",
35
36
  "inject-prompt": "tsx scripts/inject-prompt.ts",
36
37
  "inject": "tsx scripts/inject-prompt.ts",
37
38
  "check-plugin": "tsx scripts/inject-path.ts --dry-run",
@@ -201,7 +201,7 @@ function buildAndPublishNpm(): void {
201
201
  console.log(`\n📤 Step 2/6: Committing and pushing changes...`);
202
202
  try {
203
203
  execSync('echo "Prepare NPM package publication" | tsx scripts/acp.ts -b', { stdio: 'inherit' });
204
- } catch (acpError) {
204
+ } catch {
205
205
  console.log(` ℹ️ No additional changes to commit`);
206
206
  }
207
207
 
package/scripts/help.ts CHANGED
@@ -26,6 +26,7 @@ BUILD & TESTING:
26
26
  INJECTION (Development phase):
27
27
  yarn inject-prompt <path> # Interactive injection (recommended)
28
28
  yarn inject-path <path> --yes # Direct injection with auto-confirm
29
+ yarn inject-sass <path> --yes # Injection with SASS support
29
30
  yarn inject, check-plugin # Injection shortcuts
30
31
 
31
32
  NPM PUBLISHING:
@@ -57,6 +58,7 @@ Injection Usage (Development phase):
57
58
  Usage:
58
59
  yarn inject-prompt ../my-plugin # Interactive (recommended)
59
60
  yarn inject-path ../my-plugin # Direct injection
61
+ yarn inject-sass ../my-plugin # Injection with SASS support
60
62
  yarn check-plugin ../my-plugin # Verification only
61
63
 
62
64
  ═══════════════════════════════════════════════════════════════════
@@ -67,13 +67,14 @@ async function analyzePlugin(pluginPath: string): Promise<InjectionPlan> {
67
67
  /**
68
68
  * Display injection plan and ask for confirmation
69
69
  */
70
- async function showInjectionPlan(plan: InjectionPlan, autoConfirm: boolean = false): Promise<boolean> {
70
+ async function showInjectionPlan(plan: InjectionPlan, autoConfirm: boolean = false, useSass: boolean = false): Promise<boolean> {
71
71
  console.log(`\n🎯 Injection Plan for: ${plan.targetPath}`);
72
72
  console.log(`📁 Target: ${path.basename(plan.targetPath)}`);
73
73
  console.log(`📦 Package.json: ${plan.hasPackageJson ? '✅' : '❌'}`);
74
74
  console.log(`📋 Manifest.json: ${plan.hasManifest ? '✅' : '❌'}`);
75
75
  console.log(`📂 Scripts folder: ${plan.hasScriptsFolder ? '✅ (will be updated)' : '❌ (will be created)'}`);
76
76
  console.log(`🔌 Obsidian plugin: ${plan.isObsidianPlugin ? '✅' : '❌'}`);
77
+ console.log(`🎨 SASS support: ${useSass ? '✅ (esbuild-sass-plugin will be added)' : '❌'}`);
77
78
 
78
79
  if (!plan.isObsidianPlugin) {
79
80
  console.log(`\n⚠️ Warning: This doesn't appear to be a valid Obsidian plugin`);
@@ -246,7 +247,7 @@ async function cleanOldLintFiles(targetPath: string): Promise<void> {
246
247
  /**
247
248
  * Inject scripts from local files
248
249
  */
249
- async function injectScripts(targetPath: string): Promise<void> {
250
+ async function injectScripts(targetPath: string, useSass: boolean = false): Promise<void> {
250
251
  const scriptsPath = path.join(targetPath, "scripts");
251
252
 
252
253
  // Create scripts directory if it doesn't exist
@@ -263,7 +264,7 @@ async function injectScripts(targetPath: string): Promise<void> {
263
264
 
264
265
  const scriptFiles = [
265
266
  "templates/scripts/utils.ts",
266
- "templates/scripts/esbuild.config.ts",
267
+ useSass ? "templates/scripts/esbuild.config-sass.ts" : "templates/scripts/esbuild.config.ts",
267
268
  "templates/scripts/acp.ts",
268
269
  "templates/scripts/update-version.ts",
269
270
  "templates/scripts/release.ts",
@@ -286,7 +287,13 @@ async function injectScripts(targetPath: string): Promise<void> {
286
287
  for (const scriptFile of scriptFiles) {
287
288
  try {
288
289
  const content = copyFromLocal(scriptFile);
289
- const fileName = path.basename(scriptFile);
290
+ let fileName = path.basename(scriptFile);
291
+
292
+ // Handle SASS template renaming
293
+ if (fileName === 'esbuild.config-sass.ts') {
294
+ fileName = 'esbuild.config.ts';
295
+ }
296
+
290
297
  const targetFile = path.join(scriptsPath, fileName);
291
298
 
292
299
  fs.writeFileSync(targetFile, content, 'utf8');
@@ -353,7 +360,7 @@ async function injectScripts(targetPath: string): Promise<void> {
353
360
  /**
354
361
  * Update package.json with autonomous configuration
355
362
  */
356
- async function updatePackageJson(targetPath: string): Promise<void> {
363
+ async function updatePackageJson(targetPath: string, useSass: boolean = false): Promise<void> {
357
364
  const packageJsonPath = path.join(targetPath, "package.json");
358
365
 
359
366
  if (!await isValidPath(packageJsonPath)) {
@@ -411,7 +418,8 @@ async function updatePackageJson(targetPath: string): Promise<void> {
411
418
  "obsidian-typings": "^3.9.5",
412
419
  "semver": "^7.7.2",
413
420
  "tsx": "^4.19.4",
414
- "typescript": "^5.8.2"
421
+ "typescript": "^5.8.2",
422
+ ...(useSass ? { "esbuild-sass-plugin": "^3.3.1" } : {})
415
423
  };
416
424
 
417
425
  // Force update TypeScript to compatible version
@@ -669,7 +677,7 @@ async function runYarnInstall(targetPath: string): Promise<void> {
669
677
  /**
670
678
  * Main injection function
671
679
  */
672
- export async function performInjection(targetPath: string): Promise<void> {
680
+ export async function performInjection(targetPath: string, useSass: boolean = false): Promise<void> {
673
681
  console.log(`\n🚀 Starting injection process...`);
674
682
 
675
683
  try {
@@ -680,11 +688,11 @@ export async function performInjection(targetPath: string): Promise<void> {
680
688
  await ensureTsxInstalled(targetPath);
681
689
 
682
690
  // Step 3: Inject scripts
683
- await injectScripts(targetPath);
691
+ await injectScripts(targetPath, useSass);
684
692
 
685
693
  // Step 4: Update package.json
686
694
  console.log(`\n📦 Updating package.json...`);
687
- await updatePackageJson(targetPath);
695
+ await updatePackageJson(targetPath, useSass);
688
696
 
689
697
  // Step 5: Analyze centralized imports (without modifying)
690
698
  await analyzeCentralizedImports(targetPath);
@@ -731,6 +739,7 @@ async function main(): Promise<void> {
731
739
  const args = process.argv.slice(2);
732
740
  const autoConfirm = args.includes('--yes') || args.includes('-y');
733
741
  const dryRun = args.includes('--dry-run') || args.includes('--check');
742
+ const useSass = args.includes('--sass');
734
743
  const targetPath = args.find(arg => !arg.startsWith('-'));
735
744
 
736
745
  if (!targetPath) {
@@ -739,6 +748,7 @@ async function main(): Promise<void> {
739
748
  console.error(` Options:`);
740
749
  console.error(` --yes, -y Auto-confirm injection`);
741
750
  console.error(` --dry-run Check only (no injection)`);
751
+ console.error(` --sass Use SASS templates (includes esbuild-sass-plugin)`);
742
752
  console.error(` Shortcuts:`);
743
753
  console.error(` yarn check-plugin ../plugin # Verification only`);
744
754
  console.error(` yarn verify-plugin ../plugin # Alias pour check-plugin`);
@@ -767,6 +777,7 @@ async function main(): Promise<void> {
767
777
  console.log(`📋 Manifest.json: ${plan.hasManifest ? '✅' : '❌'}`);
768
778
  console.log(`📂 Scripts folder: ${plan.hasScriptsFolder ? '✅ (will be updated)' : '❌ (will be created)'}`);
769
779
  console.log(`🔌 Obsidian plugin: ${plan.isObsidianPlugin ? '✅' : '❌'}`);
780
+ console.log(`🎨 SASS support: ${useSass ? '✅ (esbuild-sass-plugin will be added)' : '❌'}`);
770
781
 
771
782
  if (!plan.isObsidianPlugin) {
772
783
  console.log(`\n⚠️ Warning: This doesn't appear to be a valid Obsidian plugin`);
@@ -824,7 +835,7 @@ async function main(): Promise<void> {
824
835
  console.log(`\n🔍 Checking plugin-config repository status...`);
825
836
  await ensurePluginConfigClean();
826
837
 
827
- const confirmed = await showInjectionPlan(plan, autoConfirm);
838
+ const confirmed = await showInjectionPlan(plan, autoConfirm, useSass);
828
839
 
829
840
  if (!confirmed) {
830
841
  console.log(`❌ Injection cancelled by user`);
@@ -832,7 +843,7 @@ async function main(): Promise<void> {
832
843
  }
833
844
 
834
845
  // Perform injection
835
- await performInjection(resolvedPath);
846
+ await performInjection(resolvedPath, useSass);
836
847
 
837
848
  } catch (error) {
838
849
  console.error(`💥 Error: ${error instanceof Error ? error.message : String(error)}`);
@@ -0,0 +1,29 @@
1
+ {
2
+ "devDependencies": {
3
+ "@types/node": "^16.11.6",
4
+ "eslint": "^9.0.0",
5
+ "obsidian": "latest",
6
+ "obsidian-typings": "latest",
7
+ "typescript": "^5.8.2",
8
+ "tsx": "^4.19.4",
9
+ "esbuild-sass-plugin": "^3.3.1"
10
+ },
11
+ "engines": {
12
+ "npm": "please-use-yarn",
13
+ "yarn": ">=1.22.0"
14
+ },
15
+ "scripts": {
16
+ "start": "yarn install && yarn dev",
17
+ "dev": "tsx ../obsidian-plugin-config/scripts/esbuild.config.ts",
18
+ "build": "tsc -noEmit -skipLibCheck && tsx ../obsidian-plugin-config/scripts/esbuild.config.ts production",
19
+ "real": "tsx ../obsidian-plugin-config/scripts/esbuild.config.ts production real",
20
+ "acp": "tsx ../obsidian-plugin-config/scripts/acp.ts",
21
+ "bacp": "tsx ../obsidian-plugin-config/scripts/acp.ts -b",
22
+ "update-version": "tsx ../obsidian-plugin-config/scripts/update-version.ts",
23
+ "v": "tsx ../obsidian-plugin-config/scripts/update-version.ts",
24
+ "release": "tsx ../obsidian-plugin-config/scripts/release.ts",
25
+ "r": "tsx ../obsidian-plugin-config/scripts/release.ts",
26
+ "help": "tsx ../obsidian-plugin-config/templates/help-plugin.ts",
27
+ "h": "tsx ../obsidian-plugin-config/templates/help-plugin.ts"
28
+ }
29
+ }
@@ -0,0 +1,294 @@
1
+ import esbuild from "esbuild";
2
+ import process from "process";
3
+ import builtins from "builtin-modules";
4
+ import { config } from "dotenv";
5
+ import path from "path";
6
+ import { readFileSync } from "fs";
7
+ import { rm } from "fs/promises";
8
+ import fs from "fs";
9
+ // @ts-ignore - esbuild-sass-plugin is installed during SASS injection
10
+ import { sassPlugin } from "esbuild-sass-plugin";
11
+ import { isValidPath, copyFilesToTargetDir, askQuestion, createReadlineInterface, removeMainCss } from "./utils.js";
12
+
13
+ // Determine the plugin directory (where the script is called from)
14
+ const pluginDir = process.cwd();
15
+
16
+ // Create readline interface for prompts
17
+ const rl = createReadlineInterface();
18
+
19
+ async function promptForVaultPath(envKey: string): Promise<string> {
20
+ const vaultType = envKey === "REAL_VAULT" ? "real" : "test";
21
+ const usage = envKey === "REAL_VAULT"
22
+ ? "for final plugin installation"
23
+ : "for development and testing";
24
+
25
+ console.log(`❓ ${envKey} path is required ${usage}`);
26
+ const path = await askQuestion(`📝 Enter your ${vaultType} vault path (or Ctrl+C to cancel): `, rl);
27
+
28
+ if (!path) {
29
+ console.log('❌ No path provided, exiting...');
30
+ process.exit(1);
31
+ }
32
+
33
+ return path;
34
+ }
35
+
36
+
37
+
38
+ async function updateEnvFile(envKey: string, vaultPath: string): Promise<void> {
39
+ const envPath = path.join(pluginDir, '.env');
40
+ let envContent = '';
41
+
42
+ // Read existing .env if it exists
43
+ try {
44
+ envContent = readFileSync(envPath, 'utf8');
45
+ } catch {
46
+ // File doesn't exist, start with empty content
47
+ }
48
+
49
+ // Update or add the variable
50
+ const regex = new RegExp(`^${envKey}=.*$`, 'm');
51
+ const newLine = `${envKey}=${vaultPath}`;
52
+
53
+ if (regex.test(envContent)) {
54
+ envContent = envContent.replace(regex, newLine);
55
+ } else {
56
+ if (envContent.length > 0 && !envContent.endsWith('\n')) {
57
+ envContent += '\n';
58
+ }
59
+ envContent += newLine + '\n';
60
+ }
61
+
62
+ // Write back to .env
63
+ fs.writeFileSync(envPath, envContent);
64
+ console.log(`✅ Updated ${envKey} in .env file`);
65
+ }
66
+
67
+ function validateVaultPath(vaultPath: string): boolean {
68
+ // Check if the path contains .obsidian directory
69
+ const obsidianPath = path.join(vaultPath, ".obsidian");
70
+ const pluginsPath = path.join(vaultPath, ".obsidian", "plugins");
71
+
72
+ return fs.existsSync(obsidianPath) && fs.existsSync(pluginsPath);
73
+ }
74
+
75
+ function getVaultPath(vaultPath: string): string {
76
+ // Validate that this is a proper vault path
77
+ if (!validateVaultPath(vaultPath)) {
78
+ console.error(`❌ Invalid vault path: ${vaultPath}`);
79
+ console.error(` The path must contain a .obsidian/plugins directory`);
80
+ console.error(` Please enter a valid Obsidian vault path`);
81
+ process.exit(1);
82
+ }
83
+
84
+ // Check if the path already contains the plugins directory path
85
+ const pluginsPath = path.join(".obsidian", "plugins");
86
+ if (vaultPath.includes(pluginsPath)) {
87
+ return path.join(vaultPath, manifest.id);
88
+ } else {
89
+ return path.join(vaultPath, ".obsidian", "plugins", manifest.id);
90
+ }
91
+ }
92
+
93
+ const manifestPath = path.join(pluginDir, "manifest.json");
94
+
95
+ // Check if manifest exists (for plugin-config itself, it might not exist)
96
+ if (!fs.existsSync(manifestPath)) {
97
+ console.log("⚠️ No manifest.json found - this script is designed for Obsidian plugins");
98
+ console.log(" If you're building plugin-config itself, use 'tsc' instead");
99
+ process.exit(0);
100
+ }
101
+
102
+ const manifest = JSON.parse(readFileSync(manifestPath, "utf-8"));
103
+
104
+ config();
105
+
106
+ const EXTERNAL_DEPS = [
107
+ "obsidian",
108
+ "electron",
109
+ "@codemirror/autocomplete",
110
+ "@codemirror/collab",
111
+ "@codemirror/commands",
112
+ "@codemirror/language",
113
+ "@codemirror/lint",
114
+ "@codemirror/search",
115
+ "@codemirror/state",
116
+ "@codemirror/view",
117
+ "@lezer/common",
118
+ "@lezer/highlight",
119
+ "@lezer/lr",
120
+ ...builtins
121
+ ];
122
+
123
+ const BANNER = `/*
124
+ THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
125
+ if you want to view the source, please visit the github repository of this plugin
126
+ */`;
127
+
128
+ async function validateEnvironment(): Promise<void> {
129
+ const srcMainPath = path.join(pluginDir, "src/main.ts");
130
+ if (!await isValidPath(srcMainPath)) {
131
+ throw new Error("Invalid path for src/main.ts. main.ts must be in the src directory");
132
+ }
133
+ if (!await isValidPath(manifestPath)) {
134
+ throw new Error("Invalid path for manifest.json");
135
+ }
136
+ }
137
+
138
+ async function getBuildPath(isProd: boolean): Promise<string> {
139
+ // Check if we should use real vault (either -r flag or "real" argument)
140
+ const useRealVault = process.argv.includes("-r") || process.argv.includes("real");
141
+
142
+ // If production build without redirection, return plugin directory
143
+ if (isProd && !useRealVault) {
144
+ return pluginDir;
145
+ }
146
+
147
+ // Determine which path to use
148
+ const envKey = useRealVault ? "REAL_VAULT" : "TEST_VAULT";
149
+ const vaultPath = process.env[envKey]?.trim();
150
+
151
+ // If empty or undefined, we're already in the plugin folder
152
+ if (!vaultPath) {
153
+ // Check if we're in Obsidian plugins folder
154
+ const currentPath = process.cwd();
155
+ const isInObsidianPlugins = currentPath.includes('.obsidian/plugins') ||
156
+ currentPath.includes('.obsidian\\plugins');
157
+
158
+ if (isInObsidianPlugins) {
159
+ // In obsidian/plugins: allow in-place development
160
+ console.log(`ℹ️ Building in Obsidian plugins folder (in-place development)`);
161
+ return pluginDir;
162
+ } else {
163
+ // External development: prompt for missing vault path
164
+ const newPath = await promptForVaultPath(envKey);
165
+ await updateEnvFile(envKey, newPath);
166
+ config();
167
+ return getVaultPath(newPath);
168
+ }
169
+ }
170
+
171
+ // If we reach here, use the vault path directly
172
+ return getVaultPath(vaultPath);
173
+ }
174
+
175
+ async function createBuildContext(buildPath: string, isProd: boolean, entryPoints: string[], hasSass: boolean): Promise<esbuild.BuildContext> {
176
+ const plugins = [
177
+ // Add SASS plugin if SCSS files are detected
178
+ ...(hasSass ? [
179
+ sassPlugin({
180
+ syntax: 'scss',
181
+ style: 'expanded',
182
+ }),
183
+ {
184
+ name: 'remove-main-css',
185
+ setup(build: esbuild.PluginBuild): void {
186
+ build.onEnd(async (result) => {
187
+ if (result.errors.length === 0) {
188
+ await removeMainCss(buildPath);
189
+ }
190
+ });
191
+ },
192
+ }
193
+ ] : []),
194
+ // Plugin pour gérer les alias de chemin
195
+ {
196
+ name: "path-alias",
197
+ setup: (build: esbuild.PluginBuild): void => {
198
+ build.onResolve({ filter: /^@config\// }, (args) => {
199
+ const relativePath = args.path.replace(/^@config\//, "");
200
+ return {
201
+ path: path.resolve("../obsidian-plugin-config/src", relativePath)
202
+ };
203
+ });
204
+
205
+ build.onResolve({ filter: /^@config-scripts\// }, (args) => {
206
+ const relativePath = args.path.replace(/^@config-scripts\//, "");
207
+ return {
208
+ path: path.resolve("../obsidian-plugin-config/scripts", relativePath)
209
+ };
210
+ });
211
+ }
212
+ },
213
+ {
214
+ name: "copy-to-plugins-folder",
215
+ setup: (build: esbuild.PluginBuild): void => {
216
+ build.onEnd(async () => {
217
+ // if real or build
218
+ if (isProd) {
219
+ if (process.argv.includes("-r") || process.argv.includes("real")) {
220
+ await copyFilesToTargetDir(buildPath);
221
+ console.log(`Successfully installed in ${buildPath}`);
222
+ } else {
223
+ const folderToRemove = path.join(buildPath, "_.._");
224
+ if (await isValidPath(folderToRemove)) {
225
+ await rm(folderToRemove, { recursive: true });
226
+ }
227
+ console.log("Built done in initial folder");
228
+ }
229
+ }
230
+ // if watch (dev)
231
+ else {
232
+ await copyFilesToTargetDir(buildPath);
233
+ }
234
+ });
235
+ }
236
+ }
237
+ ];
238
+
239
+ return await esbuild.context({
240
+ banner: { js: BANNER },
241
+ minify: isProd,
242
+ entryPoints,
243
+ bundle: true,
244
+ external: EXTERNAL_DEPS,
245
+ format: "cjs",
246
+ target: "esNext",
247
+ platform: "node",
248
+ logLevel: "info",
249
+ sourcemap: isProd ? false : "inline",
250
+ treeShaking: true,
251
+ outdir: buildPath,
252
+ outbase: path.join(pluginDir, "src"),
253
+ plugins
254
+ });
255
+ }
256
+
257
+ async function main(): Promise<void> {
258
+ try {
259
+ await validateEnvironment();
260
+ const isProd = process.argv[2] === "production";
261
+ const buildPath = await getBuildPath(isProd);
262
+ console.log(buildPath === pluginDir
263
+ ? "Building in initial folder"
264
+ : `Building in ${buildPath}`);
265
+
266
+ // Check for SCSS first, then CSS in src, then in root
267
+ const srcStylesScssPath = path.join(pluginDir, "src/styles.scss");
268
+ const srcStylesPath = path.join(pluginDir, "src/styles.css");
269
+ const rootStylesPath = path.join(pluginDir, "styles.css");
270
+
271
+ const scssExists = await isValidPath(srcStylesScssPath);
272
+ const stylePath = scssExists ? srcStylesScssPath :
273
+ await isValidPath(srcStylesPath) ? srcStylesPath :
274
+ await isValidPath(rootStylesPath) ? rootStylesPath : "";
275
+
276
+ const mainTsPath = path.join(pluginDir, "src/main.ts");
277
+ const entryPoints = stylePath ? [mainTsPath, stylePath] : [mainTsPath];
278
+ const context = await createBuildContext(buildPath, isProd, entryPoints, scssExists);
279
+
280
+ if (isProd) {
281
+ await context.rebuild();
282
+ rl.close();
283
+ process.exit(0);
284
+ } else {
285
+ await context.watch();
286
+ }
287
+ } catch (error) {
288
+ console.error("Build failed:", error);
289
+ rl.close();
290
+ process.exit(1);
291
+ }
292
+ }
293
+
294
+ main().catch(console.error);
@@ -6,7 +6,7 @@ import path from "path";
6
6
  import { readFileSync } from "fs";
7
7
  import { rm } from "fs/promises";
8
8
  import fs from "fs";
9
- import { isValidPath, copyFilesToTargetDir, askQuestion, createReadlineInterface } from "./utils.js";
9
+ import { isValidPath, copyFilesToTargetDir, askQuestion, createReadlineInterface, removeMainCss } from "./utils.js";
10
10
 
11
11
  // Determine the plugin directory (where the script is called from)
12
12
  const pluginDir = process.cwd();
@@ -167,7 +167,80 @@ async function getBuildPath(isProd: boolean): Promise<string> {
167
167
  return getVaultPath(vaultPath);
168
168
  }
169
169
 
170
- async function createBuildContext(buildPath: string, isProd: boolean, entryPoints: string[]): Promise<esbuild.BuildContext> {
170
+ async function createBuildContext(buildPath: string, isProd: boolean, entryPoints: string[], hasSass: boolean): Promise<esbuild.BuildContext> {
171
+ const plugins = [
172
+ // Add SASS plugin if SCSS files are detected
173
+ ...(hasSass ? [
174
+ // Dynamic import for SASS plugin to avoid dependency issues when not needed
175
+ await (async () => {
176
+ try {
177
+ // @ts-ignore - esbuild-sass-plugin is installed during injection
178
+ const { sassPlugin } = await import('esbuild-sass-plugin');
179
+ return sassPlugin({
180
+ syntax: 'scss',
181
+ style: 'expanded',
182
+ });
183
+ } catch (error) {
184
+ console.warn('⚠️ esbuild-sass-plugin not found. Install it with: yarn add -D esbuild-sass-plugin');
185
+ throw error;
186
+ }
187
+ })(),
188
+ {
189
+ name: 'remove-main-css',
190
+ setup(build: esbuild.PluginBuild): void {
191
+ build.onEnd(async (result) => {
192
+ if (result.errors.length === 0) {
193
+ await removeMainCss(buildPath);
194
+ }
195
+ });
196
+ },
197
+ }
198
+ ] : []),
199
+ // Plugin pour gérer les alias de chemin
200
+ {
201
+ name: "path-alias",
202
+ setup: (build: esbuild.PluginBuild): void => {
203
+ build.onResolve({ filter: /^@config\// }, (args) => {
204
+ const relativePath = args.path.replace(/^@config\//, "");
205
+ return {
206
+ path: path.resolve("../obsidian-plugin-config/src", relativePath)
207
+ };
208
+ });
209
+
210
+ build.onResolve({ filter: /^@config-scripts\// }, (args) => {
211
+ const relativePath = args.path.replace(/^@config-scripts\//, "");
212
+ return {
213
+ path: path.resolve("../obsidian-plugin-config/scripts", relativePath)
214
+ };
215
+ });
216
+ }
217
+ },
218
+ {
219
+ name: "copy-to-plugins-folder",
220
+ setup: (build: esbuild.PluginBuild): void => {
221
+ build.onEnd(async () => {
222
+ // if real or build
223
+ if (isProd) {
224
+ if (process.argv.includes("-r") || process.argv.includes("real")) {
225
+ await copyFilesToTargetDir(buildPath);
226
+ console.log(`Successfully installed in ${buildPath}`);
227
+ } else {
228
+ const folderToRemove = path.join(buildPath, "_.._");
229
+ if (await isValidPath(folderToRemove)) {
230
+ await rm(folderToRemove, { recursive: true });
231
+ }
232
+ console.log("Built done in initial folder");
233
+ }
234
+ }
235
+ // if watch (dev)
236
+ else {
237
+ await copyFilesToTargetDir(buildPath);
238
+ }
239
+ });
240
+ }
241
+ }
242
+ ];
243
+
171
244
  return await esbuild.context({
172
245
  banner: { js: BANNER },
173
246
  minify: isProd,
@@ -182,51 +255,7 @@ async function createBuildContext(buildPath: string, isProd: boolean, entryPoint
182
255
  treeShaking: true,
183
256
  outdir: buildPath,
184
257
  outbase: path.join(pluginDir, "src"),
185
- plugins: [
186
- // Plugin pour gérer les alias de chemin
187
- {
188
- name: "path-alias",
189
- setup: (build): void => {
190
- build.onResolve({ filter: /^@config\// }, (args) => {
191
- const relativePath = args.path.replace(/^@config\//, "");
192
- return {
193
- path: path.resolve("../obsidian-plugin-config/src", relativePath)
194
- };
195
- });
196
-
197
- build.onResolve({ filter: /^@config-scripts\// }, (args) => {
198
- const relativePath = args.path.replace(/^@config-scripts\//, "");
199
- return {
200
- path: path.resolve("../obsidian-plugin-config/scripts", relativePath)
201
- };
202
- });
203
- }
204
- },
205
- {
206
- name: "copy-to-plugins-folder",
207
- setup: (build): void => {
208
- build.onEnd(async () => {
209
- // if real or build
210
- if (isProd) {
211
- if (process.argv.includes("-r") || process.argv.includes("real")) {
212
- await copyFilesToTargetDir(buildPath);
213
- console.log(`Successfully installed in ${buildPath}`);
214
- } else {
215
- const folderToRemove = path.join(buildPath, "_.._");
216
- if (await isValidPath(folderToRemove)) {
217
- await rm(folderToRemove, { recursive: true });
218
- }
219
- console.log("Built done in initial folder");
220
- }
221
- }
222
- // if watch (dev)
223
- else {
224
- await copyFilesToTargetDir(buildPath);
225
- }
226
- });
227
- }
228
- }
229
- ]
258
+ plugins
230
259
  });
231
260
  }
232
261
 
@@ -238,12 +267,20 @@ async function main(): Promise<void> {
238
267
  console.log(buildPath === pluginDir
239
268
  ? "Building in initial folder"
240
269
  : `Building in ${buildPath}`);
270
+
271
+ // Check for SCSS first, then CSS in src, then in root
272
+ const srcStylesScssPath = path.join(pluginDir, "src/styles.scss");
241
273
  const srcStylesPath = path.join(pluginDir, "src/styles.css");
242
274
  const rootStylesPath = path.join(pluginDir, "styles.css");
243
- const stylePath = await isValidPath(srcStylesPath) ? srcStylesPath : await isValidPath(rootStylesPath) ? rootStylesPath : "";
275
+
276
+ const scssExists = await isValidPath(srcStylesScssPath);
277
+ const stylePath = scssExists ? srcStylesScssPath :
278
+ await isValidPath(srcStylesPath) ? srcStylesPath :
279
+ await isValidPath(rootStylesPath) ? rootStylesPath : "";
280
+
244
281
  const mainTsPath = path.join(pluginDir, "src/main.ts");
245
282
  const entryPoints = stylePath ? [mainTsPath, stylePath] : [mainTsPath];
246
- const context = await createBuildContext(buildPath, isProd, entryPoints);
283
+ const context = await createBuildContext(buildPath, isProd, entryPoints, scssExists);
247
284
 
248
285
  if (isProd) {
249
286
  await context.rebuild();
@@ -145,4 +145,19 @@ export async function ensureGitSync(): Promise<void> {
145
145
  console.error(`❌ Git sync failed: ${error.message}`);
146
146
  throw error;
147
147
  }
148
+ }
149
+
150
+ /**
151
+ * Remove main.css file generated by esbuild when compiling SCSS
152
+ * This prevents the unwanted main.css from being included in the plugin
153
+ */
154
+ export async function removeMainCss(outdir: string): Promise<void> {
155
+ const mainCssPath = path.join(outdir, 'main.css');
156
+ try {
157
+ await rm(mainCssPath);
158
+ } catch (error: any) {
159
+ if (error.code !== 'ENOENT') {
160
+ console.warn(`Warning: Could not remove main.css: ${error.message}`);
161
+ }
162
+ }
148
163
  }
package/versions.json CHANGED
@@ -19,5 +19,7 @@
19
19
  "1.1.12": "1.8.9",
20
20
  "1.1.13": "1.8.9",
21
21
  "1.1.14": "1.8.9",
22
- "1.1.15": "1.8.9"
22
+ "1.1.15": "1.8.9",
23
+ "1.1.16": "1.8.9",
24
+ "1.1.17": "1.8.9"
23
25
  }