Skip to content

Commit 5fd4ccd

Browse files
committed
add file location to error messages
1 parent dacf6ed commit 5fd4ccd

File tree

7 files changed

+197
-46
lines changed

7 files changed

+197
-46
lines changed

jsEngine/JsMDRC.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import type { JsExecutionContext, JsExecution } from 'jsEngine/engine/JsExecution';
1+
import type { ExecutionContext, JsExecution } from 'jsEngine/engine/JsExecution';
2+
import { ExecutionSource } from 'jsEngine/engine/JsExecution';
23
import { ResultRenderer } from 'jsEngine/engine/ResultRenderer';
34
import type JsEnginePlugin from 'jsEngine/main';
45
import type { MarkdownPostProcessorContext, TAbstractFile } from 'obsidian';
@@ -34,17 +35,21 @@ export class JsMDRC extends MarkdownRenderChild {
3435
}
3536
}
3637

37-
buildExecutionContext(): JsExecutionContext {
38+
buildExecutionContext(): ExecutionContext {
3839
// console.log(this.ctx);
3940
const file = this.getExecutionFile();
41+
if (file === undefined) {
42+
throw new Error('Could not find file for execution context.');
43+
}
4044
return {
45+
executionSource: ExecutionSource.MarkdownCodeBlock,
4146
file: file,
42-
metadata: file === undefined ? undefined : (this.plugin.app.metadataCache.getFileCache(file) ?? undefined),
47+
metadata: this.plugin.app.metadataCache.getFileCache(file) ?? undefined,
4348
block: undefined,
4449
};
4550
}
4651

47-
async tryRun(context: JsExecutionContext): Promise<JsExecution> {
52+
async tryRun(context: ExecutionContext): Promise<JsExecution> {
4853
return this.plugin.jsEngine.execute({
4954
code: this.content,
5055
context: context,

jsEngine/api/InstanceId.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import type { ExecutionContext } from 'jsEngine/engine/JsExecution';
2+
13
export enum InstanceType {
24
JS_EXECUTION = 'JS_EXECUTION',
35
PLUGIN = 'PLUGIN',
@@ -11,10 +13,12 @@ export enum InstanceType {
1113
export class InstanceId {
1214
readonly name: InstanceType | string;
1315
readonly id: string;
16+
readonly executionContext: ExecutionContext | undefined;
1417

15-
constructor(name: InstanceType | string, id: string) {
18+
constructor(name: InstanceType | string, id: string, executionContext?: ExecutionContext) {
1619
this.name = name;
1720
this.id = id;
21+
this.executionContext = executionContext;
1822
}
1923

2024
toString(): string {

jsEngine/api/Internal.ts

Lines changed: 105 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
import type { API } from 'jsEngine/api/API';
22
import type { EngineExecutionParams } from 'jsEngine/engine/Engine';
3-
import type { JsExecution, JsExecutionContext, JsExecutionGlobals, JsExecutionGlobalsConstructionOptions } from 'jsEngine/engine/JsExecution';
3+
import type {
4+
JsExecution,
5+
ExecutionContext,
6+
JsExecutionGlobals,
7+
JsExecutionGlobalsConstructionOptions,
8+
CodeBlockExecutionContext,
9+
JSFileExecutionContext,
10+
UnknownExecutionContext,
11+
} from 'jsEngine/engine/JsExecution';
12+
import { ExecutionSource } from 'jsEngine/engine/JsExecution';
413
import { ResultRenderer } from 'jsEngine/engine/ResultRenderer';
514
import { validateAPIArgs } from 'jsEngine/utils/Validators';
615
import { Component, TFile } from 'obsidian';
@@ -50,16 +59,21 @@ export class InternalAPI {
5059
* @param path
5160
* @param params
5261
*/
53-
public async executeFile(path: string, params: Omit<EngineExecutionParams, 'code'>): Promise<JsExecution> {
54-
validateAPIArgs(z.object({ path: z.string(), params: this.apiInstance.validators.engineExecutionParamsNoCode }), { path, params });
62+
public async executeFile(path: string, params: Omit<EngineExecutionParams, 'code' | 'context'>): Promise<JsExecution> {
63+
validateAPIArgs(z.object({ path: z.string(), params: this.apiInstance.validators.engineExecutionParamsFile }), { path, params });
5564

5665
const file = this.apiInstance.app.vault.getAbstractFileByPath(path);
5766
if (!file || !(file instanceof TFile)) {
5867
throw new Error(`File ${path} not found.`);
5968
}
60-
const fullParams = params as EngineExecutionParams;
61-
fullParams.code = await this.apiInstance.app.vault.read(file);
62-
return await this.execute(fullParams);
69+
return await this.execute({
70+
...params,
71+
code: await this.apiInstance.app.vault.read(file),
72+
context: {
73+
executionSource: ExecutionSource.JSFile,
74+
file: file,
75+
},
76+
});
6377
}
6478

6579
/**
@@ -70,16 +84,19 @@ export class InternalAPI {
7084
* @param path
7185
* @param params
7286
*/
73-
public async executeFileSimple(path: string, params?: Omit<EngineExecutionParams, 'code' | 'component'>): Promise<JsExecution> {
74-
validateAPIArgs(z.object({ path: z.string(), params: this.apiInstance.validators.engineExecutionParamsNoCodeAndComponent.optional() }), {
87+
public async executeFileSimple(path: string, params?: Omit<EngineExecutionParams, 'code' | 'component' | 'context'>): Promise<JsExecution> {
88+
validateAPIArgs(z.object({ path: z.string(), params: this.apiInstance.validators.engineExecutionParamsFileSimple.optional() }), {
7589
path,
7690
params,
7791
});
7892

7993
const component = new Component();
8094
component.load();
8195
try {
82-
return await this.executeFile(path, { component: component, ...params });
96+
return await this.executeFile(path, {
97+
component: component,
98+
...params,
99+
});
83100
} finally {
84101
component.unload();
85102
}
@@ -89,24 +106,102 @@ export class InternalAPI {
89106
* Gets the execution context for a specific file, throws when the file does not exist.
90107
*
91108
* @param path
109+
* @deprecated use {@link getContextForMarkdownCodeBlock}, {@link getContextForJSFile}, or {@link getContextForUnknown} instead
110+
*/
111+
public async getContextForFile(path: string): Promise<ExecutionContext> {
112+
validateAPIArgs(z.object({ path: z.string() }), { path });
113+
114+
const file = this.apiInstance.app.vault.getAbstractFileByPath(path);
115+
if (!file || !(file instanceof TFile)) {
116+
throw new Error(`File ${path} not found.`);
117+
}
118+
119+
const metadata = this.apiInstance.app.metadataCache.getFileCache(file);
120+
121+
return {
122+
executionSource: ExecutionSource.MarkdownCodeBlock,
123+
file: file,
124+
metadata: metadata ?? undefined,
125+
block: undefined,
126+
};
127+
}
128+
129+
/**
130+
* Gets the execution context for a markdown code block.
131+
*
132+
* @param path The file path of the markdown file the code block is in.
133+
* @returns
92134
*/
93-
public async getContextForFile(path: string): Promise<JsExecutionContext> {
135+
public async getContextForMarkdownCodeBlock(path: string): Promise<CodeBlockExecutionContext> {
94136
validateAPIArgs(z.object({ path: z.string() }), { path });
95137

96138
const file = this.apiInstance.app.vault.getAbstractFileByPath(path);
97139
if (!file || !(file instanceof TFile)) {
98140
throw new Error(`File ${path} not found.`);
99141
}
142+
if (file.extension !== 'md' && file.extension !== '.md') {
143+
throw new Error(`File ${path} is not a markdown file. Expected file extension to be ".md".`);
144+
}
100145

101146
const metadata = this.apiInstance.app.metadataCache.getFileCache(file);
102147

103148
return {
149+
executionSource: ExecutionSource.MarkdownCodeBlock,
104150
file: file,
105151
metadata: metadata ?? undefined,
106152
block: undefined,
107153
};
108154
}
109155

156+
/**
157+
* Gets the execution context for a JS file.
158+
*
159+
* @param path The file path of the JS file.
160+
* @returns
161+
*/
162+
public async getContextForJSFile(path: string): Promise<JSFileExecutionContext> {
163+
validateAPIArgs(z.object({ path: z.string() }), { path });
164+
165+
const file = this.apiInstance.app.vault.getAbstractFileByPath(path);
166+
if (!file || !(file instanceof TFile)) {
167+
throw new Error(`File ${path} not found.`);
168+
}
169+
if (file.extension !== 'js' && file.extension !== '.js') {
170+
throw new Error(`File ${path} is not a JS file. Expected file extension to be ".js".`);
171+
}
172+
173+
return {
174+
executionSource: ExecutionSource.JSFile,
175+
file: file,
176+
};
177+
}
178+
179+
/**
180+
* Gets an unknown execution context for anything that is not a markdown code block or a JS file.
181+
*
182+
* @param path An optional file path that will get resolved to a {@link TFile}.
183+
* @returns
184+
*/
185+
public async getContextForUnknown(path?: string): Promise<UnknownExecutionContext> {
186+
validateAPIArgs(z.object({ path: z.string().optional() }), { path });
187+
188+
if (path) {
189+
const file = this.apiInstance.app.vault.getAbstractFileByPath(path);
190+
if (!file || !(file instanceof TFile)) {
191+
throw new Error(`File ${path} not found.`);
192+
}
193+
194+
return {
195+
executionSource: ExecutionSource.Unknown,
196+
file: file,
197+
};
198+
} else {
199+
return {
200+
executionSource: ExecutionSource.Unknown,
201+
};
202+
}
203+
}
204+
110205
/**
111206
* Creates execution globals.
112207
*

jsEngine/engine/Engine.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { ExecutionStatsModal } from 'jsEngine/engine/ExecutionStatsModal';
2-
import type { JsExecutionContext } from 'jsEngine/engine/JsExecution';
2+
import type { ExecutionContext } from 'jsEngine/engine/JsExecution';
33
import { JsExecution } from 'jsEngine/engine/JsExecution';
44
import type JsEnginePlugin from 'jsEngine/main';
55
import type { App, Component } from 'obsidian';
@@ -21,9 +21,9 @@ export interface EngineExecutionParams {
2121
*/
2222
container?: HTMLElement | undefined;
2323
/**
24-
* Optional context to provide to the JavaScript code.
24+
* Context about the location the code was executed from.
2525
*/
26-
context?: JsExecutionContext | undefined;
26+
context: ExecutionContext;
2727
/**
2828
* Optional extra context variables to provide to the JavaScript code.
2929
*/

jsEngine/engine/JsExecution.ts

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,20 @@ import type * as Obsidian from 'obsidian';
1212
*/
1313
export type JsFunc = (...args: unknown[]) => Promise<unknown>;
1414

15-
/**
16-
* Context provided to a {@link JsExecution}.
17-
*/
18-
export interface JsExecutionContext {
15+
export enum ExecutionSource {
16+
MarkdownCodeBlock = 'markdown-code-block',
17+
JSFile = 'js-file',
18+
Unknown = 'unknown',
19+
}
20+
21+
export interface CodeBlockExecutionContext {
22+
executionSource: ExecutionSource.MarkdownCodeBlock;
1923
/**
20-
* The file that the execution was triggered from.
24+
* The file that the code block is in.
2125
*/
22-
file?: TFile | undefined;
26+
file: TFile;
2327
/**
24-
* The metadata of the file that the execution was triggered from.
28+
* The metadata of the file.
2529
*/
2630
metadata?: CachedMetadata | undefined;
2731
/**
@@ -35,6 +39,27 @@ export interface Block {
3539
to: number;
3640
}
3741

42+
export interface JSFileExecutionContext {
43+
executionSource: ExecutionSource.JSFile;
44+
/**
45+
* The JS that is being executed.
46+
*/
47+
file: TFile;
48+
}
49+
50+
export interface UnknownExecutionContext {
51+
executionSource: ExecutionSource.Unknown;
52+
/**
53+
* The file that the execution was triggered from.
54+
*/
55+
file?: TFile | undefined;
56+
}
57+
58+
/**
59+
* Context provided to a {@link JsExecution}.
60+
*/
61+
export type ExecutionContext = CodeBlockExecutionContext | JSFileExecutionContext | UnknownExecutionContext;
62+
3863
/**
3964
* Global variables provided to a {@link JsExecution}.
4065
*/
@@ -54,7 +79,7 @@ export interface JsExecutionGlobals {
5479
/**
5580
* The context provided. This can be undefined and extended by other properties.
5681
*/
57-
context: (JsExecutionContext | undefined) & Record<string, unknown>;
82+
context: ExecutionContext & Record<string, unknown>;
5883
/**
5984
* The container element that the execution can render to. This can be undefined.
6085
*/
@@ -81,7 +106,7 @@ export interface JsExecutionGlobalsConstructionOptions {
81106
/**
82107
* The context provided. This can be undefined and extended by other properties.
83108
*/
84-
context: (JsExecutionContext | undefined) & Record<string, unknown>;
109+
context: ExecutionContext & Record<string, unknown>;
85110
/**
86111
* The container element that the execution can render to. This can be undefined.
87112
*/
@@ -103,7 +128,7 @@ export class JsExecution {
103128
readonly app: App;
104129
readonly plugin: JsEnginePlugin;
105130

106-
private readonly context: (JsExecutionContext | undefined) & Record<string, unknown>;
131+
private readonly context: (ExecutionContext | undefined) & Record<string, unknown>;
107132
private readonly apiInstance: API;
108133
private messages: MessageWrapper[];
109134
private func: JsFunc | undefined;
@@ -124,10 +149,13 @@ export class JsExecution {
124149
this.plugin = params.plugin;
125150

126151
this.code = params.code;
127-
this.context = Object.assign({}, params.context, params.contextOverrides);
152+
this.context = {
153+
...params.context,
154+
...params.contextOverrides,
155+
};
128156

129157
this.uuid = self.crypto.randomUUID();
130-
this.apiInstance = new API(this.app, this.plugin, new InstanceId(InstanceType.JS_EXECUTION, this.uuid));
158+
this.apiInstance = new API(this.app, this.plugin, new InstanceId(InstanceType.JS_EXECUTION, this.uuid, params.context));
131159
this.messages = [];
132160

133161
this.func = undefined;

jsEngine/messages/MessageComponent.svelte

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
showMessageSource?: boolean;
1616
} = $props();
1717
18+
console.log(messageWrapper.source.executionContext);
19+
1820
let showMore: boolean = $state(false);
1921
</script>
2022

@@ -63,6 +65,11 @@
6365
{#if showMessageSource}
6466
<p>Source Name: {messageWrapper.source.name}</p>
6567
<p>Source ID: {messageWrapper.source.id}</p>
68+
{@const executionContext = messageWrapper.source.executionContext}
69+
{#if executionContext}
70+
<p>Execution Context Type: {executionContext.executionSource}</p>
71+
<p>Execution Context File: {executionContext.file?.path ?? 'none'}</p>
72+
{/if}
6673
{/if}
6774
</div>
6875
{/if}

0 commit comments

Comments
 (0)