zed-ets-language-server 2.3.4 → 3.1.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.
- package/index.js +142 -9
- package/lib/data-parser.js +17 -12
- package/lib/lib-expander.js +11 -3
- package/lib/sdk-discovery.js +182 -0
- package/lib/sdk-discovery.test.js +252 -0
- package/package.json +3 -3
- package/tests/integration/kit-diagnostics.test.js +232 -0
- package/tests/integration/lsp-server.test.js +148 -68
- package/tests/integration/sdk-initialization.test.js +167 -0
- package/tests/mocks/mock-ets-server.js +4 -2
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it } from 'vitest';
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { listHelperPaths } from './lib-expander.js';
|
|
6
|
+
import { resolveHmsSdkPath, resolveOhosSdkPath } from './sdk-discovery.js';
|
|
7
|
+
|
|
8
|
+
const temporaryDirectories = [];
|
|
9
|
+
|
|
10
|
+
async function createSdkRoot(directory, relativePath) {
|
|
11
|
+
const sdkRoot = path.join(directory, relativePath);
|
|
12
|
+
await fs.mkdir(path.join(sdkRoot, 'ets', 'api'), { recursive: true });
|
|
13
|
+
await fs.mkdir(path.join(sdkRoot, 'ets', 'kits'), { recursive: true });
|
|
14
|
+
await fs.mkdir(path.join(sdkRoot, 'ets', 'component'), { recursive: true });
|
|
15
|
+
await fs.mkdir(path.join(sdkRoot, 'ets', 'build-tools', 'ets-loader', 'declarations'), {
|
|
16
|
+
recursive: true,
|
|
17
|
+
});
|
|
18
|
+
await fs.writeFile(
|
|
19
|
+
path.join(sdkRoot, 'ets', 'build-tools', 'ets-loader', 'tsconfig.json'),
|
|
20
|
+
'{}\n',
|
|
21
|
+
);
|
|
22
|
+
await fs.writeFile(
|
|
23
|
+
path.join(sdkRoot, 'ets', 'kits', '@kit.AbilityKit.d.ts'),
|
|
24
|
+
'export declare class UIAbility {}\n',
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
return sdkRoot;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function createSdkFixture() {
|
|
31
|
+
const directory = await fs.mkdtemp(path.join(os.tmpdir(), 'zed-arkts-sdk-'));
|
|
32
|
+
temporaryDirectories.push(directory);
|
|
33
|
+
const sdkRoot = await createSdkRoot(directory, path.join('sdk', 'default', 'openharmony'));
|
|
34
|
+
return { directory, sdkRoot };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async function createHmsSdkRoot(ohosSdkRoot) {
|
|
38
|
+
const hmsSdkRoot = path.join(path.dirname(ohosSdkRoot), 'hms');
|
|
39
|
+
await fs.mkdir(path.join(hmsSdkRoot, 'ets', 'api'), { recursive: true });
|
|
40
|
+
await fs.mkdir(path.join(hmsSdkRoot, 'ets', 'kits'), { recursive: true });
|
|
41
|
+
await fs.writeFile(
|
|
42
|
+
path.join(hmsSdkRoot, 'ets', 'kits', '@kit.TestHmsKit.d.ts'),
|
|
43
|
+
'export declare const hmsApi: string;\n',
|
|
44
|
+
);
|
|
45
|
+
return hmsSdkRoot;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
afterEach(async () => {
|
|
49
|
+
await Promise.all(
|
|
50
|
+
temporaryDirectories.splice(0).map((directory) =>
|
|
51
|
+
fs.rm(directory, { recursive: true, force: true }),
|
|
52
|
+
),
|
|
53
|
+
);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
describe('HarmonyOS SDK discovery', () => {
|
|
57
|
+
it('normalizes a DevEco SDK directory to its OpenHarmony root', async () => {
|
|
58
|
+
const { directory, sdkRoot } = await createSdkFixture();
|
|
59
|
+
|
|
60
|
+
const result = resolveOhosSdkPath({
|
|
61
|
+
configuredPath: path.join(directory, 'sdk', 'default'),
|
|
62
|
+
env: {},
|
|
63
|
+
candidates: [],
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
expect(result).toEqual({ path: sdkRoot, source: 'settings' });
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('prefers the explicitly configured SDK over environment values', async () => {
|
|
70
|
+
const configured = await createSdkFixture();
|
|
71
|
+
const fromEnvironment = await createSdkFixture();
|
|
72
|
+
|
|
73
|
+
const result = resolveOhosSdkPath({
|
|
74
|
+
configuredPath: configured.sdkRoot,
|
|
75
|
+
env: { ZED_ETS_OHOS_SDK_PATH: fromEnvironment.sdkRoot },
|
|
76
|
+
candidates: [],
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
expect(result).toEqual({ path: configured.sdkRoot, source: 'settings' });
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('expands a configured OpenHarmony SDK path relative to the supplied home', async () => {
|
|
83
|
+
const homeDirectory = await fs.mkdtemp(path.join(os.tmpdir(), 'zed-arkts-tilde-ohos-'));
|
|
84
|
+
temporaryDirectories.push(homeDirectory);
|
|
85
|
+
const sdkRoot = await createSdkRoot(
|
|
86
|
+
homeDirectory,
|
|
87
|
+
path.join('HarmonySdk', 'default', 'openharmony'),
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
const result = resolveOhosSdkPath({
|
|
91
|
+
configuredPath: '~/HarmonySdk',
|
|
92
|
+
env: {},
|
|
93
|
+
candidates: [],
|
|
94
|
+
homeDirectory,
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
expect(result).toEqual({ path: sdkRoot, source: 'settings' });
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('discovers the SDK bundled with DevEco Studio', async () => {
|
|
101
|
+
const { directory, sdkRoot } = await createSdkFixture();
|
|
102
|
+
|
|
103
|
+
const result = resolveOhosSdkPath({
|
|
104
|
+
env: {},
|
|
105
|
+
candidates: [path.join(directory, 'sdk')],
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
expect(result).toEqual({ path: sdkRoot, source: 'auto-detected' });
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it.each([
|
|
112
|
+
['OpenHarmony', path.join('Library', 'OpenHarmony', 'Sdk')],
|
|
113
|
+
['Huawei', path.join('Library', 'Huawei', 'Sdk')],
|
|
114
|
+
])('discovers the macOS %s user SDK directory', async (_vendor, sdkDirectory) => {
|
|
115
|
+
const homeDirectory = await fs.mkdtemp(path.join(os.tmpdir(), 'zed-arkts-home-sdk-'));
|
|
116
|
+
temporaryDirectories.push(homeDirectory);
|
|
117
|
+
const sdkRoot = await createSdkRoot(
|
|
118
|
+
homeDirectory,
|
|
119
|
+
path.join(sdkDirectory, 'default', 'openharmony'),
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
const result = resolveOhosSdkPath({
|
|
123
|
+
env: {},
|
|
124
|
+
platform: 'darwin',
|
|
125
|
+
homeDirectory,
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
expect(result).toEqual({ path: sdkRoot, source: 'auto-detected' });
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('selects the newest installed version when no default SDK exists', async () => {
|
|
132
|
+
const directory = await fs.mkdtemp(path.join(os.tmpdir(), 'zed-arkts-versioned-sdk-'));
|
|
133
|
+
temporaryDirectories.push(directory);
|
|
134
|
+
await createSdkRoot(directory, path.join('sdk', '12', 'openharmony'));
|
|
135
|
+
const newestSdk = await createSdkRoot(directory, path.join('sdk', '24', 'openharmony'));
|
|
136
|
+
|
|
137
|
+
const result = resolveOhosSdkPath({
|
|
138
|
+
env: {},
|
|
139
|
+
candidates: [path.join(directory, 'sdk')],
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
expect(result).toEqual({ path: newestSdk, source: 'auto-detected' });
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('rejects a path that does not contain ArkTS SDK declarations', async () => {
|
|
146
|
+
const directory = await fs.mkdtemp(path.join(os.tmpdir(), 'zed-arkts-invalid-sdk-'));
|
|
147
|
+
temporaryDirectories.push(directory);
|
|
148
|
+
|
|
149
|
+
expect(() =>
|
|
150
|
+
resolveOhosSdkPath({
|
|
151
|
+
configuredPath: directory,
|
|
152
|
+
env: {},
|
|
153
|
+
candidates: [],
|
|
154
|
+
}),
|
|
155
|
+
).toThrow(/ets\/kits.*ets-loader\/tsconfig\.json/);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it('rejects SDK entries with the wrong file types', async () => {
|
|
159
|
+
const directory = await fs.mkdtemp(path.join(os.tmpdir(), 'zed-arkts-malformed-sdk-'));
|
|
160
|
+
temporaryDirectories.push(directory);
|
|
161
|
+
const sdkRoot = path.join(directory, 'openharmony');
|
|
162
|
+
await fs.mkdir(path.join(sdkRoot, 'ets', 'api'), { recursive: true });
|
|
163
|
+
await fs.writeFile(path.join(sdkRoot, 'ets', 'kits'), 'not a directory\n');
|
|
164
|
+
await fs.mkdir(path.join(sdkRoot, 'ets', 'build-tools', 'ets-loader', 'tsconfig.json'), {
|
|
165
|
+
recursive: true,
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
expect(() =>
|
|
169
|
+
resolveOhosSdkPath({ configuredPath: sdkRoot, env: {}, candidates: [] }),
|
|
170
|
+
).toThrow(/Invalid HarmonyOS SDK path/);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it.runIf(process.platform !== 'win32')('rejects SDK directories that cannot be traversed', async () => {
|
|
174
|
+
const { sdkRoot } = await createSdkFixture();
|
|
175
|
+
const apiDirectory = path.join(sdkRoot, 'ets', 'api');
|
|
176
|
+
await fs.chmod(apiDirectory, 0o400);
|
|
177
|
+
|
|
178
|
+
expect(() =>
|
|
179
|
+
resolveOhosSdkPath({ configuredPath: sdkRoot, env: {}, candidates: [] }),
|
|
180
|
+
).toThrow(/Invalid HarmonyOS SDK path/);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it('discovers a sibling HMS SDK and adds it to module resolution paths', async () => {
|
|
184
|
+
const { directory, sdkRoot } = await createSdkFixture();
|
|
185
|
+
const hmsSdkRoot = await createHmsSdkRoot(sdkRoot);
|
|
186
|
+
const tsdk = path.join(directory, 'typescript', 'lib');
|
|
187
|
+
await fs.mkdir(tsdk, { recursive: true });
|
|
188
|
+
await fs.writeFile(path.join(tsdk, 'lib.es5.d.ts'), 'interface Object {}\n');
|
|
189
|
+
|
|
190
|
+
const hmsResult = resolveHmsSdkPath({ ohosSdkPath: sdkRoot, env: {} });
|
|
191
|
+
const helperPaths = await listHelperPaths(tsdk, sdkRoot, hmsResult.path);
|
|
192
|
+
|
|
193
|
+
expect(hmsResult).toEqual({ path: hmsSdkRoot, source: 'auto-detected' });
|
|
194
|
+
expect(helperPaths.hmsSdkPath).toBe(hmsSdkRoot);
|
|
195
|
+
expect(helperPaths.paths['*']).toContain(path.join(hmsSdkRoot, 'ets', 'kits', '*'));
|
|
196
|
+
expect(helperPaths.paths['*']).toContain(path.join(hmsSdkRoot, 'ets', 'api', '*'));
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it('expands a configured HMS SDK path relative to the supplied home', async () => {
|
|
200
|
+
const homeDirectory = await fs.mkdtemp(path.join(os.tmpdir(), 'zed-arkts-tilde-hms-'));
|
|
201
|
+
temporaryDirectories.push(homeDirectory);
|
|
202
|
+
const hmsSdkRoot = path.join(homeDirectory, 'HarmonySdk', 'default', 'hms');
|
|
203
|
+
await fs.mkdir(path.join(hmsSdkRoot, 'ets', 'api'), { recursive: true });
|
|
204
|
+
await fs.mkdir(path.join(hmsSdkRoot, 'ets', 'kits'), { recursive: true });
|
|
205
|
+
|
|
206
|
+
const result = resolveHmsSdkPath({
|
|
207
|
+
configuredPath: '~/HarmonySdk/default/hms',
|
|
208
|
+
env: {},
|
|
209
|
+
homeDirectory,
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
expect(result).toEqual({ path: hmsSdkRoot, source: 'settings' });
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it('resolves OpenHarmony and HMS kit modules through the generated paths', async () => {
|
|
216
|
+
const typescriptModule = await import('ohos-typescript');
|
|
217
|
+
const ts = typescriptModule.default ?? typescriptModule;
|
|
218
|
+
const { directory, sdkRoot } = await createSdkFixture();
|
|
219
|
+
const hmsSdkRoot = await createHmsSdkRoot(sdkRoot);
|
|
220
|
+
const helperPaths = await listHelperPaths(
|
|
221
|
+
path.join(process.cwd(), 'node_modules', 'ohos-typescript', 'lib'),
|
|
222
|
+
sdkRoot,
|
|
223
|
+
hmsSdkRoot,
|
|
224
|
+
);
|
|
225
|
+
const compilerOptions = {
|
|
226
|
+
moduleResolution: ts.ModuleResolutionKind.NodeJs,
|
|
227
|
+
baseUrl: helperPaths.baseUrl,
|
|
228
|
+
paths: helperPaths.paths,
|
|
229
|
+
};
|
|
230
|
+
const containingFile = path.join(directory, 'entry', 'src', 'main', 'ets', 'Probe.ets');
|
|
231
|
+
|
|
232
|
+
const abilityKit = ts.resolveModuleName(
|
|
233
|
+
'@kit.AbilityKit',
|
|
234
|
+
containingFile,
|
|
235
|
+
compilerOptions,
|
|
236
|
+
ts.sys,
|
|
237
|
+
).resolvedModule;
|
|
238
|
+
const hmsKit = ts.resolveModuleName(
|
|
239
|
+
'@kit.TestHmsKit',
|
|
240
|
+
containingFile,
|
|
241
|
+
compilerOptions,
|
|
242
|
+
ts.sys,
|
|
243
|
+
).resolvedModule;
|
|
244
|
+
|
|
245
|
+
expect(abilityKit?.resolvedFileName).toBe(
|
|
246
|
+
path.join(sdkRoot, 'ets', 'kits', '@kit.AbilityKit.d.ts'),
|
|
247
|
+
);
|
|
248
|
+
expect(hmsKit?.resolvedFileName).toBe(
|
|
249
|
+
path.join(hmsSdkRoot, 'ets', 'kits', '@kit.TestHmsKit.d.ts'),
|
|
250
|
+
);
|
|
251
|
+
});
|
|
252
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zed-ets-language-server",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"description": "ETS language server wrapper for Zed ArkTS extension.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -17,9 +17,9 @@
|
|
|
17
17
|
"author": "liuyanghejerry <liuyanghejerry@126.com>",
|
|
18
18
|
"license": "MIT",
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@arkts/language-server": "^1.
|
|
20
|
+
"@arkts/language-server": "^1.3.10"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
|
-
"vitest": "^4.
|
|
23
|
+
"vitest": "^4.1.11"
|
|
24
24
|
}
|
|
25
25
|
}
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
|
|
2
|
+
import { spawn } from 'node:child_process';
|
|
3
|
+
import fs from 'node:fs/promises';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
7
|
+
|
|
8
|
+
const testDirectory = path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
let fixtureDirectory;
|
|
10
|
+
let projectDirectory;
|
|
11
|
+
let sourcePath;
|
|
12
|
+
let serverProcess;
|
|
13
|
+
let languageServerVersion;
|
|
14
|
+
let stdoutBuffer = Buffer.alloc(0);
|
|
15
|
+
const messages = [];
|
|
16
|
+
const waiters = [];
|
|
17
|
+
|
|
18
|
+
function send(message) {
|
|
19
|
+
const body = Buffer.from(JSON.stringify(message));
|
|
20
|
+
serverProcess.stdin.write(
|
|
21
|
+
Buffer.concat([Buffer.from(`Content-Length: ${body.length}\r\n\r\n`), body]),
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function dispatch(message) {
|
|
26
|
+
messages.push(message);
|
|
27
|
+
|
|
28
|
+
if (message.method && message.id !== undefined) {
|
|
29
|
+
let result = null;
|
|
30
|
+
if (message.method === 'workspace/configuration') {
|
|
31
|
+
result = (message.params?.items ?? []).map(() => null);
|
|
32
|
+
} else if (message.method === 'workspace/workspaceFolders') {
|
|
33
|
+
result = [{ uri: pathToFileURL(projectDirectory).href, name: 'project' }];
|
|
34
|
+
}
|
|
35
|
+
send({ jsonrpc: '2.0', id: message.id, result });
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
for (let index = waiters.length - 1; index >= 0; index -= 1) {
|
|
39
|
+
if (!waiters[index].predicate(message)) continue;
|
|
40
|
+
const [waiter] = waiters.splice(index, 1);
|
|
41
|
+
clearTimeout(waiter.timer);
|
|
42
|
+
waiter.resolve(message);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function collectMessages(chunk) {
|
|
47
|
+
stdoutBuffer = Buffer.concat([stdoutBuffer, chunk]);
|
|
48
|
+
while (true) {
|
|
49
|
+
const headerEnd = stdoutBuffer.indexOf('\r\n\r\n');
|
|
50
|
+
if (headerEnd === -1) return;
|
|
51
|
+
const header = stdoutBuffer.subarray(0, headerEnd).toString('ascii');
|
|
52
|
+
const lengthMatch = header.match(/Content-Length:\s*(\d+)/i);
|
|
53
|
+
if (!lengthMatch) throw new Error('LSP response is missing Content-Length');
|
|
54
|
+
const bodyLength = Number(lengthMatch[1]);
|
|
55
|
+
const bodyStart = headerEnd + 4;
|
|
56
|
+
if (stdoutBuffer.length < bodyStart + bodyLength) return;
|
|
57
|
+
const body = stdoutBuffer.subarray(bodyStart, bodyStart + bodyLength);
|
|
58
|
+
stdoutBuffer = stdoutBuffer.subarray(bodyStart + bodyLength);
|
|
59
|
+
dispatch(JSON.parse(body.toString('utf8')));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function waitForMessage(predicate, timeout = 30000) {
|
|
64
|
+
const existing = messages.find(predicate);
|
|
65
|
+
if (existing) return Promise.resolve(existing);
|
|
66
|
+
return new Promise((resolve, reject) => {
|
|
67
|
+
const timer = setTimeout(() => reject(new Error('Timed out waiting for LSP message')), timeout);
|
|
68
|
+
waiters.push({ predicate, resolve, timer });
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
beforeAll(async () => {
|
|
73
|
+
fixtureDirectory = await fs.mkdtemp(path.join(os.tmpdir(), 'zed-arkts-real-lsp-'));
|
|
74
|
+
projectDirectory = path.join(fixtureDirectory, 'project');
|
|
75
|
+
const sdkDirectory = path.join(fixtureDirectory, 'studio', 'Contents', 'sdk', 'default');
|
|
76
|
+
const ohosSdk = path.join(sdkDirectory, 'openharmony');
|
|
77
|
+
const hmsSdk = path.join(sdkDirectory, 'hms');
|
|
78
|
+
sourcePath = path.join(projectDirectory, 'entry', 'src', 'main', 'ets', 'Probe.ets');
|
|
79
|
+
|
|
80
|
+
await Promise.all([
|
|
81
|
+
fs.mkdir(path.dirname(sourcePath), { recursive: true }),
|
|
82
|
+
fs.mkdir(path.join(ohosSdk, 'ets', 'api'), { recursive: true }),
|
|
83
|
+
fs.mkdir(path.join(ohosSdk, 'ets', 'kits'), { recursive: true }),
|
|
84
|
+
fs.mkdir(path.join(ohosSdk, 'ets', 'component'), { recursive: true }),
|
|
85
|
+
fs.mkdir(path.join(ohosSdk, 'ets', 'build-tools', 'ets-loader', 'declarations'), {
|
|
86
|
+
recursive: true,
|
|
87
|
+
}),
|
|
88
|
+
fs.mkdir(path.join(hmsSdk, 'ets', 'api'), { recursive: true }),
|
|
89
|
+
fs.mkdir(path.join(hmsSdk, 'ets', 'kits'), { recursive: true }),
|
|
90
|
+
]);
|
|
91
|
+
await Promise.all([
|
|
92
|
+
fs.writeFile(path.join(ohosSdk, 'ets', 'build-tools', 'ets-loader', 'tsconfig.json'), '{}\n'),
|
|
93
|
+
fs.writeFile(
|
|
94
|
+
path.join(ohosSdk, 'ets', 'kits', '@kit.AbilityKit.d.ts'),
|
|
95
|
+
'export declare class UIAbility {}\n',
|
|
96
|
+
),
|
|
97
|
+
fs.writeFile(
|
|
98
|
+
path.join(hmsSdk, 'ets', 'kits', '@kit.TestHmsKit.d.ts'),
|
|
99
|
+
'export declare const hmsApi: string;\n',
|
|
100
|
+
),
|
|
101
|
+
fs.writeFile(
|
|
102
|
+
path.join(projectDirectory, 'build-profile.json5'),
|
|
103
|
+
'{ app: { products: [{ name: "default" }] }, modules: [{ name: "entry", srcPath: "./entry" }] }\n',
|
|
104
|
+
),
|
|
105
|
+
fs.writeFile(
|
|
106
|
+
path.join(projectDirectory, 'oh-package.json5'),
|
|
107
|
+
'{ name: "probe", version: "1.0.0" }\n',
|
|
108
|
+
),
|
|
109
|
+
fs.writeFile(
|
|
110
|
+
path.join(projectDirectory, 'entry', 'build-profile.json5'),
|
|
111
|
+
'{ apiType: "stageMode", buildOption: {} }\n',
|
|
112
|
+
),
|
|
113
|
+
fs.writeFile(
|
|
114
|
+
sourcePath,
|
|
115
|
+
"import { UIAbility } from '@kit.AbilityKit';\n" +
|
|
116
|
+
"import { hmsApi } from '@kit.TestHmsKit';\n" +
|
|
117
|
+
"import { missingApi } from '@kit.DoesNotExist';\n" +
|
|
118
|
+
'export const probe: UIAbility | string = hmsApi;\n' +
|
|
119
|
+
'export const missingProbe = missingApi;\n',
|
|
120
|
+
),
|
|
121
|
+
]);
|
|
122
|
+
|
|
123
|
+
const wrapperPath = path.join(testDirectory, '..', '..', 'index.js');
|
|
124
|
+
const languageServerPath = path.join(
|
|
125
|
+
testDirectory,
|
|
126
|
+
'..',
|
|
127
|
+
'..',
|
|
128
|
+
'node_modules',
|
|
129
|
+
'@arkts',
|
|
130
|
+
'language-server',
|
|
131
|
+
'bin',
|
|
132
|
+
'ets-language-server.js',
|
|
133
|
+
);
|
|
134
|
+
const languageServerPackage = JSON.parse(
|
|
135
|
+
await fs.readFile(
|
|
136
|
+
path.join(testDirectory, '..', '..', 'node_modules', '@arkts', 'language-server', 'package.json'),
|
|
137
|
+
'utf8',
|
|
138
|
+
),
|
|
139
|
+
);
|
|
140
|
+
languageServerVersion = languageServerPackage.version;
|
|
141
|
+
const env = { ...process.env };
|
|
142
|
+
for (const variableName of [
|
|
143
|
+
'ZED_ETS_OHOS_SDK_PATH',
|
|
144
|
+
'OHOS_SDK_PATH',
|
|
145
|
+
'HARMONYOS_SDK_HOME',
|
|
146
|
+
'OPENHARMONY_SDK_HOME',
|
|
147
|
+
'DEVECO_SDK_HOME',
|
|
148
|
+
'ZED_ETS_HMS_SDK_PATH',
|
|
149
|
+
'HMS_SDK_PATH',
|
|
150
|
+
'ZED_ETS_TSDK',
|
|
151
|
+
'TSDK',
|
|
152
|
+
]) {
|
|
153
|
+
delete env[variableName];
|
|
154
|
+
}
|
|
155
|
+
Object.assign(env, {
|
|
156
|
+
ETS_LANG_SERVER: languageServerPath,
|
|
157
|
+
DEVECO_STUDIO_HOME: path.join(fixtureDirectory, 'studio'),
|
|
158
|
+
});
|
|
159
|
+
serverProcess = spawn(process.execPath, [wrapperPath], {
|
|
160
|
+
cwd: projectDirectory,
|
|
161
|
+
env,
|
|
162
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
163
|
+
});
|
|
164
|
+
serverProcess.stdout.on('data', collectMessages);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
afterAll(async () => {
|
|
168
|
+
if (serverProcess && serverProcess.exitCode === null) {
|
|
169
|
+
await new Promise((resolve) => {
|
|
170
|
+
const timeout = setTimeout(() => serverProcess.kill('SIGKILL'), 5000);
|
|
171
|
+
serverProcess.once('exit', () => {
|
|
172
|
+
clearTimeout(timeout);
|
|
173
|
+
resolve();
|
|
174
|
+
});
|
|
175
|
+
serverProcess.kill();
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
await fs.rm(fixtureDirectory, { recursive: true, force: true });
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
describe('real ArkTS language server Kit diagnostics', () => {
|
|
182
|
+
it('resolves OpenHarmony and HMS Kit modules with language server 1.3.x', async () => {
|
|
183
|
+
expect(languageServerVersion).toMatch(/^1\.3\./);
|
|
184
|
+
const rootUri = pathToFileURL(projectDirectory).href;
|
|
185
|
+
send({
|
|
186
|
+
jsonrpc: '2.0',
|
|
187
|
+
id: 1,
|
|
188
|
+
method: 'initialize',
|
|
189
|
+
params: {
|
|
190
|
+
processId: process.pid,
|
|
191
|
+
rootUri,
|
|
192
|
+
workspaceFolders: [{ uri: rootUri, name: 'project' }],
|
|
193
|
+
capabilities: {
|
|
194
|
+
workspace: { configuration: true, workspaceFolders: true },
|
|
195
|
+
textDocument: { publishDiagnostics: {} },
|
|
196
|
+
},
|
|
197
|
+
initializationOptions: {},
|
|
198
|
+
},
|
|
199
|
+
});
|
|
200
|
+
const initializeResponse = await waitForMessage((message) => message.id === 1);
|
|
201
|
+
expect(initializeResponse.error).toBeUndefined();
|
|
202
|
+
send({ jsonrpc: '2.0', method: 'initialized', params: {} });
|
|
203
|
+
|
|
204
|
+
const uri = pathToFileURL(sourcePath).href;
|
|
205
|
+
send({
|
|
206
|
+
jsonrpc: '2.0',
|
|
207
|
+
method: 'textDocument/didOpen',
|
|
208
|
+
params: {
|
|
209
|
+
textDocument: {
|
|
210
|
+
uri,
|
|
211
|
+
languageId: 'ets',
|
|
212
|
+
version: 1,
|
|
213
|
+
text: await fs.readFile(sourcePath, 'utf8'),
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
});
|
|
217
|
+
const diagnostics = await waitForMessage(
|
|
218
|
+
(message) =>
|
|
219
|
+
message.method === 'textDocument/publishDiagnostics' &&
|
|
220
|
+
message.params.uri === uri &&
|
|
221
|
+
message.params.diagnostics.some((diagnostic) => Number(diagnostic.code) === 2307),
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
const unresolvedModules = diagnostics.params.diagnostics.filter(
|
|
225
|
+
(diagnostic) => Number(diagnostic.code) === 2307,
|
|
226
|
+
);
|
|
227
|
+
expect(unresolvedModules).toHaveLength(1);
|
|
228
|
+
expect(unresolvedModules[0].message).toContain('@kit.DoesNotExist');
|
|
229
|
+
expect(unresolvedModules[0].message).not.toContain('@kit.AbilityKit');
|
|
230
|
+
expect(unresolvedModules[0].message).not.toContain('@kit.TestHmsKit');
|
|
231
|
+
}, 40000);
|
|
232
|
+
});
|