Skip to content

Commit 107fd9d

Browse files
author
Akos Kitta
committed
fix: workaround for # in the app path
Closes eclipse-theia/theia#12064 Signed-off-by: Akos Kitta <[email protected]>
1 parent 26d3963 commit 107fd9d

File tree

2 files changed

+111
-0
lines changed

2 files changed

+111
-0
lines changed

Diff for: arduino-ide-extension/src/node/arduino-ide-backend-module.ts

+11
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,11 @@ import { MessagingContribution } from './theia/core/messaging-contribution';
113113
import { MessagingService } from '@theia/core/lib/node/messaging/messaging-service';
114114
import { HostedPluginReader } from './theia/plugin-ext/plugin-reader';
115115
import { HostedPluginReader as TheiaHostedPluginReader } from '@theia/plugin-ext/lib/hosted/node/plugin-reader';
116+
import { PluginDeployer } from '@theia/plugin-ext/lib/common/plugin-protocol';
117+
import {
118+
LocalDirectoryPluginDeployerResolverWithFallback,
119+
PluginDeployer_GH_12064,
120+
} from './theia/plugin-ext/plugin-deployer';
116121

117122
export default new ContainerModule((bind, unbind, isBound, rebind) => {
118123
bind(BackendApplication).toSelf().inSingletonScope();
@@ -392,6 +397,12 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
392397
// https://github.com/arduino/arduino-ide/pull/1706#pullrequestreview-1195595080
393398
bind(HostedPluginReader).toSelf().inSingletonScope();
394399
rebind(TheiaHostedPluginReader).toService(HostedPluginReader);
400+
401+
// https://github.com/eclipse-theia/theia/issues/12064
402+
bind(LocalDirectoryPluginDeployerResolverWithFallback)
403+
.toSelf()
404+
.inSingletonScope();
405+
rebind(PluginDeployer).to(PluginDeployer_GH_12064).inSingletonScope();
395406
});
396407

397408
function bindChildLogger(bind: interfaces.Bind, name: string): void {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import { URI } from '@theia/core/lib/common/uri';
2+
import {
3+
inject,
4+
injectable,
5+
postConstruct,
6+
} from '@theia/core/shared/inversify';
7+
import {
8+
PluginDeployerResolver,
9+
PluginDeployerResolverContext,
10+
} from '@theia/plugin-ext/lib/common/plugin-protocol';
11+
import { PluginDeployerImpl } from '@theia/plugin-ext/lib/main/node/plugin-deployer-impl';
12+
import { LocalDirectoryPluginDeployerResolver } from '@theia/plugin-ext/lib/main/node/resolvers/local-directory-plugin-deployer-resolver';
13+
import { constants, promises as fs } from 'fs';
14+
import { isAbsolute, resolve } from 'path';
15+
16+
@injectable()
17+
export class LocalDirectoryPluginDeployerResolverWithFallback extends LocalDirectoryPluginDeployerResolver {
18+
override async resolve(
19+
pluginResolverContext: PluginDeployerResolverContext
20+
): Promise<void> {
21+
const origin = pluginResolverContext.getOriginId();
22+
// The original implementation must not run when there is a hash in the path. Otherwise, it can resolve an undesired directory.
23+
// Consider app under c:\Users\username\Desktop\# here is my app\
24+
// Then the flawed logic will incorrectly find c:\Users\username\Desktop location after stripping the rest of the path after the hash.
25+
// The implementation which provides a workaround for the hash in the path assumes that the original Theia logic is correct, when no hash present in the URI path.
26+
let localPath: string | null;
27+
if (origin.includes('#')) {
28+
localPath = await resolveLocalPluginPathFallback(
29+
pluginResolverContext,
30+
this.supportedScheme
31+
);
32+
} else {
33+
localPath = await this.originalResolveLocalPluginPath(
34+
pluginResolverContext,
35+
this.supportedScheme
36+
);
37+
}
38+
if (localPath) {
39+
await this.resolveFromLocalPath(pluginResolverContext, localPath);
40+
}
41+
}
42+
43+
private async originalResolveLocalPluginPath(
44+
context: PluginDeployerResolverContext,
45+
scheme: string
46+
): Promise<string | null> {
47+
const object = <Record<string, unknown>>this;
48+
if (
49+
'resolveLocalPluginPath' in object &&
50+
typeof object['resolveLocalPluginPath'] === 'function'
51+
) {
52+
return object['resolveLocalPluginPath'](context, scheme);
53+
}
54+
return null;
55+
}
56+
}
57+
58+
async function resolveLocalPluginPathFallback(
59+
context: PluginDeployerResolverContext,
60+
scheme: string
61+
): Promise<string | null> {
62+
const uri = new URI(context.getOriginId());
63+
if (uri.scheme === scheme) {
64+
const unencodedRawUri = uri.toString(true);
65+
let fsPath = unencodedRawUri.substring(`${scheme}:`.length);
66+
if (!isAbsolute(fsPath)) {
67+
fsPath = resolve(process.cwd(), fsPath);
68+
}
69+
try {
70+
await fs.access(fsPath, constants.R_OK);
71+
return fsPath;
72+
} catch {
73+
console.warn(
74+
`The local plugin referenced by ${context.getOriginId()} does not exist.`
75+
);
76+
}
77+
}
78+
return null;
79+
}
80+
81+
@injectable()
82+
export class PluginDeployer_GH_12064 extends PluginDeployerImpl {
83+
@inject(LocalDirectoryPluginDeployerResolverWithFallback)
84+
private readonly pluginResolver: LocalDirectoryPluginDeployerResolverWithFallback;
85+
86+
@postConstruct()
87+
protected adjustPluginResolvers(): void {
88+
const pluginResolvers = <PluginDeployerResolver[]>(
89+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
90+
(this as any).pluginResolvers
91+
);
92+
const index = pluginResolvers.findIndex(
93+
(pluginResolver) =>
94+
pluginResolver instanceof LocalDirectoryPluginDeployerResolver
95+
);
96+
if (index >= 0) {
97+
pluginResolvers.splice(index, 1, this.pluginResolver);
98+
}
99+
}
100+
}

0 commit comments

Comments
 (0)