@ic-reactor/vite-plugin 0.4.1 → 0.5.1
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/README.md +32 -114
- package/dist/index.cjs +67 -88
- package/dist/index.d.cts +19 -38
- package/dist/index.d.ts +19 -38
- package/dist/index.js +67 -90
- package/package.json +3 -4
- package/src/env.ts +80 -0
- package/src/index.test.ts +21 -99
- package/src/index.ts +94 -184
package/dist/index.js
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
-
import fs from "fs";
|
|
3
2
|
import path from "path";
|
|
4
|
-
import { execFileSync } from "child_process";
|
|
5
3
|
import {
|
|
6
|
-
|
|
7
|
-
generateReactorFile,
|
|
8
|
-
generateClientFile
|
|
4
|
+
runCanisterPipeline
|
|
9
5
|
} from "@ic-reactor/codegen";
|
|
6
|
+
|
|
7
|
+
// src/env.ts
|
|
8
|
+
import { execFileSync } from "child_process";
|
|
10
9
|
function getIcEnvironmentInfo(canisterNames) {
|
|
11
10
|
const environment = process.env.ICP_ENVIRONMENT || "local";
|
|
12
11
|
try {
|
|
13
12
|
const networkStatus = JSON.parse(
|
|
14
13
|
execFileSync("icp", ["network", "status", "-e", environment, "--json"], {
|
|
15
|
-
encoding: "utf-8"
|
|
14
|
+
encoding: "utf-8",
|
|
15
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
16
|
+
// suppress stderr
|
|
16
17
|
})
|
|
17
18
|
);
|
|
18
19
|
const rootKey = networkStatus.root_key;
|
|
@@ -23,35 +24,48 @@ function getIcEnvironmentInfo(canisterNames) {
|
|
|
23
24
|
const canisterId = execFileSync(
|
|
24
25
|
"icp",
|
|
25
26
|
["canister", "status", name, "-e", environment, "-i"],
|
|
26
|
-
{
|
|
27
|
-
encoding: "utf-8"
|
|
28
|
-
}
|
|
27
|
+
{ encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] }
|
|
29
28
|
).trim();
|
|
30
|
-
|
|
29
|
+
if (canisterId) {
|
|
30
|
+
canisterIds[name] = canisterId;
|
|
31
|
+
}
|
|
31
32
|
} catch {
|
|
32
33
|
}
|
|
33
34
|
}
|
|
34
35
|
return { environment, rootKey, proxyTarget, canisterIds };
|
|
35
|
-
} catch {
|
|
36
|
+
} catch (error) {
|
|
36
37
|
return null;
|
|
37
38
|
}
|
|
38
39
|
}
|
|
39
40
|
function buildIcEnvCookie(canisterIds, rootKey) {
|
|
40
|
-
const
|
|
41
|
+
const parts = [`ic_root_key=${rootKey}`];
|
|
41
42
|
for (const [name, id] of Object.entries(canisterIds)) {
|
|
42
|
-
|
|
43
|
+
parts.push(`PUBLIC_CANISTER_ID:${name}=${id}`);
|
|
43
44
|
}
|
|
44
|
-
return encodeURIComponent(
|
|
45
|
+
return encodeURIComponent(parts.join("&"));
|
|
45
46
|
}
|
|
47
|
+
|
|
48
|
+
// src/index.ts
|
|
46
49
|
function icReactorPlugin(options) {
|
|
47
|
-
const
|
|
48
|
-
|
|
50
|
+
const {
|
|
51
|
+
canisters,
|
|
52
|
+
outDir = "src/declarations",
|
|
53
|
+
clientManagerPath = "../../clients",
|
|
54
|
+
injectEnvironment = true
|
|
55
|
+
} = options;
|
|
56
|
+
const globalConfig = {
|
|
57
|
+
outDir,
|
|
58
|
+
clientManagerPath
|
|
59
|
+
};
|
|
60
|
+
const plugin = {
|
|
49
61
|
name: "ic-reactor-plugin",
|
|
50
|
-
|
|
51
|
-
|
|
62
|
+
enforce: "pre",
|
|
63
|
+
// Run before other plugins
|
|
64
|
+
config(config, { command }) {
|
|
65
|
+
if (command !== "serve" || !injectEnvironment) {
|
|
52
66
|
return {};
|
|
53
67
|
}
|
|
54
|
-
const canisterNames =
|
|
68
|
+
const canisterNames = canisters.map((c) => c.name).filter((n) => !!n);
|
|
55
69
|
if (!canisterNames.includes("internet_identity")) {
|
|
56
70
|
canisterNames.push("internet_identity");
|
|
57
71
|
}
|
|
@@ -84,95 +98,58 @@ function icReactorPlugin(options) {
|
|
|
84
98
|
};
|
|
85
99
|
},
|
|
86
100
|
async buildStart() {
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
101
|
+
const projectRoot = process.cwd();
|
|
102
|
+
console.log(
|
|
103
|
+
`[ic-reactor] Generating hooks for ${canisters.length} canisters...`
|
|
90
104
|
);
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
for (const canister of options.canisters) {
|
|
100
|
-
let didFile = canister.didFile;
|
|
101
|
-
const outDir = canister.outDir ?? path.join(baseOutDir, canister.name);
|
|
102
|
-
if (!didFile) {
|
|
103
|
-
const environment = process.env.ICP_ENVIRONMENT || "local";
|
|
104
|
-
console.log(
|
|
105
|
-
`[ic-reactor] didFile not specified for "${canister.name}". Attempting to download from canister...`
|
|
106
|
-
);
|
|
107
|
-
try {
|
|
108
|
-
const candidContent = execFileSync(
|
|
109
|
-
"icp",
|
|
110
|
-
[
|
|
111
|
-
"canister",
|
|
112
|
-
"metadata",
|
|
113
|
-
canister.name,
|
|
114
|
-
"candid:service",
|
|
115
|
-
"-e",
|
|
116
|
-
environment
|
|
117
|
-
],
|
|
118
|
-
{ encoding: "utf-8" }
|
|
119
|
-
).trim();
|
|
120
|
-
const declarationsDir = path.join(outDir, "declarations");
|
|
121
|
-
if (!fs.existsSync(declarationsDir)) {
|
|
122
|
-
fs.mkdirSync(declarationsDir, { recursive: true });
|
|
123
|
-
}
|
|
124
|
-
didFile = path.join(declarationsDir, `${canister.name}.did`);
|
|
125
|
-
fs.writeFileSync(didFile, candidContent);
|
|
126
|
-
console.log(
|
|
127
|
-
`[ic-reactor] Candid downloaded and saved to ${didFile}`
|
|
128
|
-
);
|
|
129
|
-
} catch (error) {
|
|
105
|
+
for (const canisterConfig of canisters) {
|
|
106
|
+
try {
|
|
107
|
+
const result = await runCanisterPipeline({
|
|
108
|
+
canisterConfig,
|
|
109
|
+
projectRoot,
|
|
110
|
+
globalConfig
|
|
111
|
+
});
|
|
112
|
+
if (!result.success) {
|
|
130
113
|
console.error(
|
|
131
|
-
`[ic-reactor] Failed to
|
|
114
|
+
`[ic-reactor] Failed to generate ${canisterConfig.name}: ${result.error}`
|
|
132
115
|
);
|
|
133
|
-
|
|
116
|
+
} else {
|
|
134
117
|
}
|
|
135
|
-
}
|
|
136
|
-
console.log(
|
|
137
|
-
`[ic-reactor] Generating hooks for ${canister.name} from ${didFile}`
|
|
138
|
-
);
|
|
139
|
-
const result = await generateDeclarations({
|
|
140
|
-
didFile,
|
|
141
|
-
outDir,
|
|
142
|
-
canisterName: canister.name
|
|
143
|
-
});
|
|
144
|
-
if (!result.success) {
|
|
118
|
+
} catch (err) {
|
|
145
119
|
console.error(
|
|
146
|
-
`[ic-reactor]
|
|
120
|
+
`[ic-reactor] Error generating ${canisterConfig.name}:`,
|
|
121
|
+
err
|
|
147
122
|
);
|
|
148
|
-
continue;
|
|
149
123
|
}
|
|
150
|
-
const reactorContent = generateReactorFile({
|
|
151
|
-
canisterName: canister.name,
|
|
152
|
-
didFile,
|
|
153
|
-
clientManagerPath: canister.clientManagerPath ?? options.clientManagerPath
|
|
154
|
-
});
|
|
155
|
-
const reactorPath = path.join(outDir, "index.ts");
|
|
156
|
-
fs.mkdirSync(outDir, { recursive: true });
|
|
157
|
-
fs.writeFileSync(reactorPath, reactorContent);
|
|
158
|
-
console.log(`[ic-reactor] Reactor hooks generated at ${reactorPath}`);
|
|
159
124
|
}
|
|
160
125
|
},
|
|
161
126
|
handleHotUpdate({ file, server }) {
|
|
162
127
|
if (file.endsWith(".did")) {
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
return
|
|
128
|
+
const affectedCanister = canisters.find((c) => {
|
|
129
|
+
const configPath = path.resolve(process.cwd(), c.didFile);
|
|
130
|
+
return configPath === file;
|
|
166
131
|
});
|
|
167
|
-
if (
|
|
132
|
+
if (affectedCanister) {
|
|
168
133
|
console.log(
|
|
169
|
-
`[ic-reactor]
|
|
134
|
+
`[ic-reactor] .did file changed: ${affectedCanister.name}. Regenerating...`
|
|
170
135
|
);
|
|
171
|
-
|
|
136
|
+
const projectRoot = process.cwd();
|
|
137
|
+
runCanisterPipeline({
|
|
138
|
+
canisterConfig: affectedCanister,
|
|
139
|
+
projectRoot,
|
|
140
|
+
globalConfig
|
|
141
|
+
}).then((result) => {
|
|
142
|
+
if (result.success) {
|
|
143
|
+
server.ws.send({ type: "full-reload" });
|
|
144
|
+
} else {
|
|
145
|
+
console.error(`[ic-reactor] Regeneration failed: ${result.error}`);
|
|
146
|
+
}
|
|
147
|
+
});
|
|
172
148
|
}
|
|
173
149
|
}
|
|
174
150
|
}
|
|
175
151
|
};
|
|
152
|
+
return plugin;
|
|
176
153
|
}
|
|
177
154
|
export {
|
|
178
155
|
icReactorPlugin
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ic-reactor/vite-plugin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "Vite plugin for zero-config IC reactor generation from Candid files",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -37,17 +37,16 @@
|
|
|
37
37
|
"url": "https://github.com/B3Pay/ic-reactor/issues"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@ic-reactor/codegen": "0.
|
|
40
|
+
"@ic-reactor/codegen": "0.5.1"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
|
-
"vite": "^5.0.0 || ^6.0.0 || ^7.0.0"
|
|
43
|
+
"vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@icp-sdk/bindgen": "^0.2.1",
|
|
47
47
|
"@types/node": "^25.2.3",
|
|
48
48
|
"tsup": "^8.5.1",
|
|
49
49
|
"typescript": "^5.9.3",
|
|
50
|
-
"vite": "^7.3.1",
|
|
51
50
|
"vitest": "^4.0.18"
|
|
52
51
|
},
|
|
53
52
|
"scripts": {
|
package/src/env.ts
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Environment Injection Utilities
|
|
3
|
+
*
|
|
4
|
+
* Handles detecting the local IC environment (via `dfx` or `icp` CLI)
|
|
5
|
+
* and building the `ic_env` cookie for the browser.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { execFileSync } from "child_process"
|
|
9
|
+
|
|
10
|
+
export interface IcEnvironment {
|
|
11
|
+
environment: string
|
|
12
|
+
rootKey: string
|
|
13
|
+
proxyTarget: string
|
|
14
|
+
canisterIds: Record<string, string>
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Detect the IC environment using the `icp` or `dfx` CLI.
|
|
19
|
+
*/
|
|
20
|
+
export function getIcEnvironmentInfo(
|
|
21
|
+
canisterNames: string[]
|
|
22
|
+
): IcEnvironment | null {
|
|
23
|
+
const environment = process.env.ICP_ENVIRONMENT || "local"
|
|
24
|
+
|
|
25
|
+
// We try `icp` first, but could fallback to `dfx` logic if needed.
|
|
26
|
+
// For now, assuming `icp` CLI is available based on previous code.
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
const networkStatus = JSON.parse(
|
|
30
|
+
execFileSync("icp", ["network", "status", "-e", environment, "--json"], {
|
|
31
|
+
encoding: "utf-8",
|
|
32
|
+
stdio: ["ignore", "pipe", "ignore"], // suppress stderr
|
|
33
|
+
})
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
const rootKey = networkStatus.root_key
|
|
37
|
+
// Default to localhost:4943 if port strictly needed, but `icp` gives us the port
|
|
38
|
+
const proxyTarget = `http://127.0.0.1:${networkStatus.port}`
|
|
39
|
+
|
|
40
|
+
const canisterIds: Record<string, string> = {}
|
|
41
|
+
|
|
42
|
+
for (const name of canisterNames) {
|
|
43
|
+
try {
|
|
44
|
+
const canisterId = execFileSync(
|
|
45
|
+
"icp",
|
|
46
|
+
["canister", "status", name, "-e", environment, "-i"],
|
|
47
|
+
{ encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] }
|
|
48
|
+
).trim()
|
|
49
|
+
|
|
50
|
+
if (canisterId) {
|
|
51
|
+
canisterIds[name] = canisterId
|
|
52
|
+
}
|
|
53
|
+
} catch {
|
|
54
|
+
// Canister might not exist or be deployed yet
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return { environment, rootKey, proxyTarget, canisterIds }
|
|
59
|
+
} catch (error) {
|
|
60
|
+
// CLI not found or failed
|
|
61
|
+
return null
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Build the `ic_env` cookie string.
|
|
67
|
+
* Format: `ic_root_key=<key>&PUBLIC_CANISTER_ID:<name>=<id>&...`
|
|
68
|
+
*/
|
|
69
|
+
export function buildIcEnvCookie(
|
|
70
|
+
canisterIds: Record<string, string>,
|
|
71
|
+
rootKey: string
|
|
72
|
+
): string {
|
|
73
|
+
const parts = [`ic_root_key=${rootKey}`]
|
|
74
|
+
|
|
75
|
+
for (const [name, id] of Object.entries(canisterIds)) {
|
|
76
|
+
parts.push(`PUBLIC_CANISTER_ID:${name}=${id}`)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return encodeURIComponent(parts.join("&"))
|
|
80
|
+
}
|
package/src/index.test.ts
CHANGED
|
@@ -1,19 +1,12 @@
|
|
|
1
1
|
import { describe, it, expect, vi, beforeEach } from "vitest"
|
|
2
2
|
import { icReactorPlugin, type IcReactorPluginOptions } from "./index"
|
|
3
|
-
import fs from "fs"
|
|
4
3
|
import path from "path"
|
|
5
4
|
import { execFileSync } from "child_process"
|
|
6
|
-
import {
|
|
7
|
-
generateDeclarations,
|
|
8
|
-
generateReactorFile,
|
|
9
|
-
generateClientFile,
|
|
10
|
-
} from "@ic-reactor/codegen"
|
|
5
|
+
import { runCanisterPipeline } from "@ic-reactor/codegen"
|
|
11
6
|
|
|
12
7
|
// Mock internal dependencies
|
|
13
8
|
vi.mock("@ic-reactor/codegen", () => ({
|
|
14
|
-
|
|
15
|
-
generateReactorFile: vi.fn(),
|
|
16
|
-
generateClientFile: vi.fn(),
|
|
9
|
+
runCanisterPipeline: vi.fn(),
|
|
17
10
|
}))
|
|
18
11
|
|
|
19
12
|
// Mock child_process
|
|
@@ -54,16 +47,16 @@ describe("icReactorPlugin", () => {
|
|
|
54
47
|
use: vi.fn(),
|
|
55
48
|
},
|
|
56
49
|
restart: vi.fn(),
|
|
50
|
+
ws: {
|
|
51
|
+
send: vi.fn(),
|
|
52
|
+
},
|
|
57
53
|
}
|
|
58
54
|
|
|
59
55
|
beforeEach(() => {
|
|
60
56
|
vi.resetAllMocks()
|
|
61
|
-
;(
|
|
57
|
+
;(runCanisterPipeline as any).mockResolvedValue({
|
|
62
58
|
success: true,
|
|
63
|
-
declarationsDir: "/mock/declarations",
|
|
64
59
|
})
|
|
65
|
-
;(generateReactorFile as any).mockReturnValue("export const reactor = {}")
|
|
66
|
-
;(generateClientFile as any).mockReturnValue("export const client = {}")
|
|
67
60
|
})
|
|
68
61
|
|
|
69
62
|
it("should return correct plugin structure", () => {
|
|
@@ -72,8 +65,6 @@ describe("icReactorPlugin", () => {
|
|
|
72
65
|
expect(plugin.buildStart).toBeDefined()
|
|
73
66
|
expect(plugin.handleHotUpdate).toBeDefined()
|
|
74
67
|
expect((plugin as any).config).toBeDefined()
|
|
75
|
-
// configureServer is no longer used for middleware
|
|
76
|
-
expect(plugin.configureServer).toBeUndefined()
|
|
77
68
|
})
|
|
78
69
|
|
|
79
70
|
describe("config", () => {
|
|
@@ -136,82 +127,13 @@ describe("icReactorPlugin", () => {
|
|
|
136
127
|
const plugin = icReactorPlugin(mockOptions)
|
|
137
128
|
await (plugin.buildStart as any)()
|
|
138
129
|
|
|
139
|
-
expect(
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
canisterName: "test_canister",
|
|
147
|
-
didFile: "src/declarations/test.did",
|
|
148
|
-
clientManagerPath: undefined,
|
|
149
|
-
})
|
|
150
|
-
|
|
151
|
-
expect(fs.writeFileSync).toHaveBeenCalledWith(
|
|
152
|
-
path.join("src/declarations/test_canister", "index.ts"),
|
|
153
|
-
"export const reactor = {}"
|
|
154
|
-
)
|
|
155
|
-
})
|
|
156
|
-
|
|
157
|
-
it("should download candid if not specified", async () => {
|
|
158
|
-
const optionsWithMissingDid: IcReactorPluginOptions = {
|
|
159
|
-
canisters: [
|
|
160
|
-
{
|
|
161
|
-
name: "missing_did",
|
|
162
|
-
outDir: "src/declarations/missing_did",
|
|
163
|
-
},
|
|
164
|
-
],
|
|
165
|
-
outDir: "src/declarations",
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
;(execFileSync as any).mockImplementation(
|
|
169
|
-
(command: string, args: string[]) => {
|
|
170
|
-
if (command === "icp" && args.includes("metadata")) {
|
|
171
|
-
return "service : { greet: (text) -> (text) query }"
|
|
172
|
-
}
|
|
173
|
-
return ""
|
|
174
|
-
}
|
|
175
|
-
)
|
|
176
|
-
|
|
177
|
-
const plugin = icReactorPlugin(optionsWithMissingDid)
|
|
178
|
-
await (plugin.buildStart as any)()
|
|
179
|
-
|
|
180
|
-
expect(execFileSync).toHaveBeenCalledWith(
|
|
181
|
-
"icp",
|
|
182
|
-
expect.arrayContaining([
|
|
183
|
-
"canister",
|
|
184
|
-
"metadata",
|
|
185
|
-
"missing_did",
|
|
186
|
-
"candid:service",
|
|
187
|
-
]),
|
|
188
|
-
expect.any(Object)
|
|
189
|
-
)
|
|
190
|
-
|
|
191
|
-
expect(fs.writeFileSync).toHaveBeenCalledWith(
|
|
192
|
-
path.join(
|
|
193
|
-
"src/declarations/missing_did/declarations",
|
|
194
|
-
"missing_did.did"
|
|
195
|
-
),
|
|
196
|
-
"service : { greet: (text) -> (text) query }"
|
|
197
|
-
)
|
|
198
|
-
|
|
199
|
-
expect(generateDeclarations).toHaveBeenCalledWith({
|
|
200
|
-
didFile: path.join(
|
|
201
|
-
"src/declarations/missing_did/declarations",
|
|
202
|
-
"missing_did.did"
|
|
203
|
-
),
|
|
204
|
-
outDir: "src/declarations/missing_did",
|
|
205
|
-
canisterName: "missing_did",
|
|
206
|
-
})
|
|
207
|
-
|
|
208
|
-
expect(generateReactorFile).toHaveBeenCalledWith({
|
|
209
|
-
canisterName: "missing_did",
|
|
210
|
-
didFile: path.join(
|
|
211
|
-
"src/declarations/missing_did/declarations",
|
|
212
|
-
"missing_did.did"
|
|
213
|
-
),
|
|
214
|
-
clientManagerPath: undefined,
|
|
130
|
+
expect(runCanisterPipeline).toHaveBeenCalledWith({
|
|
131
|
+
canisterConfig: mockOptions.canisters[0],
|
|
132
|
+
projectRoot: expect.any(String),
|
|
133
|
+
globalConfig: {
|
|
134
|
+
outDir: "src/declarations",
|
|
135
|
+
clientManagerPath: "../../clients",
|
|
136
|
+
},
|
|
215
137
|
})
|
|
216
138
|
})
|
|
217
139
|
|
|
@@ -220,7 +142,7 @@ describe("icReactorPlugin", () => {
|
|
|
220
142
|
.spyOn(console, "error")
|
|
221
143
|
.mockImplementation(() => {})
|
|
222
144
|
|
|
223
|
-
;(
|
|
145
|
+
;(runCanisterPipeline as any).mockResolvedValue({
|
|
224
146
|
success: false,
|
|
225
147
|
error: "Failed to generate",
|
|
226
148
|
})
|
|
@@ -229,16 +151,15 @@ describe("icReactorPlugin", () => {
|
|
|
229
151
|
await (plugin.buildStart as any)()
|
|
230
152
|
|
|
231
153
|
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
|
232
|
-
expect.stringContaining("Failed to generate
|
|
154
|
+
expect.stringContaining("Failed to generate test_canister")
|
|
233
155
|
)
|
|
234
|
-
expect(generateReactorFile).not.toHaveBeenCalled()
|
|
235
156
|
|
|
236
157
|
consoleErrorSpy.mockRestore()
|
|
237
158
|
})
|
|
238
159
|
})
|
|
239
160
|
|
|
240
161
|
describe("handleHotUpdate", () => {
|
|
241
|
-
it("should restart server when .did file changes", () => {
|
|
162
|
+
it("should restart server when .did file changes", async () => {
|
|
242
163
|
const plugin = icReactorPlugin(mockOptions)
|
|
243
164
|
const ctx = {
|
|
244
165
|
file: "/absolute/path/to/src/declarations/test.did",
|
|
@@ -248,14 +169,15 @@ describe("icReactorPlugin", () => {
|
|
|
248
169
|
// Mock path.resolve to match the test case
|
|
249
170
|
const originalResolve = path.resolve
|
|
250
171
|
vi.spyOn(path, "resolve").mockImplementation((...args) => {
|
|
251
|
-
if (args.some((a) => a.includes("test.did"))) {
|
|
172
|
+
if (args.some((a) => a && a.includes("test.did"))) {
|
|
252
173
|
return "/absolute/path/to/src/declarations/test.did"
|
|
253
174
|
}
|
|
254
175
|
return originalResolve(...args)
|
|
255
176
|
})
|
|
256
|
-
;(plugin.handleHotUpdate as any)(ctx)
|
|
257
177
|
|
|
258
|
-
|
|
178
|
+
await (plugin.handleHotUpdate as any)(ctx)
|
|
179
|
+
|
|
180
|
+
expect(mockServer.ws.send).toHaveBeenCalledWith({ type: "full-reload" })
|
|
259
181
|
})
|
|
260
182
|
|
|
261
183
|
it("should ignore other files", () => {
|
|
@@ -267,7 +189,7 @@ describe("icReactorPlugin", () => {
|
|
|
267
189
|
|
|
268
190
|
;(plugin.handleHotUpdate as any)(ctx)
|
|
269
191
|
|
|
270
|
-
expect(mockServer.
|
|
192
|
+
expect(mockServer.ws.send).not.toHaveBeenCalled()
|
|
271
193
|
})
|
|
272
194
|
})
|
|
273
195
|
})
|