Fix code quality violations and add VS Code extension features

Fix VS Code extension storage paths for new directory structure
Fix jqhtml compiled files missing from bundle
Fix bundle babel transformation and add rsxrealpath() function

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
root
2025-10-22 00:43:05 +00:00
parent 53d359bc91
commit 37a6183eb4
80 changed files with 1066 additions and 255 deletions

View File

@@ -28,7 +28,7 @@ export class RspadeDefinitionProvider implements vscode.DefinitionProvider {
}
/**
* Find the RSpade project root folder (contains app/RSpade/)
* Find the RSpade project root folder (contains system/app/RSpade/)
* Works in both single-folder and multi-root workspace modes
*/
private find_rspade_root(): string | undefined {
@@ -36,9 +36,9 @@ export class RspadeDefinitionProvider implements vscode.DefinitionProvider {
return undefined;
}
// Check each workspace folder for app/RSpade/
// Check each workspace folder for system/app/RSpade/
for (const folder of vscode.workspace.workspaceFolders) {
const app_rspade = path.join(folder.uri.fsPath, 'app', 'RSpade');
const app_rspade = path.join(folder.uri.fsPath, 'system', 'app', 'RSpade');
if (fs.existsSync(app_rspade)) {
return folder.uri.fsPath;
}
@@ -86,6 +86,14 @@ export class RspadeDefinitionProvider implements vscode.DefinitionProvider {
return routeResult;
}
// Check for href="/" pattern in Blade/Jqhtml files
if (fileName.endsWith('.blade.php') || fileName.endsWith('.jqhtml')) {
const hrefResult = await this.handleHrefPattern(document, position);
if (hrefResult) {
return hrefResult;
}
}
// Handle "this.xxx" references in .jqhtml files (highest priority for jqhtml files)
if (fileName.endsWith('.jqhtml')) {
const thisResult = await this.handleThisReference(document, position);
@@ -206,6 +214,45 @@ export class RspadeDefinitionProvider implements vscode.DefinitionProvider {
return undefined;
}
/**
* Handle href="/" pattern in Blade/Jqhtml files
* Detects when cursor is on "/" within href attribute
* Resolves to the controller action that handles the root URL
*/
private async handleHrefPattern(
document: vscode.TextDocument,
position: vscode.Position
): Promise<vscode.Definition | undefined> {
const line = document.lineAt(position.line).text;
// Match href="/" or href='/'
const hrefPattern = /href\s*=\s*(['"])\/\1/g;
let match;
while ((match = hrefPattern.exec(line)) !== null) {
const matchStart = match.index + match[0].indexOf('/');
const matchEnd = matchStart + 1; // Just the "/" character
// Check if cursor is on the "/"
if (position.character >= matchStart && position.character <= matchEnd) {
try {
// Query IDE bridge to resolve "/" URL to route
const result = await this.ide_bridge.queryUrl('/');
if (result && result.found && result.controller && result.method) {
// Resolved to controller/method - navigate to it
const phpResult = await this.queryIdeHelper(result.controller, result.method, 'class');
return this.createLocationFromResult(phpResult);
}
} catch (error) {
console.error('Error resolving href="/" to route:', error);
}
}
}
return undefined;
}
/**
* Handle "this.xxx" references in .jqhtml files
* Only handles patterns where cursor is on a word after "this."
@@ -488,12 +535,8 @@ export class RspadeDefinitionProvider implements vscode.DefinitionProvider {
if (isRsxView && stringContent) {
// Query as a view
try {
const result = await this.queryIdeHelper(stringContent, undefined, 'view');
return this.createLocationFromResult(result);
} catch (error) {
console.error('Error querying IDE helper for view:', error);
}
const result = await this.queryIdeHelper(stringContent, undefined, 'view');
return this.createLocationFromResult(result);
}
}
@@ -641,7 +684,7 @@ export class RspadeDefinitionProvider implements vscode.DefinitionProvider {
}
try {
const result = await this.ide_bridge.request('/_idehelper', params);
const result = await this.ide_bridge.request('/_ide/service/resolve_class', params);
return result;
} catch (error: any) {
this.show_error_status('IDE helper request failed');

View File

@@ -5,7 +5,7 @@
* All formatting is performed on the server - no local PHP execution.
*
* Authentication Flow:
* 1. Reads domain from storage/rsx-ide-bridge/domain.txt (auto-discovered)
* 1. Reads domain from system/storage/rsx-ide-bridge/domain.txt (auto-discovered)
* 2. Creates session with auth tokens on first use
* 3. Signs all requests with SHA1(body + client_key)
* 4. Validates server responses with SHA1(body + server_key)
@@ -321,7 +321,7 @@ export class RspadeFormattingProvider implements vscode.DocumentFormattingEditPr
// Try new structure first
const system_app_rspade = path.join(folder.uri.fsPath, 'system', 'app', 'RSpade');
if (fs.existsSync(system_app_rspade)) {
rspade_root = path.join(folder.uri.fsPath, 'system');
rspade_root = folder.uri.fsPath; // Project root, not system subdirectory
break;
}
@@ -339,7 +339,7 @@ export class RspadeFormattingProvider implements vscode.DocumentFormattingEditPr
throw new Error('RSpade project root not found');
}
const domain_file = path.join(rspade_root, 'storage', 'rsx-ide-bridge', 'domain.txt');
const domain_file = path.join(rspade_root, 'system', 'storage', 'rsx-ide-bridge', 'domain.txt');
this.output_channel.appendLine(`Checking for domain file: ${domain_file}`);
if (await exists(domain_file)) {
@@ -357,7 +357,7 @@ export class RspadeFormattingProvider implements vscode.DocumentFormattingEditPr
this.output_channel.appendLine('\nThe extension needs to know your development server URL.');
this.output_channel.appendLine('\nPlease do ONE of the following:\n');
this.output_channel.appendLine('1. Load your site in a web browser');
this.output_channel.appendLine(' This will auto-create: storage/rsx-ide-bridge/domain.txt\n');
this.output_channel.appendLine(' This will auto-create: system/storage/rsx-ide-bridge/domain.txt\n');
this.output_channel.appendLine('2. Set VS Code setting: rspade.serverUrl');
this.output_channel.appendLine(' File → Preferences → Settings → Search "rspade"');
this.output_channel.appendLine(' Set to your development URL (e.g., https://myapp.test)\n');
@@ -365,7 +365,7 @@ export class RspadeFormattingProvider implements vscode.DocumentFormattingEditPr
this.output_channel.appendLine(' In config/rsx.php, set ide_integration.enabled = true');
this.output_channel.appendLine('\n════════════════════════════════════════════════════════');
throw new Error('RSpade: storage/rsx-ide-bridge/domain.txt not found. Please load site in browser or configure server URL.');
throw new Error('RSpade: system/storage/rsx-ide-bridge/domain.txt not found. Please load site in browser or configure server URL.');
}
private async make_authenticated_request(

View File

@@ -265,7 +265,7 @@ export class GitDiffProvider {
return this.server_url;
}
const domain_file = path.join(this.rspade_root!, 'storage', 'rsx-ide-bridge', 'domain.txt');
const domain_file = path.join(this.rspade_root!, 'system', 'storage', 'rsx-ide-bridge', 'domain.txt');
if (fs.existsSync(domain_file)) {
const domain = fs.readFileSync(domain_file, 'utf8').trim();
@@ -380,7 +380,7 @@ export class GitDiffProvider {
return;
}
const domain_file = path.join(this.rspade_root, 'storage', 'rsx-ide-bridge', 'domain.txt');
const domain_file = path.join(this.rspade_root, 'system', 'storage', 'rsx-ide-bridge', 'domain.txt');
const domain_dir = path.dirname(domain_file);
// Watch the directory (file might not exist yet)

View File

@@ -134,7 +134,7 @@ export class GitStatusProvider implements vscode.FileDecorationProvider {
}
// Try to auto-discover from domain.txt
const domain_file = path.join(this.rspade_root!, 'storage', 'rsx-ide-bridge', 'domain.txt');
const domain_file = path.join(this.rspade_root!, 'system', 'storage', 'rsx-ide-bridge', 'domain.txt');
if (fs.existsSync(domain_file)) {
const domain = fs.readFileSync(domain_file, 'utf8').trim();
@@ -249,7 +249,7 @@ export class GitStatusProvider implements vscode.FileDecorationProvider {
return;
}
const domain_file = path.join(this.rspade_root, 'storage', 'rsx-ide-bridge', 'domain.txt');
const domain_file = path.join(this.rspade_root, 'system', 'storage', 'rsx-ide-bridge', 'domain.txt');
const domain_dir = path.dirname(domain_file);
// Watch the directory (file might not exist yet)

View File

@@ -10,7 +10,7 @@
* 4. Auto-retries with refreshed URL on connection failure
*
* AUTHENTICATION FLOW:
* 1. Client requests session from /_idehelper/auth/create
* 1. Client requests session from /_ide/service/auth/create
* 2. Server generates session ID, client_key, server_key
* 3. Client signs requests: SHA1(body + client_key)
* 4. Server validates signature and signs response: SHA1(body + server_key)
@@ -31,7 +31,7 @@
* const client = new IdeBridgeClient(output_channel);
*
* // Make request to IDE helper endpoint
* const response = await client.request('/_idehelper/your_endpoint', {
* const response = await client.request('/_ide/service/your_endpoint', {
* param1: 'value1',
* param2: 'value2'
* });
@@ -48,10 +48,10 @@
* ADDING NEW IDE HELPER ENDPOINTS:
*
* Backend (PHP):
* 1. Add method to /app/RSpade/Ide/Helper/Ide_Helper_Controller.php
* 2. Register route in /routes/web.php:
* Route::get('/_idehelper/your_endpoint', [Ide_Helper_Controller::class, 'your_method']);
* 3. Return JsonResponse with data
* 1. Add handler function to /app/RSpade/Ide/Services/handler.php
* 2. Add case to switch statement in handler.php:
* case 'your_endpoint': handle_your_endpoint_service($request_data); break;
* 3. Return JSON via json_response() helper
*
* Frontend (TypeScript):
* 1. Use IdeBridgeClient.request() to call endpoint
@@ -109,6 +109,16 @@ export class IdeBridgeClient {
return this.make_request_with_retry(endpoint, data, method, 0);
}
/**
* Query URL to resolve it to a route (controller/method)
*
* @param url The URL path to resolve (e.g., "/", "/users/123")
* @returns Promise with { found: boolean, controller?: string, method?: string }
*/
public async queryUrl(url: string): Promise<{ found: boolean; controller?: string; method?: string }> {
return this.request('/_ide/service/resolve_url', { url }, 'GET');
}
private async make_request_with_retry(
endpoint: string,
data: any,
@@ -309,7 +319,7 @@ export class IdeBridgeClient {
this.output_channel.appendLine('Creating new auth session...');
// Request new session (this endpoint doesn't require auth)
const response = await this.make_http_request('/auth/create', {}, 'POST', false);
const response = await this.make_http_request('/_ide/service/auth/create', {}, 'POST', false);
if (!response.success) {
throw new Error('Failed to create auth session');

View File

@@ -59,7 +59,7 @@ async function get_js_lineage(class_name: string): Promise<string[]> {
}
try {
const response = await ide_bridge_client.request('/_idehelper/js_lineage', { class: class_name });
const response = await ide_bridge_client.request('/_ide/service/js_lineage', { class: class_name });
const lineage = response.lineage || [];
// Cache the result