obsidian-plugin-config 1.6.17 → 1.7.0

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.
@@ -1,310 +1,157 @@
1
1
  import esbuild from 'esbuild';
2
2
  import process from 'process';
3
- import builtins from 'builtin-modules';
4
3
  import { config } from 'dotenv';
5
4
  import path from 'path';
6
- import { readFileSync } from 'fs';
7
5
  import { rm } from 'fs/promises';
8
- import fs from 'fs';
6
+ import { type Interface } from 'readline';
7
+ import { obsidianTypingsPlugin } from './typingsPlugin.ts';
8
+ import { EXTERNAL_DEPS, BANNER } from './constants.ts';
9
9
  import {
10
- isValidPath,
11
- copyFilesToTargetDir,
12
- askQuestion,
13
- createReadlineInterface,
14
- removeMainCss
15
- } from './utils.js';
10
+ type Manifest,
11
+ checkManifest,
12
+ validateEnvironment,
13
+ getBuildPath
14
+ } from './env.ts';
15
+ import {
16
+ copyFilesToTargetDir,
17
+ createReadlineInterface,
18
+ isValidPath,
19
+ removeMainCss
20
+ } from './utils.ts';
21
+ import { reloadObsidian } from './reload.ts';
16
22
 
17
23
  // Determine the plugin directory (where the script is called from)
18
24
  const pluginDir = process.cwd();
19
25
 
20
26
  // Create readline interface for prompts
21
- const rl = createReadlineInterface();
22
-
23
- async function promptForVaultPath(envKey: string): Promise<string> {
24
- const vaultType = envKey === 'REAL_VAULT' ? 'real' : 'test';
25
- const usage =
26
- envKey === 'REAL_VAULT'
27
- ? 'for final plugin installation'
28
- : 'for development and testing';
29
-
30
- console.log(`❓ ${envKey} path is required ${usage}`);
31
- const path = await askQuestion(
32
- `📝 Enter your ${vaultType} vault path (or Ctrl+C to cancel): `,
33
- rl
34
- );
35
-
36
- if (!path) {
37
- console.log('❌ No path provided, exiting...');
38
- process.exit(1);
39
- }
40
-
41
- return path;
42
- }
43
-
44
- async function updateEnvFile(envKey: string, vaultPath: string): Promise<void> {
45
- const envPath = path.join(pluginDir, '.env');
46
- let envContent = '';
47
-
48
- // Read existing .env if it exists
49
- try {
50
- envContent = readFileSync(envPath, 'utf8');
51
- } catch {
52
- // File doesn't exist, start with empty content
53
- }
54
-
55
- // Update or add the variable
56
- const regex = new RegExp(`^${envKey}=.*$`, 'm');
57
- const newLine = `${envKey}=${vaultPath}`;
58
-
59
- if (regex.test(envContent)) {
60
- envContent = envContent.replace(regex, newLine);
61
- } else {
62
- envContent += envContent.endsWith('\n') ? '' : '\n';
63
- envContent += newLine + '\n';
64
- }
65
-
66
- // Write back to .env
67
- await import('fs').then((fs) => fs.writeFileSync(envPath, envContent));
68
- console.log(`✅ Updated ${envKey} in .env file`);
69
- }
70
-
71
- function validateVaultPath(vaultPath: string): boolean {
72
- // Check if the path contains .obsidian directory
73
- const obsidianPath = path.join(vaultPath, '.obsidian');
74
- const pluginsPath = path.join(vaultPath, '.obsidian', 'plugins');
75
-
76
- return fs.existsSync(obsidianPath) && fs.existsSync(pluginsPath);
77
- }
78
-
79
- function getVaultPath(vaultPath: string): string {
80
- // Validate that this is a proper vault path
81
- if (!validateVaultPath(vaultPath)) {
82
- console.error(`❌ Invalid vault path: ${vaultPath}`);
83
- console.error(` The path must contain a .obsidian/plugins directory`);
84
- console.error(` Please enter a valid Obsidian vault path`);
85
- process.exit(1);
86
- }
87
-
88
- // Check if the path already contains the plugins directory path
89
- const pluginsPath = path.join('.obsidian', 'plugins');
90
- if (vaultPath.includes(pluginsPath)) {
91
- return path.join(vaultPath, manifest.id);
92
- } else {
93
- return path.join(vaultPath, '.obsidian', 'plugins', manifest.id);
94
- }
95
- }
96
- const manifestPath = path.join(pluginDir, 'manifest.json');
97
-
98
- // Check if manifest exists (for plugin-config itself, it might not exist)
99
- if (!fs.existsSync(manifestPath)) {
100
- console.log(
101
- '⚠️ No manifest.json found - this script is designed for Obsidian plugins'
102
- );
103
- console.log(" If you're building plugin-config itself, use 'tsc' instead");
104
- process.exit(0);
105
- }
27
+ const rl: Interface = createReadlineInterface();
106
28
 
107
- const manifest = JSON.parse(readFileSync(manifestPath, 'utf-8'));
29
+ const manifest: Manifest = checkManifest(pluginDir);
108
30
 
109
31
  config();
110
32
 
111
- const EXTERNAL_DEPS = [
112
- 'obsidian',
113
- 'electron',
114
- '@codemirror/autocomplete',
115
- '@codemirror/collab',
116
- '@codemirror/commands',
117
- '@codemirror/language',
118
- '@codemirror/lint',
119
- '@codemirror/search',
120
- '@codemirror/state',
121
- '@codemirror/view',
122
- '@lezer/common',
123
- '@lezer/highlight',
124
- '@lezer/lr',
125
- ...builtins
126
- ];
127
-
128
- const BANNER = `/*
129
- THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
130
- if you want to view the source, please visit the github repository of this plugin
131
- */`;
132
-
133
- async function validateEnvironment(): Promise<void> {
134
- const srcMainPath = path.join(pluginDir, 'src/main.ts');
135
- if (!(await isValidPath(srcMainPath))) {
136
- throw new Error(
137
- 'Invalid path for src/main.ts. main.ts must be in the src directory'
138
- );
139
- }
140
- if (!(await isValidPath(manifestPath))) {
141
- throw new Error('Invalid path for manifest.json');
142
- }
143
- }
144
-
145
- async function getBuildPath(isProd: boolean): Promise<string> {
146
- // Check if we're already inside an Obsidian plugins folder
147
- const pluginsPath = path.join('.obsidian', 'plugins');
148
- const isInPluginsFolder = pluginDir.includes(pluginsPath);
149
-
150
- if (isInPluginsFolder) {
151
- console.log('ℹ️ Building in Obsidian plugins folder (in-place development)');
152
- return pluginDir;
153
- }
154
-
155
- // External development: check for vault paths
156
- const useRealVault = process.argv.includes('-r') || process.argv.includes('real');
157
-
158
- // If production build without redirection, return plugin directory
159
- if (isProd && !useRealVault) {
160
- return pluginDir;
161
- }
162
-
163
- // Determine which path to use
164
- const envKey = useRealVault ? 'REAL_VAULT' : 'TEST_VAULT';
165
- const vaultPath = process.env[envKey]?.trim();
166
-
167
- // If empty or undefined, prompt for vault path
168
- if (!vaultPath || vaultPath.startsWith('/path/to/your/')) {
169
- const newPath = await promptForVaultPath(envKey);
170
- await updateEnvFile(envKey, newPath);
171
- config();
172
- return getVaultPath(newPath);
173
- }
174
-
175
- return getVaultPath(vaultPath);
176
- }
177
-
178
33
  async function createBuildContext(
179
- buildPath: string,
180
- isProd: boolean,
181
- entryPoints: string[],
182
- hasSass: boolean
34
+ buildPath: string,
35
+ isProd: boolean,
36
+ entryPoints: string[],
37
+ hasSass: boolean
183
38
  ): Promise<esbuild.BuildContext> {
184
- const plugins = [
185
- // Add SASS plugin if SCSS files are detected
186
- ...(hasSass
187
- ? [
188
- await (async () => {
189
- try {
190
- // @ts-expect-error - esbuild-sass-plugin is installed during injection
191
- const { sassPlugin } = await import('esbuild-sass-plugin');
192
- return sassPlugin({
193
- syntax: 'scss',
194
- style: 'expanded'
195
- });
196
- } catch (error) {
197
- console.warn(
198
- '⚠️ esbuild-sass-plugin not found. Install it with: yarn add -D esbuild-sass-plugin'
199
- );
200
- throw error;
201
- }
202
- })(),
203
- {
204
- name: 'remove-main-css',
205
- setup(build: esbuild.PluginBuild): void {
206
- build.onEnd(async (result) => {
207
- if (result.errors.length === 0) {
208
- await removeMainCss(buildPath);
209
- }
210
- });
211
- }
212
- }
213
- ]
214
- : []),
215
- {
216
- name: 'copy-to-plugins-folder',
217
- setup: (build: esbuild.PluginBuild): void => {
218
- build.onEnd(async () => {
219
- // if real or build
220
- if (isProd) {
221
- if (
222
- process.argv.includes('-r') ||
223
- process.argv.includes('real')
224
- ) {
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
-
244
- return await esbuild.context({
245
- banner: { js: BANNER },
246
- minify: isProd,
247
- entryPoints,
248
- bundle: true,
249
- external: EXTERNAL_DEPS,
250
- format: 'cjs',
251
- target: 'esNext',
252
- platform: 'node',
253
- logLevel: 'info',
254
- sourcemap: isProd ? false : 'inline',
255
- treeShaking: true,
256
- outdir: buildPath,
257
- outbase: path.join(pluginDir, 'src'),
258
- plugins
259
- });
39
+ const plugins = [
40
+ // Add SASS plugin if SCSS files are detected
41
+ ...(hasSass
42
+ ? [
43
+ await (async () => {
44
+ try {
45
+ // @ts-expect-error - esbuild-sass-plugin is installed during injection
46
+ const { sassPlugin } = await import('esbuild-sass-plugin');
47
+ return sassPlugin({
48
+ syntax: 'scss',
49
+ style: 'expanded'
50
+ });
51
+ } catch (error) {
52
+ console.warn(
53
+ '⚠️ esbuild-sass-plugin not found. Install it with: yarn add -D esbuild-sass-plugin'
54
+ );
55
+ throw error;
56
+ }
57
+ })(),
58
+ {
59
+ name: 'remove-main-css',
60
+ setup(build: esbuild.PluginBuild): void {
61
+ build.onEnd(async (result) => {
62
+ if (result.errors.length === 0) {
63
+ await removeMainCss(buildPath);
64
+ }
65
+ });
66
+ }
67
+ }
68
+ ]
69
+ : []),
70
+ {
71
+ name: 'copy-to-plugins-folder',
72
+ setup: (build: esbuild.PluginBuild): void => {
73
+ build.onEnd(async () => {
74
+ // if real or build
75
+ if (isProd) {
76
+ if (process.argv.includes('-r') || process.argv.includes('real')) {
77
+ await copyFilesToTargetDir(buildPath);
78
+ console.log(`Successfully installed in ${buildPath}`);
79
+ await reloadObsidian();
80
+ } else {
81
+ const folderToRemove = path.join(buildPath, '_.._');
82
+ if (await isValidPath(folderToRemove)) {
83
+ await rm(folderToRemove, { recursive: true });
84
+ }
85
+ console.log('Build done in initial folder');
86
+ }
87
+ }
88
+ // if watch (dev)
89
+ else {
90
+ await copyFilesToTargetDir(buildPath);
91
+ }
92
+ });
93
+ }
94
+ }
95
+ ];
96
+
97
+ return await esbuild.context({
98
+ banner: { js: BANNER },
99
+ minify: isProd,
100
+ entryPoints,
101
+ bundle: true,
102
+ external: EXTERNAL_DEPS,
103
+ format: 'cjs',
104
+ target: 'esNext',
105
+ platform: 'node',
106
+ logLevel: 'info',
107
+ sourcemap: isProd ? false : 'inline',
108
+ treeShaking: true,
109
+ outdir: buildPath,
110
+ outbase: path.join(pluginDir, 'src'),
111
+ plugins: [obsidianTypingsPlugin(pluginDir), ...plugins]
112
+ });
260
113
  }
261
114
 
262
115
  async function main(): Promise<void> {
263
- try {
264
- await validateEnvironment();
265
- const isProd = process.argv[2] === 'production';
266
- const buildPath = await getBuildPath(isProd);
267
- console.log(
268
- buildPath === pluginDir
269
- ? 'Building in initial folder'
270
- : `Building in ${buildPath}`
271
- );
272
-
273
- // Check for SCSS first, then CSS in src, then in root
274
- const srcStylesScssPath = path.join(pluginDir, 'src/styles.scss');
275
- const srcStylesPath = path.join(pluginDir, 'src/styles.css');
276
- const rootStylesPath = path.join(pluginDir, 'styles.css');
277
-
278
- const scssExists = await isValidPath(srcStylesScssPath);
279
- const stylePath = scssExists
280
- ? srcStylesScssPath
281
- : (await isValidPath(srcStylesPath))
282
- ? srcStylesPath
283
- : (await isValidPath(rootStylesPath))
284
- ? rootStylesPath
285
- : '';
286
-
287
- const mainTsPath = path.join(pluginDir, 'src/main.ts');
288
- const entryPoints = stylePath ? [mainTsPath, stylePath] : [mainTsPath];
289
- const context = await createBuildContext(
290
- buildPath,
291
- isProd,
292
- entryPoints,
293
- scssExists
294
- );
295
-
296
- if (isProd) {
297
- await context.rebuild();
298
- rl.close();
299
- process.exit(0);
300
- } else {
301
- await context.watch();
302
- }
303
- } catch (error) {
304
- console.error('Build failed:', error);
305
- rl.close();
306
- process.exit(1);
307
- }
116
+ try {
117
+ await validateEnvironment(pluginDir);
118
+ const isProd = process.argv[2] === 'production';
119
+ const buildPath = await getBuildPath(pluginDir, manifest, isProd, rl);
120
+ console.log(
121
+ buildPath === pluginDir ? 'Building in initial folder' : `Building in ${buildPath}`
122
+ );
123
+
124
+ // Check for SCSS first, then CSS in src, then in root
125
+ const srcStylesScssPath = path.join(pluginDir, 'src/styles.scss');
126
+ const srcStylesPath = path.join(pluginDir, 'src/styles.css');
127
+ const rootStylesPath = path.join(pluginDir, 'styles.css');
128
+
129
+ const scssExists = await isValidPath(srcStylesScssPath);
130
+ const stylePath = scssExists
131
+ ? srcStylesScssPath
132
+ : (await isValidPath(srcStylesPath))
133
+ ? srcStylesPath
134
+ : (await isValidPath(rootStylesPath))
135
+ ? rootStylesPath
136
+ : '';
137
+
138
+ const mainTsPath = path.join(pluginDir, 'src/main.ts');
139
+ const entryPoints = stylePath ? [mainTsPath, stylePath] : [mainTsPath];
140
+ const context = await createBuildContext(buildPath, isProd, entryPoints, scssExists);
141
+
142
+ if (isProd) {
143
+ await context.rebuild();
144
+ rl.close();
145
+ process.exit(0);
146
+ } else {
147
+ await context.watch();
148
+ }
149
+ } catch (error) {
150
+ console.error('Build failed:', error);
151
+ rl.close();
152
+ process.exit(1);
153
+ }
308
154
  }
309
155
 
310
156
  main().catch(console.error);
157
+
@@ -1,113 +1,118 @@
1
1
  import { writeFile, stat } from 'fs/promises';
2
2
  import { execSync } from 'child_process';
3
3
  import dedent from 'dedent';
4
- import { askConfirmation, createReadlineInterface, ensureGitSync } from './utils.js';
4
+ import { join } from 'path';
5
+ import {
6
+ askConfirmation,
7
+ askQuestion,
8
+ createReadlineInterface,
9
+ ensureGitSync
10
+ } from './utils.ts';
5
11
 
6
12
  const rl = createReadlineInterface();
7
13
 
8
- const body = '.github/workflows/release-body.md';
14
+ const body = join('.github', 'workflows', 'release-body.md');
9
15
 
10
16
  async function checkOrCreateFile(filename: string): Promise<void> {
11
- try {
12
- await stat(filename);
13
- } catch {
14
- console.log(`Creating ${filename} because it doesn't exist. Avoid deleting it.`);
15
- await writeFile(filename, '');
16
- }
17
+ try {
18
+ await stat(filename);
19
+ } catch {
20
+ console.log(`Creating ${filename} because it doesn't exist. Avoid deleting it.`);
21
+ await writeFile(filename, '');
22
+ }
17
23
  }
18
24
 
19
25
  async function createReleaseNotesFile(tagMessage: string, tag: string): Promise<void> {
20
- await writeFile(body, tagMessage);
21
- console.log(`Release notes for tag ${tag} have been written to release-body.md`);
26
+ await writeFile(body, tagMessage);
27
+ console.log(`Release notes for tag ${tag} have been written to release-body.md`);
22
28
  }
23
29
 
24
30
  async function handleExistingTag(tag: string): Promise<boolean> {
25
- // Get the existing tag message to show to the user
26
- let existingMessage = '';
27
- try {
28
- existingMessage = execSync(`git tag -l -n999 ${tag}`, {
29
- encoding: 'utf8'
30
- }).trim();
31
- } catch {
32
- // If we can't get the message, continue anyway
33
- }
34
-
35
- let prompt = `Tag ${tag} already exists.`;
36
- if (existingMessage) {
37
- prompt += `\n\nExisting tag message:\n${existingMessage}\n`;
38
- }
39
- prompt += `\nDo you want to replace it?`;
40
-
41
- const confirmed = await askConfirmation(prompt, rl);
42
-
43
- if (!confirmed) {
44
- console.log('Operation aborted');
45
- return false;
46
- }
47
-
48
- execSync(`git tag -d ${tag}`);
49
- execSync(`git push origin :refs/tags/${tag}`);
50
- console.log(`Deleted existing tag ${tag} locally and remotely.`);
51
- return true;
31
+ // Get the existing tag message to show to the user
32
+ let existingMessage = '';
33
+ try {
34
+ existingMessage = execSync(`git tag -l -n999 ${tag}`, {
35
+ encoding: 'utf8'
36
+ }).trim();
37
+ } catch {
38
+ // If we can't get the message, continue anyway
39
+ }
40
+
41
+ let prompt = `Tag ${tag} already exists.`;
42
+ if (existingMessage) {
43
+ prompt += `\n\nExisting tag message:\n${existingMessage}\n`;
44
+ }
45
+ prompt += `\nDo you want to replace it?`;
46
+
47
+ const confirmed = await askConfirmation(prompt, rl);
48
+
49
+ if (!confirmed) {
50
+ console.log('Operation aborted');
51
+ return false;
52
+ }
53
+
54
+ execSync(`git tag -d ${tag}`);
55
+ execSync(`git push origin :refs/tags/${tag}`);
56
+ console.log(`Deleted existing tag ${tag} locally and remotely.`);
57
+ return true;
52
58
  }
53
59
 
54
60
  async function createTag(): Promise<void> {
55
- const currentVersion = process.env.npm_package_version;
56
- const tag = `${currentVersion}`;
57
-
58
- await checkOrCreateFile(body);
59
- const exists = execSync('git ls-remote --tags origin')
60
- .toString()
61
- .includes(`refs/tags/${tag}`);
62
-
63
- if (exists && !(await handleExistingTag(tag))) {
64
- rl.close();
65
- return;
66
- }
67
- await doCommit(currentVersion, tag);
61
+ const currentVersion = process.env.npm_package_version;
62
+ const tag = `${currentVersion}`;
63
+
64
+ await checkOrCreateFile(body);
65
+ const exists = execSync('git ls-remote --tags origin')
66
+ .toString()
67
+ .includes(`refs/tags/${tag}`);
68
+
69
+ if (exists && !(await handleExistingTag(tag))) {
70
+ rl.close();
71
+ return;
72
+ }
73
+ await doCommit(currentVersion, tag);
68
74
  }
69
75
 
70
76
  async function doCommit(currentVersion: string | undefined, tag: string): Promise<void> {
71
- rl.question(
72
- `Enter the commit message for version ${currentVersion}: `,
73
- async (message) => {
74
- doNextSteps(message, tag);
75
- rl.close();
76
- }
77
- );
77
+ const message = await askQuestion(
78
+ `Enter the commit message for version ${currentVersion}: `,
79
+ rl
80
+ );
81
+ rl.close();
82
+ await doNextSteps(message, tag);
78
83
  }
79
84
 
80
85
  async function doNextSteps(message: string, tag: string): Promise<void> {
81
- const messages = message.split('\\n');
82
- const toShow = message.replace(/\\n/g, '\n');
83
- await createReleaseNotesFile(toShow, tag);
84
- const tagMessage = messages.map((m) => `-m "${m}"`).join(' ');
85
-
86
- try {
87
- execSync('git add -A');
88
- execSync('git commit -m "update tag description"');
89
-
90
- // Ensure Git is synchronized before pushing
91
- await ensureGitSync();
92
-
93
- execSync('git push');
94
- } catch (error: unknown) {
95
- console.error('Error:', error instanceof Error ? error.message : String(error));
96
- }
97
- try {
98
- execSync(`git tag -a ${tag} ${tagMessage}`);
99
- } catch {
100
- execSync(`git tag -d ${tag}`);
101
- execSync(`git push origin :refs/tags/${tag}`);
102
- console.log('Fixed');
103
- execSync(`git tag -a ${tag} ${tagMessage}`);
104
- }
105
- // Ensure Git is synchronized before pushing tag
106
- await ensureGitSync();
107
-
108
- execSync(`git push origin ${tag}`);
109
- console.log(`Release ${tag} pushed to repo.`);
110
- console.log(dedent`
86
+ const messages = message.split('\\n');
87
+ const toShow = message.replace(/\\n/g, '\n');
88
+ await createReleaseNotesFile(toShow, tag);
89
+ const tagMessage = messages.map((m) => `-m "${m}"`).join(' ');
90
+
91
+ try {
92
+ execSync('git add -A');
93
+ execSync('git commit -m "update tag description"');
94
+
95
+ // Ensure Git is synchronized before pushing
96
+ await ensureGitSync();
97
+
98
+ execSync('git push');
99
+ } catch (error: unknown) {
100
+ console.error('Error:', error instanceof Error ? error.message : String(error));
101
+ }
102
+ try {
103
+ execSync(`git tag -a ${tag} ${tagMessage}`);
104
+ } catch {
105
+ execSync(`git tag -d ${tag}`);
106
+ execSync(`git push origin :refs/tags/${tag}`);
107
+ console.log('Fixed');
108
+ execSync(`git tag -a ${tag} ${tagMessage}`);
109
+ }
110
+ // Ensure Git is synchronized before pushing tag
111
+ await ensureGitSync();
112
+
113
+ execSync(`git push origin ${tag}`);
114
+ console.log(`Release ${tag} pushed to repo.`);
115
+ console.log(dedent`
111
116
  with message:
112
117
  ${toShow}
113
118
  `);