@ic-reactor/vite-plugin 0.7.0 → 0.7.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 CHANGED
@@ -38,12 +38,12 @@ pnpm add @ic-reactor/react @tanstack/react-query @icp-sdk/core
38
38
  // vite.config.ts
39
39
  import { defineConfig } from "vite"
40
40
  import react from "@vitejs/plugin-react"
41
- import { icReactor } from "@ic-reactor/vite-plugin"
41
+ import { icReactorPlugin } from "@ic-reactor/vite-plugin"
42
42
 
43
43
  export default defineConfig({
44
44
  plugins: [
45
45
  react(),
46
- icReactor({
46
+ icReactorPlugin({
47
47
  canisters: [
48
48
  {
49
49
  name: "backend",
@@ -87,23 +87,6 @@ function MyComponent() {
87
87
  }
88
88
  ```
89
89
 
90
- ### Reactor Mode (per canister)
91
-
92
- By default, generated hooks use `DisplayReactor` (backward compatible). Set `mode` on a canister to choose the generated reactor class (`Reactor`, `DisplayReactor`, `CandidReactor`, `CandidDisplayReactor`, or `MetadataDisplayReactor`).
93
-
94
- ```ts
95
- icReactor({
96
- canisters: [
97
- { name: "backend", didFile: "./backend/backend.did" },
98
- {
99
- name: "workflow_engine",
100
- mode: "Reactor",
101
- didFile: "./workflow/workflow_engine.did",
102
- },
103
- ],
104
- })
105
- ```
106
-
107
90
  ## Configuration
108
91
 
109
92
  ### Plugin Options
@@ -117,14 +100,13 @@ icReactor({
117
100
 
118
101
  ### Canister Config
119
102
 
120
- | Option | Type | Description | Required |
121
- | :------------------ | :------------------------------------------------------------------------------------------------------- | :---------------------------------------------- | :------- |
122
- | `name` | `string` | Name of the canister (used for variable names). | Yes |
123
- | `didFile` | `string` | Path to the `.did` file. | Yes |
124
- | `outDir` | `string` | Override output directory for this canister. | No |
125
- | `clientManagerPath` | `string` | Override client manager path. | No |
126
- | `mode` | `"Reactor" \| "DisplayReactor" \| "CandidReactor" \| "CandidDisplayReactor" \| "MetadataDisplayReactor"` | Reactor class for generated hooks. | No |
127
- | `canisterId` | `string` | Optional fixed canister ID. | No |
103
+ | Option | Type | Description | Required |
104
+ | :------------------ | :------- | :---------------------------------------------- | :------- |
105
+ | `name` | `string` | Name of the canister (used for variable names). | Yes |
106
+ | `didFile` | `string` | Path to the `.did` file. | Yes |
107
+ | `outDir` | `string` | Override output directory for this canister. | No |
108
+ | `clientManagerPath` | `string` | Override client manager path. | No |
109
+ | `canisterId` | `string` | Optional fixed canister ID. | No |
128
110
 
129
111
  ## Local Development (Environment Injection)
130
112
 
package/dist/index.cjs CHANGED
@@ -30,7 +30,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
- icReactor: () => icReactor
33
+ icReactorPlugin: () => icReactorPlugin
34
34
  });
35
35
  module.exports = __toCommonJS(index_exports);
36
36
  var import_node_path = __toESM(require("path"), 1);
@@ -78,7 +78,7 @@ function buildIcEnvCookie(canisterIds, rootKey) {
78
78
  }
79
79
 
80
80
  // src/index.ts
81
- function icReactor(options) {
81
+ function icReactorPlugin(options) {
82
82
  const {
83
83
  canisters,
84
84
  outDir = "src/declarations",
@@ -89,6 +89,10 @@ function icReactor(options) {
89
89
  outDir,
90
90
  clientManagerPath
91
91
  };
92
+ const projectRoot = process.cwd();
93
+ const resolveDidPath = (didFile) => import_node_path.default.normalize(
94
+ import_node_path.default.isAbsolute(didFile) ? didFile : import_node_path.default.resolve(projectRoot, didFile)
95
+ );
92
96
  const plugin = {
93
97
  name: "ic-reactor-plugin",
94
98
  enforce: "pre",
@@ -129,8 +133,11 @@ function icReactor(options) {
129
133
  }
130
134
  };
131
135
  },
136
+ configureServer(server) {
137
+ const didFiles = canisters.map((c) => resolveDidPath(c.didFile));
138
+ server.watcher.add(didFiles);
139
+ },
132
140
  async buildStart() {
133
- const projectRoot = process.cwd();
134
141
  console.log(
135
142
  `[ic-reactor] Generating hooks for ${canisters.length} canisters...`
136
143
  );
@@ -158,14 +165,13 @@ function icReactor(options) {
158
165
  handleHotUpdate({ file, server }) {
159
166
  if (file.endsWith(".did")) {
160
167
  const affectedCanister = canisters.find((c) => {
161
- const configPath = import_node_path.default.resolve(process.cwd(), c.didFile);
162
- return configPath === file;
168
+ const configPath = resolveDidPath(c.didFile);
169
+ return configPath === import_node_path.default.normalize(file);
163
170
  });
164
171
  if (affectedCanister) {
165
172
  console.log(
166
173
  `[ic-reactor] .did file changed: ${affectedCanister.name}. Regenerating...`
167
174
  );
168
- const projectRoot = process.cwd();
169
175
  (0, import_codegen.runCanisterPipeline)({
170
176
  canisterConfig: affectedCanister,
171
177
  projectRoot,
@@ -185,5 +191,5 @@ function icReactor(options) {
185
191
  }
186
192
  // Annotate the CommonJS export names for ESM import in node:
187
193
  0 && (module.exports = {
188
- icReactor
194
+ icReactorPlugin
189
195
  });
package/dist/index.d.cts CHANGED
@@ -31,6 +31,6 @@ interface IcReactorPluginOptions {
31
31
  */
32
32
  injectEnvironment?: boolean;
33
33
  }
34
- declare function icReactor(options: IcReactorPluginOptions): any;
34
+ declare function icReactorPlugin(options: IcReactorPluginOptions): any;
35
35
 
36
- export { type IcReactorPluginOptions, icReactor };
36
+ export { type IcReactorPluginOptions, icReactorPlugin };
package/dist/index.d.ts CHANGED
@@ -31,6 +31,6 @@ interface IcReactorPluginOptions {
31
31
  */
32
32
  injectEnvironment?: boolean;
33
33
  }
34
- declare function icReactor(options: IcReactorPluginOptions): any;
34
+ declare function icReactorPlugin(options: IcReactorPluginOptions): any;
35
35
 
36
- export { type IcReactorPluginOptions, icReactor };
36
+ export { type IcReactorPluginOptions, icReactorPlugin };
package/dist/index.js CHANGED
@@ -46,7 +46,7 @@ function buildIcEnvCookie(canisterIds, rootKey) {
46
46
  }
47
47
 
48
48
  // src/index.ts
49
- function icReactor(options) {
49
+ function icReactorPlugin(options) {
50
50
  const {
51
51
  canisters,
52
52
  outDir = "src/declarations",
@@ -57,6 +57,10 @@ function icReactor(options) {
57
57
  outDir,
58
58
  clientManagerPath
59
59
  };
60
+ const projectRoot = process.cwd();
61
+ const resolveDidPath = (didFile) => path.normalize(
62
+ path.isAbsolute(didFile) ? didFile : path.resolve(projectRoot, didFile)
63
+ );
60
64
  const plugin = {
61
65
  name: "ic-reactor-plugin",
62
66
  enforce: "pre",
@@ -97,8 +101,11 @@ function icReactor(options) {
97
101
  }
98
102
  };
99
103
  },
104
+ configureServer(server) {
105
+ const didFiles = canisters.map((c) => resolveDidPath(c.didFile));
106
+ server.watcher.add(didFiles);
107
+ },
100
108
  async buildStart() {
101
- const projectRoot = process.cwd();
102
109
  console.log(
103
110
  `[ic-reactor] Generating hooks for ${canisters.length} canisters...`
104
111
  );
@@ -126,14 +133,13 @@ function icReactor(options) {
126
133
  handleHotUpdate({ file, server }) {
127
134
  if (file.endsWith(".did")) {
128
135
  const affectedCanister = canisters.find((c) => {
129
- const configPath = path.resolve(process.cwd(), c.didFile);
130
- return configPath === file;
136
+ const configPath = resolveDidPath(c.didFile);
137
+ return configPath === path.normalize(file);
131
138
  });
132
139
  if (affectedCanister) {
133
140
  console.log(
134
141
  `[ic-reactor] .did file changed: ${affectedCanister.name}. Regenerating...`
135
142
  );
136
- const projectRoot = process.cwd();
137
143
  runCanisterPipeline({
138
144
  canisterConfig: affectedCanister,
139
145
  projectRoot,
@@ -152,5 +158,5 @@ function icReactor(options) {
152
158
  return plugin;
153
159
  }
154
160
  export {
155
- icReactor
161
+ icReactorPlugin
156
162
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ic-reactor/vite-plugin",
3
- "version": "0.7.0",
3
+ "version": "0.7.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,7 +37,7 @@
37
37
  "url": "https://github.com/B3Pay/ic-reactor/issues"
38
38
  },
39
39
  "dependencies": {
40
- "@ic-reactor/codegen": "0.7.0"
40
+ "@ic-reactor/codegen": "0.7.1"
41
41
  },
42
42
  "peerDependencies": {
43
43
  "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
package/src/index.test.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { describe, it, expect, vi, beforeEach } from "vitest"
2
- import { icReactor, type IcReactorPluginOptions } from "./index"
2
+ import { icReactorPlugin, type IcReactorPluginOptions } from "./index"
3
3
  import path from "path"
4
4
  import { execFileSync } from "child_process"
5
5
  import { runCanisterPipeline } from "@ic-reactor/codegen"
@@ -24,7 +24,7 @@ vi.mock("fs", () => ({
24
24
  },
25
25
  }))
26
26
 
27
- describe("icReactor", () => {
27
+ describe("icReactorPlugin", () => {
28
28
  const mockOptions: IcReactorPluginOptions = {
29
29
  canisters: [
30
30
  {
@@ -50,6 +50,9 @@ describe("icReactor", () => {
50
50
  ws: {
51
51
  send: vi.fn(),
52
52
  },
53
+ watcher: {
54
+ add: vi.fn(),
55
+ },
53
56
  }
54
57
 
55
58
  beforeEach(() => {
@@ -60,7 +63,7 @@ describe("icReactor", () => {
60
63
  })
61
64
 
62
65
  it("should return correct plugin structure", () => {
63
- const plugin = icReactor(mockOptions)
66
+ const plugin = icReactorPlugin(mockOptions)
64
67
  expect(plugin.name).toBe("ic-reactor-plugin")
65
68
  expect(plugin.buildStart).toBeDefined()
66
69
  expect(plugin.handleHotUpdate).toBeDefined()
@@ -89,7 +92,7 @@ describe("icReactor", () => {
89
92
  }
90
93
  )
91
94
 
92
- const plugin = icReactor(mockOptions)
95
+ const plugin = icReactorPlugin(mockOptions)
93
96
  const config = (plugin as any).config({}, { command: "serve" })
94
97
 
95
98
  expect(config.server.headers["Set-Cookie"]).toContain("ic_env=")
@@ -107,7 +110,7 @@ describe("icReactor", () => {
107
110
  throw new Error("Command not found")
108
111
  })
109
112
 
110
- const plugin = icReactor(mockOptions)
113
+ const plugin = icReactorPlugin(mockOptions)
111
114
  const config = (plugin as any).config({}, { command: "serve" })
112
115
 
113
116
  expect(config.server.headers).toBeUndefined()
@@ -115,7 +118,7 @@ describe("icReactor", () => {
115
118
  })
116
119
 
117
120
  it("should return empty config for build command", () => {
118
- const plugin = icReactor(mockOptions)
121
+ const plugin = icReactorPlugin(mockOptions)
119
122
  const config = (plugin as any).config({}, { command: "build" })
120
123
 
121
124
  expect(config).toEqual({})
@@ -124,7 +127,7 @@ describe("icReactor", () => {
124
127
 
125
128
  describe("buildStart", () => {
126
129
  it("should generate declarations and reactor file", async () => {
127
- const plugin = icReactor(mockOptions)
130
+ const plugin = icReactorPlugin(mockOptions)
128
131
  await (plugin.buildStart as any)()
129
132
 
130
133
  expect(runCanisterPipeline).toHaveBeenCalledWith({
@@ -147,7 +150,7 @@ describe("icReactor", () => {
147
150
  error: "Failed to generate",
148
151
  })
149
152
 
150
- const plugin = icReactor(mockOptions)
153
+ const plugin = icReactorPlugin(mockOptions)
151
154
  await (plugin.buildStart as any)()
152
155
 
153
156
  expect(consoleErrorSpy).toHaveBeenCalledWith(
@@ -156,37 +159,20 @@ describe("icReactor", () => {
156
159
 
157
160
  consoleErrorSpy.mockRestore()
158
161
  })
162
+ })
159
163
 
160
- it("should pass canister mode through to codegen via canister config", async () => {
161
- const plugin = icReactor({
162
- canisters: [
163
- {
164
- ...mockOptions.canisters[0],
165
- mode: "Reactor",
166
- },
167
- ],
168
- outDir: mockOptions.outDir,
169
- })
170
-
171
- await (plugin.buildStart as any)()
164
+ describe("handleHotUpdate", () => {
165
+ it("should register configured .did files with the watcher", () => {
166
+ const plugin = icReactorPlugin(mockOptions)
167
+ ;(plugin.configureServer as any)(mockServer)
172
168
 
173
- expect(runCanisterPipeline).toHaveBeenCalledWith({
174
- canisterConfig: {
175
- ...mockOptions.canisters[0],
176
- mode: "Reactor",
177
- },
178
- projectRoot: expect.any(String),
179
- globalConfig: {
180
- outDir: "src/declarations",
181
- clientManagerPath: "../../clients",
182
- },
183
- })
169
+ expect(mockServer.watcher.add).toHaveBeenCalledWith([
170
+ expect.stringContaining("src/declarations/test.did"),
171
+ ])
184
172
  })
185
- })
186
173
 
187
- describe("handleHotUpdate", () => {
188
174
  it("should restart server when .did file changes", async () => {
189
- const plugin = icReactor(mockOptions)
175
+ const plugin = icReactorPlugin(mockOptions)
190
176
  const ctx = {
191
177
  file: "/absolute/path/to/src/declarations/test.did",
192
178
  server: mockServer,
@@ -207,7 +193,7 @@ describe("icReactor", () => {
207
193
  })
208
194
 
209
195
  it("should ignore other files", () => {
210
- const plugin = icReactor(mockOptions)
196
+ const plugin = icReactorPlugin(mockOptions)
211
197
  const ctx = {
212
198
  file: "/some/other/file.ts",
213
199
  server: mockServer,
package/src/index.ts CHANGED
@@ -39,7 +39,7 @@ export interface IcReactorPluginOptions {
39
39
  injectEnvironment?: boolean
40
40
  }
41
41
 
42
- export function icReactor(options: IcReactorPluginOptions): any {
42
+ export function icReactorPlugin(options: IcReactorPluginOptions): any {
43
43
  const {
44
44
  canisters,
45
45
  outDir = "src/declarations",
@@ -52,6 +52,11 @@ export function icReactor(options: IcReactorPluginOptions): any {
52
52
  outDir,
53
53
  clientManagerPath,
54
54
  }
55
+ const projectRoot = process.cwd()
56
+ const resolveDidPath = (didFile: string) =>
57
+ path.normalize(
58
+ path.isAbsolute(didFile) ? didFile : path.resolve(projectRoot, didFile)
59
+ )
55
60
 
56
61
  const plugin: Plugin = {
57
62
  name: "ic-reactor-plugin",
@@ -105,10 +110,15 @@ export function icReactor(options: IcReactorPluginOptions): any {
105
110
  }
106
111
  },
107
112
 
113
+ configureServer(server) {
114
+ // Explicitly watch configured DID files so HMR works even when they are not in the module graph.
115
+ const didFiles = canisters.map((c) => resolveDidPath(c.didFile))
116
+ server.watcher.add(didFiles)
117
+ },
118
+
108
119
  async buildStart() {
109
120
  // ── Code Generation ──────────────────────────────────────────────────
110
121
 
111
- const projectRoot = process.cwd()
112
122
  console.log(
113
123
  `[ic-reactor] Generating hooks for ${canisters.length} canisters...`
114
124
  )
@@ -148,8 +158,8 @@ export function icReactor(options: IcReactorPluginOptions): any {
148
158
  const affectedCanister = canisters.find((c) => {
149
159
  // Check if changed file matches configured didFile
150
160
  // Cast is safe because didFile is required in CanisterConfig
151
- const configPath = path.resolve(process.cwd(), c.didFile)
152
- return configPath === file
161
+ const configPath = resolveDidPath(c.didFile)
162
+ return configPath === path.normalize(file)
153
163
  })
154
164
 
155
165
  if (affectedCanister) {
@@ -158,7 +168,6 @@ export function icReactor(options: IcReactorPluginOptions): any {
158
168
  )
159
169
 
160
170
  // Re-run pipeline for this canister
161
- const projectRoot = process.cwd()
162
171
  runCanisterPipeline({
163
172
  canisterConfig: affectedCanister,
164
173
  projectRoot,