@soda-gql/vite-plugin 0.2.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/README.md ADDED
@@ -0,0 +1,69 @@
1
+ # @soda-gql/vite-plugin
2
+
3
+ Vite plugin for soda-gql. Transforms soda-gql DSL to runtime calls during development and build.
4
+
5
+ ## Features
6
+
7
+ - **Zero-config setup** - Works out of the box with soda-gql config
8
+ - **HMR support** - Hot module replacement for GraphQL operations
9
+ - **Fast rebuilds** - Incremental artifact updates
10
+
11
+ ## Installation
12
+
13
+ ```bash
14
+ npm install @soda-gql/vite-plugin
15
+ # or
16
+ bun add @soda-gql/vite-plugin
17
+ ```
18
+
19
+ ## Usage
20
+
21
+ Add the plugin to your Vite config:
22
+
23
+ ```typescript
24
+ // vite.config.ts
25
+ import { defineConfig } from "vite";
26
+ import { sodaGqlPlugin } from "@soda-gql/vite-plugin";
27
+
28
+ export default defineConfig({
29
+ plugins: [sodaGqlPlugin()],
30
+ });
31
+ ```
32
+
33
+ ## Configuration
34
+
35
+ The plugin automatically loads configuration from `soda-gql.config.ts`.
36
+
37
+ ### Plugin Options
38
+
39
+ ```typescript
40
+ sodaGqlPlugin({
41
+ // Use a specific config file
42
+ configPath: "./custom-config.ts",
43
+
44
+ // Filter which files to transform
45
+ include: ["src/**/*.ts"],
46
+ exclude: ["**/*.test.ts"],
47
+ });
48
+ ```
49
+
50
+ ## How It Works
51
+
52
+ 1. **Build phase** - The plugin uses `@soda-gql/builder` to analyze source files and generate artifacts
53
+ 2. **Transform phase** - Uses `@soda-gql/babel-transformer` to replace `gql.default()` calls with `gqlRuntime.getOperation()` calls
54
+ 3. **Watch mode** - Automatically rebuilds artifacts when GraphQL files change
55
+
56
+ ## Requirements
57
+
58
+ - Vite 5.x or 6.x
59
+ - Node.js >= 18
60
+
61
+ ## Related Packages
62
+
63
+ - [@soda-gql/webpack-plugin](../webpack-plugin) - Webpack integration
64
+ - [@soda-gql/metro-plugin](../metro-plugin) - React Native/Expo integration
65
+ - [@soda-gql/babel-plugin](../babel-plugin) - Standalone Babel plugin
66
+
67
+ ## License
68
+
69
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1,233 @@
1
+ let __soda_gql_plugin_common = require("@soda-gql/plugin-common");
2
+ let __babel_core = require("@babel/core");
3
+ let __soda_gql_babel_plugin = require("@soda-gql/babel-plugin");
4
+ let __soda_gql_builder = require("@soda-gql/builder");
5
+ let __soda_gql_common = require("@soda-gql/common");
6
+
7
+ //#region packages/vite-plugin/src/plugin.ts
8
+ /**
9
+ * Vite plugin for soda-gql that handles GraphQL code transformations
10
+ * at build time with HMR support for development.
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * // vite.config.ts
15
+ * import { defineConfig } from "vite";
16
+ * import react from "@vitejs/plugin-react";
17
+ * import { sodaGqlPlugin } from "@soda-gql/vite-plugin";
18
+ *
19
+ * export default defineConfig({
20
+ * plugins: [sodaGqlPlugin({ debug: true }), react()],
21
+ * });
22
+ * ```
23
+ */
24
+ const sodaGqlPlugin = (options = {}) => {
25
+ const stateKey = (0, __soda_gql_plugin_common.getStateKey)(options.configPath);
26
+ let pluginSession = null;
27
+ let currentArtifact = null;
28
+ let previousArtifact = null;
29
+ let isDevMode = false;
30
+ let swcTransformer = null;
31
+ let swcInitialized = false;
32
+ const log = (message) => {
33
+ if (options.debug) console.log(`[@soda-gql/vite-plugin] ${message}`);
34
+ };
35
+ /**
36
+ * Initialize SWC transformer if configured.
37
+ */
38
+ const initializeSwcTransformer = async () => {
39
+ if (swcInitialized || options.transformer !== "swc") return;
40
+ swcInitialized = true;
41
+ if (!currentArtifact || !pluginSession) return;
42
+ try {
43
+ const { createTransformer } = await import("@soda-gql/swc-transformer");
44
+ swcTransformer = await createTransformer({
45
+ config: pluginSession.config,
46
+ artifact: currentArtifact,
47
+ sourceMap: true
48
+ });
49
+ (0, __soda_gql_plugin_common.setSharedSwcTransformer)(stateKey, swcTransformer);
50
+ log("SWC transformer initialized");
51
+ } catch (error) {
52
+ console.warn(`[@soda-gql/vite-plugin] Failed to initialize SWC transformer: ${error}. Make sure @soda-gql/swc-transformer is installed. Falling back to Babel.`);
53
+ swcTransformer = null;
54
+ }
55
+ };
56
+ /**
57
+ * Check if a file path corresponds to a soda-gql source file.
58
+ */
59
+ const isSodaGqlFile = (filePath) => {
60
+ if (!currentArtifact) return false;
61
+ const normalized = (0, __soda_gql_common.normalizePath)(filePath);
62
+ for (const element of Object.values(currentArtifact.elements)) if ((0, __soda_gql_common.normalizePath)(element.metadata.sourcePath) === normalized) return true;
63
+ return false;
64
+ };
65
+ /**
66
+ * Check if artifact has changed by comparing element counts and hashes.
67
+ */
68
+ const hasArtifactChanged = () => {
69
+ if (!previousArtifact || !currentArtifact) return true;
70
+ if (Object.keys(previousArtifact.elements).length !== Object.keys(currentArtifact.elements).length) return true;
71
+ const prevElements = previousArtifact.elements;
72
+ const currElements = currentArtifact.elements;
73
+ for (const [id, element] of Object.entries(currElements)) {
74
+ const prevElement = prevElements[id];
75
+ if (!prevElement) return true;
76
+ if (element.metadata.contentHash !== prevElement.metadata.contentHash) return true;
77
+ }
78
+ return false;
79
+ };
80
+ /**
81
+ * Get files that changed between previous and current artifact.
82
+ */
83
+ const getChangedSodaGqlFiles = () => {
84
+ const changed = /* @__PURE__ */ new Set();
85
+ if (!previousArtifact || !currentArtifact) return changed;
86
+ const prevElements = previousArtifact.elements;
87
+ const currElements = currentArtifact.elements;
88
+ for (const [id, element] of Object.entries(currElements)) {
89
+ const prevElement = prevElements[id];
90
+ const sourcePath = element.metadata.sourcePath;
91
+ if (!prevElement || prevElement.metadata.contentHash !== element.metadata.contentHash) changed.add((0, __soda_gql_common.normalizePath)(sourcePath));
92
+ }
93
+ for (const [id, element] of Object.entries(prevElements)) if (!currElements[id]) {
94
+ const sourcePath = element.metadata.sourcePath;
95
+ changed.add((0, __soda_gql_common.normalizePath)(sourcePath));
96
+ }
97
+ return changed;
98
+ };
99
+ return {
100
+ name: "@soda-gql/vite-plugin",
101
+ enforce: "pre",
102
+ configResolved(config) {
103
+ isDevMode = config.command === "serve";
104
+ log(`Mode: ${isDevMode ? "development" : "production"}`);
105
+ },
106
+ async buildStart() {
107
+ pluginSession = (0, __soda_gql_plugin_common.createPluginSession)(options, "@soda-gql/vite-plugin");
108
+ if (!pluginSession) {
109
+ log("Plugin disabled or config load failed");
110
+ return;
111
+ }
112
+ (0, __soda_gql_plugin_common.setSharedPluginSession)(stateKey, pluginSession);
113
+ currentArtifact = await pluginSession.getArtifactAsync();
114
+ (0, __soda_gql_plugin_common.setSharedArtifact)(stateKey, currentArtifact);
115
+ log(`Initial build: ${Object.keys(currentArtifact?.elements ?? {}).length} elements`);
116
+ await initializeSwcTransformer();
117
+ },
118
+ configureServer(server) {
119
+ log("Dev server configured");
120
+ },
121
+ async transform(code, id) {
122
+ if (!/\.[jt]sx?$/.test(id)) return null;
123
+ if (id.includes("node_modules")) return null;
124
+ if (!pluginSession || !currentArtifact) return null;
125
+ const normalizedPath = (0, __soda_gql_common.normalizePath)(id);
126
+ if (!Object.values(currentArtifact.elements).some((element) => (0, __soda_gql_common.normalizePath)(element.metadata.sourcePath) === normalizedPath)) return null;
127
+ log(`Transforming: ${normalizedPath}`);
128
+ if (swcTransformer) {
129
+ const swcResult = swcTransformer.transform({
130
+ sourceCode: code,
131
+ sourcePath: id
132
+ });
133
+ if (swcResult.transformed) return {
134
+ code: swcResult.sourceCode,
135
+ map: swcResult.sourceMap ? JSON.parse(swcResult.sourceMap) : void 0
136
+ };
137
+ return null;
138
+ }
139
+ const result = (0, __babel_core.transformSync)(code, {
140
+ filename: id,
141
+ babelrc: false,
142
+ configFile: false,
143
+ plugins: [(0, __soda_gql_babel_plugin.createPluginWithArtifact)({
144
+ artifact: currentArtifact,
145
+ config: pluginSession.config
146
+ })],
147
+ sourceMaps: true
148
+ });
149
+ if (result?.code) return {
150
+ code: result.code,
151
+ map: result.map
152
+ };
153
+ return null;
154
+ },
155
+ buildEnd() {
156
+ if (!isDevMode) {
157
+ log("Production build complete, cleaning up");
158
+ pluginSession = null;
159
+ currentArtifact = null;
160
+ previousArtifact = null;
161
+ swcTransformer = null;
162
+ (0, __soda_gql_plugin_common.setSharedPluginSession)(stateKey, null);
163
+ (0, __soda_gql_plugin_common.setSharedArtifact)(stateKey, null);
164
+ (0, __soda_gql_plugin_common.setSharedSwcTransformer)(stateKey, null);
165
+ }
166
+ },
167
+ async handleHotUpdate(ctx) {
168
+ const { file, server, modules } = ctx;
169
+ const normalizedPath = (0, __soda_gql_common.normalizePath)(file);
170
+ if (!pluginSession || !currentArtifact) return;
171
+ if (!isSodaGqlFile(normalizedPath)) return;
172
+ log(`soda-gql file changed: ${normalizedPath}`);
173
+ previousArtifact = currentArtifact;
174
+ currentArtifact = await pluginSession.getArtifactAsync();
175
+ (0, __soda_gql_plugin_common.setSharedArtifact)(stateKey, currentArtifact);
176
+ if (!currentArtifact) return;
177
+ if (!hasArtifactChanged()) {
178
+ log("Artifact unchanged, using normal HMR");
179
+ return;
180
+ }
181
+ const sharedState = (0, __soda_gql_plugin_common.getSharedState)(stateKey);
182
+ const changedFiles = getChangedSodaGqlFiles();
183
+ const affectedFiles = (0, __soda_gql_builder.collectAffectedFiles)({
184
+ changedFiles,
185
+ removedFiles: /* @__PURE__ */ new Set(),
186
+ previousModuleAdjacency: sharedState.moduleAdjacency
187
+ });
188
+ log(`Changed files: ${changedFiles.size}, Affected files: ${affectedFiles.size}`);
189
+ const affectedModules = /* @__PURE__ */ new Set();
190
+ for (const affectedPath of affectedFiles) {
191
+ const modulesByFile = server.moduleGraph.getModulesByFile(affectedPath);
192
+ if (modulesByFile) for (const mod of modulesByFile) affectedModules.add(mod);
193
+ }
194
+ for (const mod of modules) affectedModules.add(mod);
195
+ if (affectedModules.size > 0) {
196
+ log(`Invalidating ${affectedModules.size} modules for HMR`);
197
+ return [...affectedModules];
198
+ }
199
+ return modules;
200
+ }
201
+ };
202
+ };
203
+
204
+ //#endregion
205
+ //#region packages/vite-plugin/src/index.ts
206
+ /**
207
+ * Convenience alias for sodaGqlPlugin.
208
+ * @see {@link sodaGqlPlugin}
209
+ */
210
+ const withSodaGql = sodaGqlPlugin;
211
+
212
+ //#endregion
213
+ Object.defineProperty(exports, 'getSharedArtifact', {
214
+ enumerable: true,
215
+ get: function () {
216
+ return __soda_gql_plugin_common.getSharedArtifact;
217
+ }
218
+ });
219
+ Object.defineProperty(exports, 'getSharedState', {
220
+ enumerable: true,
221
+ get: function () {
222
+ return __soda_gql_plugin_common.getSharedState;
223
+ }
224
+ });
225
+ Object.defineProperty(exports, 'getStateKey', {
226
+ enumerable: true,
227
+ get: function () {
228
+ return __soda_gql_plugin_common.getStateKey;
229
+ }
230
+ });
231
+ exports.sodaGqlPlugin = sodaGqlPlugin;
232
+ exports.withSodaGql = withSodaGql;
233
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","names":["pluginSession: PluginSession | null","currentArtifact: BuilderArtifact | null","previousArtifact: BuilderArtifact | null","swcTransformer: SwcTransformerInterface | null","_sodaGqlPlugin"],"sources":["../src/plugin.ts","../src/index.ts"],"sourcesContent":["import { type TransformOptions, transformSync } from \"@babel/core\";\nimport { createPluginWithArtifact } from \"@soda-gql/babel-plugin\";\nimport { type BuilderArtifact, type BuilderArtifactElement, collectAffectedFiles } from \"@soda-gql/builder\";\nimport { normalizePath } from \"@soda-gql/common\";\nimport {\n createPluginSession,\n getSharedState,\n getStateKey,\n type PluginSession,\n type SwcTransformerInterface,\n setSharedArtifact,\n setSharedPluginSession,\n setSharedSwcTransformer,\n} from \"@soda-gql/plugin-common\";\nimport type { HmrContext, ModuleNode, Plugin, ViteDevServer } from \"vite\";\nimport type { VitePluginOptions } from \"./types\";\n\n/**\n * Vite plugin for soda-gql that handles GraphQL code transformations\n * at build time with HMR support for development.\n *\n * @example\n * ```typescript\n * // vite.config.ts\n * import { defineConfig } from \"vite\";\n * import react from \"@vitejs/plugin-react\";\n * import { sodaGqlPlugin } from \"@soda-gql/vite-plugin\";\n *\n * export default defineConfig({\n * plugins: [sodaGqlPlugin({ debug: true }), react()],\n * });\n * ```\n */\nexport const sodaGqlPlugin = (options: VitePluginOptions = {}): Plugin => {\n const stateKey = getStateKey(options.configPath);\n\n let pluginSession: PluginSession | null = null;\n let currentArtifact: BuilderArtifact | null = null;\n let previousArtifact: BuilderArtifact | null = null;\n let _viteServer: ViteDevServer | null = null;\n let isDevMode = false;\n let swcTransformer: SwcTransformerInterface | null = null;\n let swcInitialized = false;\n\n const log = (message: string): void => {\n if (options.debug) {\n console.log(`[@soda-gql/vite-plugin] ${message}`);\n }\n };\n\n /**\n * Initialize SWC transformer if configured.\n */\n const initializeSwcTransformer = async (): Promise<void> => {\n if (swcInitialized || options.transformer !== \"swc\") {\n return;\n }\n\n swcInitialized = true;\n\n if (!currentArtifact || !pluginSession) {\n return;\n }\n\n try {\n const { createTransformer } = await import(\"@soda-gql/swc-transformer\");\n swcTransformer = await createTransformer({\n config: pluginSession.config,\n artifact: currentArtifact,\n sourceMap: true,\n });\n setSharedSwcTransformer(stateKey, swcTransformer);\n log(\"SWC transformer initialized\");\n } catch (error) {\n console.warn(\n `[@soda-gql/vite-plugin] Failed to initialize SWC transformer: ${error}. ` +\n \"Make sure @soda-gql/swc-transformer is installed. Falling back to Babel.\",\n );\n swcTransformer = null;\n }\n };\n\n /**\n * Check if a file path corresponds to a soda-gql source file.\n */\n const isSodaGqlFile = (filePath: string): boolean => {\n if (!currentArtifact) return false;\n\n const normalized = normalizePath(filePath);\n for (const element of Object.values(currentArtifact.elements)) {\n if (normalizePath(element.metadata.sourcePath) === normalized) {\n return true;\n }\n }\n return false;\n };\n\n /**\n * Check if artifact has changed by comparing element counts and hashes.\n */\n const hasArtifactChanged = (): boolean => {\n if (!previousArtifact || !currentArtifact) return true;\n\n const prevCount = Object.keys(previousArtifact.elements).length;\n const newCount = Object.keys(currentArtifact.elements).length;\n if (prevCount !== newCount) return true;\n\n // Compare individual elements by their content hash\n const prevElements = previousArtifact.elements as Record<string, BuilderArtifactElement>;\n const currElements = currentArtifact.elements as Record<string, BuilderArtifactElement>;\n\n for (const [id, element] of Object.entries(currElements)) {\n const prevElement = prevElements[id];\n if (!prevElement) return true;\n if (element.metadata.contentHash !== prevElement.metadata.contentHash) {\n return true;\n }\n }\n\n return false;\n };\n\n /**\n * Get files that changed between previous and current artifact.\n */\n const getChangedSodaGqlFiles = (): Set<string> => {\n const changed = new Set<string>();\n\n if (!previousArtifact || !currentArtifact) return changed;\n\n const prevElements = previousArtifact.elements as Record<string, BuilderArtifactElement>;\n const currElements = currentArtifact.elements as Record<string, BuilderArtifactElement>;\n\n // Compare elements by their source paths and content hashes\n for (const [id, element] of Object.entries(currElements)) {\n const prevElement = prevElements[id];\n const sourcePath = element.metadata.sourcePath;\n\n if (!prevElement || prevElement.metadata.contentHash !== element.metadata.contentHash) {\n changed.add(normalizePath(sourcePath));\n }\n }\n\n // Check for removed elements\n for (const [id, element] of Object.entries(prevElements)) {\n if (!currElements[id]) {\n const sourcePath = element.metadata.sourcePath;\n changed.add(normalizePath(sourcePath));\n }\n }\n\n return changed;\n };\n\n return {\n name: \"@soda-gql/vite-plugin\",\n enforce: \"pre\", // Run before other plugins to transform source early\n\n configResolved(config) {\n isDevMode = config.command === \"serve\";\n log(`Mode: ${isDevMode ? \"development\" : \"production\"}`);\n },\n\n async buildStart() {\n // Initialize plugin session\n pluginSession = createPluginSession(options, \"@soda-gql/vite-plugin\");\n if (!pluginSession) {\n log(\"Plugin disabled or config load failed\");\n return;\n }\n\n setSharedPluginSession(stateKey, pluginSession);\n\n // Build initial artifact\n currentArtifact = await pluginSession.getArtifactAsync();\n setSharedArtifact(stateKey, currentArtifact);\n\n log(`Initial build: ${Object.keys(currentArtifact?.elements ?? {}).length} elements`);\n\n // Initialize SWC transformer if configured\n await initializeSwcTransformer();\n },\n\n configureServer(server) {\n _viteServer = server;\n log(\"Dev server configured\");\n },\n\n async transform(code, id) {\n // Skip non-JS/TS files\n if (!/\\.[jt]sx?$/.test(id)) {\n return null;\n }\n\n // Skip node_modules\n if (id.includes(\"node_modules\")) {\n return null;\n }\n\n // Skip if plugin is disabled or no artifact\n if (!pluginSession || !currentArtifact) {\n return null;\n }\n\n // Check if this file contains any soda-gql elements\n const normalizedPath = normalizePath(id);\n const hasElements = Object.values(currentArtifact.elements).some(\n (element) => normalizePath(element.metadata.sourcePath) === normalizedPath,\n );\n\n if (!hasElements) {\n return null; // Not a soda-gql file\n }\n\n log(`Transforming: ${normalizedPath}`);\n\n // Try SWC transformer first if available\n if (swcTransformer) {\n const swcResult = swcTransformer.transform({\n sourceCode: code,\n sourcePath: id,\n });\n\n if (swcResult.transformed) {\n return {\n code: swcResult.sourceCode,\n map: swcResult.sourceMap ? JSON.parse(swcResult.sourceMap) : undefined,\n };\n }\n // SWC didn't transform (no soda-gql code), return null to pass through\n return null;\n }\n\n // Fall back to Babel transformer\n const babelOptions: TransformOptions = {\n filename: id,\n babelrc: false,\n configFile: false,\n plugins: [createPluginWithArtifact({ artifact: currentArtifact, config: pluginSession.config })],\n sourceMaps: true,\n };\n\n const result = transformSync(code, babelOptions);\n\n if (result?.code) {\n return {\n code: result.code,\n map: result.map,\n };\n }\n\n return null;\n },\n\n buildEnd() {\n if (!isDevMode) {\n // Cleanup for production builds\n log(\"Production build complete, cleaning up\");\n pluginSession = null;\n currentArtifact = null;\n previousArtifact = null;\n swcTransformer = null;\n setSharedPluginSession(stateKey, null);\n setSharedArtifact(stateKey, null);\n setSharedSwcTransformer(stateKey, null);\n }\n },\n\n async handleHotUpdate(ctx: HmrContext): Promise<ModuleNode[] | void> {\n const { file, server, modules } = ctx;\n const normalizedPath = normalizePath(file);\n\n if (!pluginSession || !currentArtifact) {\n return; // Let Vite handle normally\n }\n\n // Check if the changed file is a soda-gql source file\n if (!isSodaGqlFile(normalizedPath)) {\n return; // Not a soda-gql file, let Vite handle normally\n }\n\n log(`soda-gql file changed: ${normalizedPath}`);\n\n // Store previous artifact for change detection\n previousArtifact = currentArtifact;\n\n // Rebuild artifact to detect changes\n currentArtifact = await pluginSession.getArtifactAsync();\n setSharedArtifact(stateKey, currentArtifact);\n\n if (!currentArtifact) {\n return;\n }\n\n // If artifact hasn't changed, just let normal HMR happen\n if (!hasArtifactChanged()) {\n log(\"Artifact unchanged, using normal HMR\");\n return;\n }\n\n // Compute affected files using module adjacency\n const sharedState = getSharedState(stateKey);\n const changedFiles = getChangedSodaGqlFiles();\n\n const affectedFiles = collectAffectedFiles({\n changedFiles,\n removedFiles: new Set(),\n previousModuleAdjacency: sharedState.moduleAdjacency,\n });\n\n log(`Changed files: ${changedFiles.size}, Affected files: ${affectedFiles.size}`);\n\n // Convert affected file paths to Vite module nodes\n const affectedModules = new Set<ModuleNode>();\n\n for (const affectedPath of affectedFiles) {\n // Try to get module by file path\n const modulesByFile = server.moduleGraph.getModulesByFile(affectedPath);\n if (modulesByFile) {\n for (const mod of modulesByFile) {\n affectedModules.add(mod);\n }\n }\n }\n\n // Include original modules\n for (const mod of modules) {\n affectedModules.add(mod);\n }\n\n if (affectedModules.size > 0) {\n log(`Invalidating ${affectedModules.size} modules for HMR`);\n return [...affectedModules];\n }\n\n // Fall back to original modules\n return modules;\n },\n };\n};\n","// Re-export shared state utilities for advanced usage\nexport { getSharedArtifact, getSharedState, getStateKey } from \"@soda-gql/plugin-common\";\nexport { sodaGqlPlugin } from \"./plugin\";\nexport type { TransformerType, VitePluginOptions } from \"./types\";\n\n// Re-import for convenience aliases\nimport { sodaGqlPlugin as _sodaGqlPlugin } from \"./plugin\";\n\n/**\n * Convenience alias for sodaGqlPlugin.\n * @see {@link sodaGqlPlugin}\n */\nexport const withSodaGql = _sodaGqlPlugin;\n\nexport default _sodaGqlPlugin;\n"],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ import { PluginOptions, TransformerType, TransformerType as TransformerType$1, getSharedArtifact, getSharedState, getStateKey } from "@soda-gql/plugin-common";
2
+ import * as vite0 from "vite";
3
+ import { Plugin } from "vite";
4
+
5
+ //#region packages/vite-plugin/src/types.d.ts
6
+ /**
7
+ * Options for the Vite plugin.
8
+ */
9
+ type VitePluginOptions = PluginOptions & {
10
+ /** File patterns to include for transformation (defaults to config.include) */
11
+ readonly include?: RegExp | RegExp[];
12
+ /** File patterns to exclude from transformation */
13
+ readonly exclude?: RegExp | RegExp[];
14
+ /** Enable verbose logging for debugging */
15
+ readonly debug?: boolean;
16
+ /**
17
+ * Transformer to use for code transformation.
18
+ * @default 'babel'
19
+ */
20
+ readonly transformer?: TransformerType$1;
21
+ };
22
+ //#endregion
23
+ //#region packages/vite-plugin/src/plugin.d.ts
24
+ /**
25
+ * Vite plugin for soda-gql that handles GraphQL code transformations
26
+ * at build time with HMR support for development.
27
+ *
28
+ * @example
29
+ * ```typescript
30
+ * // vite.config.ts
31
+ * import { defineConfig } from "vite";
32
+ * import react from "@vitejs/plugin-react";
33
+ * import { sodaGqlPlugin } from "@soda-gql/vite-plugin";
34
+ *
35
+ * export default defineConfig({
36
+ * plugins: [sodaGqlPlugin({ debug: true }), react()],
37
+ * });
38
+ * ```
39
+ */
40
+ declare const sodaGqlPlugin: (options?: VitePluginOptions) => Plugin;
41
+ //#endregion
42
+ //#region packages/vite-plugin/src/index.d.ts
43
+ /**
44
+ * Convenience alias for sodaGqlPlugin.
45
+ * @see {@link sodaGqlPlugin}
46
+ */
47
+ declare const withSodaGql: (options?: VitePluginOptions) => vite0.Plugin;
48
+ //#endregion
49
+ export { type TransformerType, type VitePluginOptions, getSharedArtifact, getSharedState, getStateKey, sodaGqlPlugin, withSodaGql };
50
+ //# sourceMappingURL=index.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../src/types.ts","../src/plugin.ts","../src/index.ts"],"sourcesContent":[],"mappings":";;;;;;;;AAOY,KAAA,iBAAA,GAAoB,aAAH,GAAA;EAAG;EAEX,SAAA,OAAA,CAAA,EAAA,MAAA,GAAS,MAAT,EAAA;EAAS;EAET,SAAA,OAAA,CAAA,EAAA,MAAA,GAAS,MAAT,EAAA;EAAS;EAOL,SAAA,KAAA,CAAA,EAAA,OAAA;EAAe;;;;ECe3B,SAAA,WAkTZ,CAAA,EDjUwB,iBCec;;;;;;;AD1BvC;;;;;;;;;;;AC0BA;;cAAa,0BAA0B,sBAAyB;;;;AD1BhE;;;AAE8B,cEGjB,WFHiB,EAAA,CAAA,OAAA,CAAA,EEGW,iBFHX,EAAA,GEGN,KAAA,CAAA,MFHM"}
@@ -0,0 +1,50 @@
1
+ import { PluginOptions, TransformerType, TransformerType as TransformerType$1, getSharedArtifact, getSharedState, getStateKey } from "@soda-gql/plugin-common";
2
+ import * as vite0 from "vite";
3
+ import { Plugin } from "vite";
4
+
5
+ //#region packages/vite-plugin/src/types.d.ts
6
+ /**
7
+ * Options for the Vite plugin.
8
+ */
9
+ type VitePluginOptions = PluginOptions & {
10
+ /** File patterns to include for transformation (defaults to config.include) */
11
+ readonly include?: RegExp | RegExp[];
12
+ /** File patterns to exclude from transformation */
13
+ readonly exclude?: RegExp | RegExp[];
14
+ /** Enable verbose logging for debugging */
15
+ readonly debug?: boolean;
16
+ /**
17
+ * Transformer to use for code transformation.
18
+ * @default 'babel'
19
+ */
20
+ readonly transformer?: TransformerType$1;
21
+ };
22
+ //#endregion
23
+ //#region packages/vite-plugin/src/plugin.d.ts
24
+ /**
25
+ * Vite plugin for soda-gql that handles GraphQL code transformations
26
+ * at build time with HMR support for development.
27
+ *
28
+ * @example
29
+ * ```typescript
30
+ * // vite.config.ts
31
+ * import { defineConfig } from "vite";
32
+ * import react from "@vitejs/plugin-react";
33
+ * import { sodaGqlPlugin } from "@soda-gql/vite-plugin";
34
+ *
35
+ * export default defineConfig({
36
+ * plugins: [sodaGqlPlugin({ debug: true }), react()],
37
+ * });
38
+ * ```
39
+ */
40
+ declare const sodaGqlPlugin: (options?: VitePluginOptions) => Plugin;
41
+ //#endregion
42
+ //#region packages/vite-plugin/src/index.d.ts
43
+ /**
44
+ * Convenience alias for sodaGqlPlugin.
45
+ * @see {@link sodaGqlPlugin}
46
+ */
47
+ declare const withSodaGql: (options?: VitePluginOptions) => vite0.Plugin;
48
+ //#endregion
49
+ export { type TransformerType, type VitePluginOptions, getSharedArtifact, getSharedState, getStateKey, sodaGqlPlugin, withSodaGql };
50
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/plugin.ts","../src/index.ts"],"sourcesContent":[],"mappings":";;;;;;;;AAOY,KAAA,iBAAA,GAAoB,aAAH,GAAA;EAAG;EAEX,SAAA,OAAA,CAAA,EAAA,MAAA,GAAS,MAAT,EAAA;EAAS;EAET,SAAA,OAAA,CAAA,EAAA,MAAA,GAAS,MAAT,EAAA;EAAS;EAOL,SAAA,KAAA,CAAA,EAAA,OAAA;EAAe;;;;ECe3B,SAAA,WAkTZ,CAAA,EDjUwB,iBCec;;;;;;;AD1BvC;;;;;;;;;;;AC0BA;;cAAa,0BAA0B,sBAAyB;;;;AD1BhE;;;AAE8B,cEGjB,WFHiB,EAAA,CAAA,OAAA,CAAA,EEGW,iBFHX,EAAA,GEGN,KAAA,CAAA,MFHM"}
package/dist/index.mjs ADDED
@@ -0,0 +1,214 @@
1
+ import { createPluginSession, getSharedArtifact, getSharedState, getSharedState as getSharedState$1, getStateKey, getStateKey as getStateKey$1, setSharedArtifact, setSharedPluginSession, setSharedSwcTransformer } from "@soda-gql/plugin-common";
2
+ import { transformSync } from "@babel/core";
3
+ import { createPluginWithArtifact } from "@soda-gql/babel-plugin";
4
+ import { collectAffectedFiles } from "@soda-gql/builder";
5
+ import { normalizePath } from "@soda-gql/common";
6
+
7
+ //#region packages/vite-plugin/src/plugin.ts
8
+ /**
9
+ * Vite plugin for soda-gql that handles GraphQL code transformations
10
+ * at build time with HMR support for development.
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * // vite.config.ts
15
+ * import { defineConfig } from "vite";
16
+ * import react from "@vitejs/plugin-react";
17
+ * import { sodaGqlPlugin } from "@soda-gql/vite-plugin";
18
+ *
19
+ * export default defineConfig({
20
+ * plugins: [sodaGqlPlugin({ debug: true }), react()],
21
+ * });
22
+ * ```
23
+ */
24
+ const sodaGqlPlugin = (options = {}) => {
25
+ const stateKey = getStateKey$1(options.configPath);
26
+ let pluginSession = null;
27
+ let currentArtifact = null;
28
+ let previousArtifact = null;
29
+ let isDevMode = false;
30
+ let swcTransformer = null;
31
+ let swcInitialized = false;
32
+ const log = (message) => {
33
+ if (options.debug) console.log(`[@soda-gql/vite-plugin] ${message}`);
34
+ };
35
+ /**
36
+ * Initialize SWC transformer if configured.
37
+ */
38
+ const initializeSwcTransformer = async () => {
39
+ if (swcInitialized || options.transformer !== "swc") return;
40
+ swcInitialized = true;
41
+ if (!currentArtifact || !pluginSession) return;
42
+ try {
43
+ const { createTransformer } = await import("@soda-gql/swc-transformer");
44
+ swcTransformer = await createTransformer({
45
+ config: pluginSession.config,
46
+ artifact: currentArtifact,
47
+ sourceMap: true
48
+ });
49
+ setSharedSwcTransformer(stateKey, swcTransformer);
50
+ log("SWC transformer initialized");
51
+ } catch (error) {
52
+ console.warn(`[@soda-gql/vite-plugin] Failed to initialize SWC transformer: ${error}. Make sure @soda-gql/swc-transformer is installed. Falling back to Babel.`);
53
+ swcTransformer = null;
54
+ }
55
+ };
56
+ /**
57
+ * Check if a file path corresponds to a soda-gql source file.
58
+ */
59
+ const isSodaGqlFile = (filePath) => {
60
+ if (!currentArtifact) return false;
61
+ const normalized = normalizePath(filePath);
62
+ for (const element of Object.values(currentArtifact.elements)) if (normalizePath(element.metadata.sourcePath) === normalized) return true;
63
+ return false;
64
+ };
65
+ /**
66
+ * Check if artifact has changed by comparing element counts and hashes.
67
+ */
68
+ const hasArtifactChanged = () => {
69
+ if (!previousArtifact || !currentArtifact) return true;
70
+ if (Object.keys(previousArtifact.elements).length !== Object.keys(currentArtifact.elements).length) return true;
71
+ const prevElements = previousArtifact.elements;
72
+ const currElements = currentArtifact.elements;
73
+ for (const [id, element] of Object.entries(currElements)) {
74
+ const prevElement = prevElements[id];
75
+ if (!prevElement) return true;
76
+ if (element.metadata.contentHash !== prevElement.metadata.contentHash) return true;
77
+ }
78
+ return false;
79
+ };
80
+ /**
81
+ * Get files that changed between previous and current artifact.
82
+ */
83
+ const getChangedSodaGqlFiles = () => {
84
+ const changed = /* @__PURE__ */ new Set();
85
+ if (!previousArtifact || !currentArtifact) return changed;
86
+ const prevElements = previousArtifact.elements;
87
+ const currElements = currentArtifact.elements;
88
+ for (const [id, element] of Object.entries(currElements)) {
89
+ const prevElement = prevElements[id];
90
+ const sourcePath = element.metadata.sourcePath;
91
+ if (!prevElement || prevElement.metadata.contentHash !== element.metadata.contentHash) changed.add(normalizePath(sourcePath));
92
+ }
93
+ for (const [id, element] of Object.entries(prevElements)) if (!currElements[id]) {
94
+ const sourcePath = element.metadata.sourcePath;
95
+ changed.add(normalizePath(sourcePath));
96
+ }
97
+ return changed;
98
+ };
99
+ return {
100
+ name: "@soda-gql/vite-plugin",
101
+ enforce: "pre",
102
+ configResolved(config) {
103
+ isDevMode = config.command === "serve";
104
+ log(`Mode: ${isDevMode ? "development" : "production"}`);
105
+ },
106
+ async buildStart() {
107
+ pluginSession = createPluginSession(options, "@soda-gql/vite-plugin");
108
+ if (!pluginSession) {
109
+ log("Plugin disabled or config load failed");
110
+ return;
111
+ }
112
+ setSharedPluginSession(stateKey, pluginSession);
113
+ currentArtifact = await pluginSession.getArtifactAsync();
114
+ setSharedArtifact(stateKey, currentArtifact);
115
+ log(`Initial build: ${Object.keys(currentArtifact?.elements ?? {}).length} elements`);
116
+ await initializeSwcTransformer();
117
+ },
118
+ configureServer(server) {
119
+ log("Dev server configured");
120
+ },
121
+ async transform(code, id) {
122
+ if (!/\.[jt]sx?$/.test(id)) return null;
123
+ if (id.includes("node_modules")) return null;
124
+ if (!pluginSession || !currentArtifact) return null;
125
+ const normalizedPath = normalizePath(id);
126
+ if (!Object.values(currentArtifact.elements).some((element) => normalizePath(element.metadata.sourcePath) === normalizedPath)) return null;
127
+ log(`Transforming: ${normalizedPath}`);
128
+ if (swcTransformer) {
129
+ const swcResult = swcTransformer.transform({
130
+ sourceCode: code,
131
+ sourcePath: id
132
+ });
133
+ if (swcResult.transformed) return {
134
+ code: swcResult.sourceCode,
135
+ map: swcResult.sourceMap ? JSON.parse(swcResult.sourceMap) : void 0
136
+ };
137
+ return null;
138
+ }
139
+ const result = transformSync(code, {
140
+ filename: id,
141
+ babelrc: false,
142
+ configFile: false,
143
+ plugins: [createPluginWithArtifact({
144
+ artifact: currentArtifact,
145
+ config: pluginSession.config
146
+ })],
147
+ sourceMaps: true
148
+ });
149
+ if (result?.code) return {
150
+ code: result.code,
151
+ map: result.map
152
+ };
153
+ return null;
154
+ },
155
+ buildEnd() {
156
+ if (!isDevMode) {
157
+ log("Production build complete, cleaning up");
158
+ pluginSession = null;
159
+ currentArtifact = null;
160
+ previousArtifact = null;
161
+ swcTransformer = null;
162
+ setSharedPluginSession(stateKey, null);
163
+ setSharedArtifact(stateKey, null);
164
+ setSharedSwcTransformer(stateKey, null);
165
+ }
166
+ },
167
+ async handleHotUpdate(ctx) {
168
+ const { file, server, modules } = ctx;
169
+ const normalizedPath = normalizePath(file);
170
+ if (!pluginSession || !currentArtifact) return;
171
+ if (!isSodaGqlFile(normalizedPath)) return;
172
+ log(`soda-gql file changed: ${normalizedPath}`);
173
+ previousArtifact = currentArtifact;
174
+ currentArtifact = await pluginSession.getArtifactAsync();
175
+ setSharedArtifact(stateKey, currentArtifact);
176
+ if (!currentArtifact) return;
177
+ if (!hasArtifactChanged()) {
178
+ log("Artifact unchanged, using normal HMR");
179
+ return;
180
+ }
181
+ const sharedState = getSharedState$1(stateKey);
182
+ const changedFiles = getChangedSodaGqlFiles();
183
+ const affectedFiles = collectAffectedFiles({
184
+ changedFiles,
185
+ removedFiles: /* @__PURE__ */ new Set(),
186
+ previousModuleAdjacency: sharedState.moduleAdjacency
187
+ });
188
+ log(`Changed files: ${changedFiles.size}, Affected files: ${affectedFiles.size}`);
189
+ const affectedModules = /* @__PURE__ */ new Set();
190
+ for (const affectedPath of affectedFiles) {
191
+ const modulesByFile = server.moduleGraph.getModulesByFile(affectedPath);
192
+ if (modulesByFile) for (const mod of modulesByFile) affectedModules.add(mod);
193
+ }
194
+ for (const mod of modules) affectedModules.add(mod);
195
+ if (affectedModules.size > 0) {
196
+ log(`Invalidating ${affectedModules.size} modules for HMR`);
197
+ return [...affectedModules];
198
+ }
199
+ return modules;
200
+ }
201
+ };
202
+ };
203
+
204
+ //#endregion
205
+ //#region packages/vite-plugin/src/index.ts
206
+ /**
207
+ * Convenience alias for sodaGqlPlugin.
208
+ * @see {@link sodaGqlPlugin}
209
+ */
210
+ const withSodaGql = sodaGqlPlugin;
211
+
212
+ //#endregion
213
+ export { getSharedArtifact, getSharedState, getStateKey, sodaGqlPlugin, withSodaGql };
214
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":["getStateKey","pluginSession: PluginSession | null","currentArtifact: BuilderArtifact | null","previousArtifact: BuilderArtifact | null","swcTransformer: SwcTransformerInterface | null","getSharedState","_sodaGqlPlugin"],"sources":["../src/plugin.ts","../src/index.ts"],"sourcesContent":["import { type TransformOptions, transformSync } from \"@babel/core\";\nimport { createPluginWithArtifact } from \"@soda-gql/babel-plugin\";\nimport { type BuilderArtifact, type BuilderArtifactElement, collectAffectedFiles } from \"@soda-gql/builder\";\nimport { normalizePath } from \"@soda-gql/common\";\nimport {\n createPluginSession,\n getSharedState,\n getStateKey,\n type PluginSession,\n type SwcTransformerInterface,\n setSharedArtifact,\n setSharedPluginSession,\n setSharedSwcTransformer,\n} from \"@soda-gql/plugin-common\";\nimport type { HmrContext, ModuleNode, Plugin, ViteDevServer } from \"vite\";\nimport type { VitePluginOptions } from \"./types\";\n\n/**\n * Vite plugin for soda-gql that handles GraphQL code transformations\n * at build time with HMR support for development.\n *\n * @example\n * ```typescript\n * // vite.config.ts\n * import { defineConfig } from \"vite\";\n * import react from \"@vitejs/plugin-react\";\n * import { sodaGqlPlugin } from \"@soda-gql/vite-plugin\";\n *\n * export default defineConfig({\n * plugins: [sodaGqlPlugin({ debug: true }), react()],\n * });\n * ```\n */\nexport const sodaGqlPlugin = (options: VitePluginOptions = {}): Plugin => {\n const stateKey = getStateKey(options.configPath);\n\n let pluginSession: PluginSession | null = null;\n let currentArtifact: BuilderArtifact | null = null;\n let previousArtifact: BuilderArtifact | null = null;\n let _viteServer: ViteDevServer | null = null;\n let isDevMode = false;\n let swcTransformer: SwcTransformerInterface | null = null;\n let swcInitialized = false;\n\n const log = (message: string): void => {\n if (options.debug) {\n console.log(`[@soda-gql/vite-plugin] ${message}`);\n }\n };\n\n /**\n * Initialize SWC transformer if configured.\n */\n const initializeSwcTransformer = async (): Promise<void> => {\n if (swcInitialized || options.transformer !== \"swc\") {\n return;\n }\n\n swcInitialized = true;\n\n if (!currentArtifact || !pluginSession) {\n return;\n }\n\n try {\n const { createTransformer } = await import(\"@soda-gql/swc-transformer\");\n swcTransformer = await createTransformer({\n config: pluginSession.config,\n artifact: currentArtifact,\n sourceMap: true,\n });\n setSharedSwcTransformer(stateKey, swcTransformer);\n log(\"SWC transformer initialized\");\n } catch (error) {\n console.warn(\n `[@soda-gql/vite-plugin] Failed to initialize SWC transformer: ${error}. ` +\n \"Make sure @soda-gql/swc-transformer is installed. Falling back to Babel.\",\n );\n swcTransformer = null;\n }\n };\n\n /**\n * Check if a file path corresponds to a soda-gql source file.\n */\n const isSodaGqlFile = (filePath: string): boolean => {\n if (!currentArtifact) return false;\n\n const normalized = normalizePath(filePath);\n for (const element of Object.values(currentArtifact.elements)) {\n if (normalizePath(element.metadata.sourcePath) === normalized) {\n return true;\n }\n }\n return false;\n };\n\n /**\n * Check if artifact has changed by comparing element counts and hashes.\n */\n const hasArtifactChanged = (): boolean => {\n if (!previousArtifact || !currentArtifact) return true;\n\n const prevCount = Object.keys(previousArtifact.elements).length;\n const newCount = Object.keys(currentArtifact.elements).length;\n if (prevCount !== newCount) return true;\n\n // Compare individual elements by their content hash\n const prevElements = previousArtifact.elements as Record<string, BuilderArtifactElement>;\n const currElements = currentArtifact.elements as Record<string, BuilderArtifactElement>;\n\n for (const [id, element] of Object.entries(currElements)) {\n const prevElement = prevElements[id];\n if (!prevElement) return true;\n if (element.metadata.contentHash !== prevElement.metadata.contentHash) {\n return true;\n }\n }\n\n return false;\n };\n\n /**\n * Get files that changed between previous and current artifact.\n */\n const getChangedSodaGqlFiles = (): Set<string> => {\n const changed = new Set<string>();\n\n if (!previousArtifact || !currentArtifact) return changed;\n\n const prevElements = previousArtifact.elements as Record<string, BuilderArtifactElement>;\n const currElements = currentArtifact.elements as Record<string, BuilderArtifactElement>;\n\n // Compare elements by their source paths and content hashes\n for (const [id, element] of Object.entries(currElements)) {\n const prevElement = prevElements[id];\n const sourcePath = element.metadata.sourcePath;\n\n if (!prevElement || prevElement.metadata.contentHash !== element.metadata.contentHash) {\n changed.add(normalizePath(sourcePath));\n }\n }\n\n // Check for removed elements\n for (const [id, element] of Object.entries(prevElements)) {\n if (!currElements[id]) {\n const sourcePath = element.metadata.sourcePath;\n changed.add(normalizePath(sourcePath));\n }\n }\n\n return changed;\n };\n\n return {\n name: \"@soda-gql/vite-plugin\",\n enforce: \"pre\", // Run before other plugins to transform source early\n\n configResolved(config) {\n isDevMode = config.command === \"serve\";\n log(`Mode: ${isDevMode ? \"development\" : \"production\"}`);\n },\n\n async buildStart() {\n // Initialize plugin session\n pluginSession = createPluginSession(options, \"@soda-gql/vite-plugin\");\n if (!pluginSession) {\n log(\"Plugin disabled or config load failed\");\n return;\n }\n\n setSharedPluginSession(stateKey, pluginSession);\n\n // Build initial artifact\n currentArtifact = await pluginSession.getArtifactAsync();\n setSharedArtifact(stateKey, currentArtifact);\n\n log(`Initial build: ${Object.keys(currentArtifact?.elements ?? {}).length} elements`);\n\n // Initialize SWC transformer if configured\n await initializeSwcTransformer();\n },\n\n configureServer(server) {\n _viteServer = server;\n log(\"Dev server configured\");\n },\n\n async transform(code, id) {\n // Skip non-JS/TS files\n if (!/\\.[jt]sx?$/.test(id)) {\n return null;\n }\n\n // Skip node_modules\n if (id.includes(\"node_modules\")) {\n return null;\n }\n\n // Skip if plugin is disabled or no artifact\n if (!pluginSession || !currentArtifact) {\n return null;\n }\n\n // Check if this file contains any soda-gql elements\n const normalizedPath = normalizePath(id);\n const hasElements = Object.values(currentArtifact.elements).some(\n (element) => normalizePath(element.metadata.sourcePath) === normalizedPath,\n );\n\n if (!hasElements) {\n return null; // Not a soda-gql file\n }\n\n log(`Transforming: ${normalizedPath}`);\n\n // Try SWC transformer first if available\n if (swcTransformer) {\n const swcResult = swcTransformer.transform({\n sourceCode: code,\n sourcePath: id,\n });\n\n if (swcResult.transformed) {\n return {\n code: swcResult.sourceCode,\n map: swcResult.sourceMap ? JSON.parse(swcResult.sourceMap) : undefined,\n };\n }\n // SWC didn't transform (no soda-gql code), return null to pass through\n return null;\n }\n\n // Fall back to Babel transformer\n const babelOptions: TransformOptions = {\n filename: id,\n babelrc: false,\n configFile: false,\n plugins: [createPluginWithArtifact({ artifact: currentArtifact, config: pluginSession.config })],\n sourceMaps: true,\n };\n\n const result = transformSync(code, babelOptions);\n\n if (result?.code) {\n return {\n code: result.code,\n map: result.map,\n };\n }\n\n return null;\n },\n\n buildEnd() {\n if (!isDevMode) {\n // Cleanup for production builds\n log(\"Production build complete, cleaning up\");\n pluginSession = null;\n currentArtifact = null;\n previousArtifact = null;\n swcTransformer = null;\n setSharedPluginSession(stateKey, null);\n setSharedArtifact(stateKey, null);\n setSharedSwcTransformer(stateKey, null);\n }\n },\n\n async handleHotUpdate(ctx: HmrContext): Promise<ModuleNode[] | void> {\n const { file, server, modules } = ctx;\n const normalizedPath = normalizePath(file);\n\n if (!pluginSession || !currentArtifact) {\n return; // Let Vite handle normally\n }\n\n // Check if the changed file is a soda-gql source file\n if (!isSodaGqlFile(normalizedPath)) {\n return; // Not a soda-gql file, let Vite handle normally\n }\n\n log(`soda-gql file changed: ${normalizedPath}`);\n\n // Store previous artifact for change detection\n previousArtifact = currentArtifact;\n\n // Rebuild artifact to detect changes\n currentArtifact = await pluginSession.getArtifactAsync();\n setSharedArtifact(stateKey, currentArtifact);\n\n if (!currentArtifact) {\n return;\n }\n\n // If artifact hasn't changed, just let normal HMR happen\n if (!hasArtifactChanged()) {\n log(\"Artifact unchanged, using normal HMR\");\n return;\n }\n\n // Compute affected files using module adjacency\n const sharedState = getSharedState(stateKey);\n const changedFiles = getChangedSodaGqlFiles();\n\n const affectedFiles = collectAffectedFiles({\n changedFiles,\n removedFiles: new Set(),\n previousModuleAdjacency: sharedState.moduleAdjacency,\n });\n\n log(`Changed files: ${changedFiles.size}, Affected files: ${affectedFiles.size}`);\n\n // Convert affected file paths to Vite module nodes\n const affectedModules = new Set<ModuleNode>();\n\n for (const affectedPath of affectedFiles) {\n // Try to get module by file path\n const modulesByFile = server.moduleGraph.getModulesByFile(affectedPath);\n if (modulesByFile) {\n for (const mod of modulesByFile) {\n affectedModules.add(mod);\n }\n }\n }\n\n // Include original modules\n for (const mod of modules) {\n affectedModules.add(mod);\n }\n\n if (affectedModules.size > 0) {\n log(`Invalidating ${affectedModules.size} modules for HMR`);\n return [...affectedModules];\n }\n\n // Fall back to original modules\n return modules;\n },\n };\n};\n","// Re-export shared state utilities for advanced usage\nexport { getSharedArtifact, getSharedState, getStateKey } from \"@soda-gql/plugin-common\";\nexport { sodaGqlPlugin } from \"./plugin\";\nexport type { TransformerType, VitePluginOptions } from \"./types\";\n\n// Re-import for convenience aliases\nimport { sodaGqlPlugin as _sodaGqlPlugin } from \"./plugin\";\n\n/**\n * Convenience alias for sodaGqlPlugin.\n * @see {@link sodaGqlPlugin}\n */\nexport const withSodaGql = _sodaGqlPlugin;\n\nexport default _sodaGqlPlugin;\n"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "@soda-gql/vite-plugin",
3
+ "version": "0.2.0",
4
+ "description": "Vite plugin for soda-gql",
5
+ "type": "module",
6
+ "private": false,
7
+ "license": "MIT",
8
+ "files": [
9
+ "dist"
10
+ ],
11
+ "author": {
12
+ "name": "Shota Hatada",
13
+ "email": "shota.hatada@whatasoda.me",
14
+ "url": "https://github.com/whatasoda"
15
+ },
16
+ "keywords": [
17
+ "graphql",
18
+ "codegen",
19
+ "zero-runtime",
20
+ "typescript",
21
+ "vite",
22
+ "vite-plugin"
23
+ ],
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "https://github.com/whatasoda/soda-gql.git",
27
+ "directory": "packages/vite-plugin"
28
+ },
29
+ "homepage": "https://github.com/whatasoda/soda-gql#readme",
30
+ "bugs": {
31
+ "url": "https://github.com/whatasoda/soda-gql/issues"
32
+ },
33
+ "engines": {
34
+ "node": ">=18"
35
+ },
36
+ "main": "./dist/index.mjs",
37
+ "module": "./dist/index.mjs",
38
+ "types": "./dist/index.d.mts",
39
+ "exports": {
40
+ ".": {
41
+ "@soda-gql": "./@x-index.ts",
42
+ "types": "./dist/index.d.mts",
43
+ "import": "./dist/index.mjs",
44
+ "require": "./dist/index.cjs",
45
+ "default": "./dist/index.mjs"
46
+ },
47
+ "./package.json": "./package.json"
48
+ },
49
+ "dependencies": {
50
+ "@babel/core": "^7.24.0",
51
+ "@soda-gql/babel-plugin": "0.2.0",
52
+ "@soda-gql/builder": "0.2.0",
53
+ "@soda-gql/common": "0.2.0",
54
+ "@soda-gql/config": "0.2.0",
55
+ "@soda-gql/plugin-common": "0.2.0"
56
+ },
57
+ "devDependencies": {},
58
+ "peerDependencies": {
59
+ "vite": "^5.0.0 || ^6.0.0",
60
+ "@soda-gql/swc-transformer": "0.2.0"
61
+ }
62
+ }