@soda-gql/webpack-plugin 0.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/README.md ADDED
@@ -0,0 +1,130 @@
1
+ # @soda-gql/webpack-plugin
2
+
3
+ Webpack plugin for soda-gql with incremental rebuild and HMR support.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @soda-gql/webpack-plugin
9
+ # or
10
+ yarn add @soda-gql/webpack-plugin
11
+ # or
12
+ bun add @soda-gql/webpack-plugin
13
+ ```
14
+
15
+ ## Usage
16
+
17
+ ### webpack.config.js
18
+
19
+ ```javascript
20
+ const { SodaGqlWebpackPlugin } = require("@soda-gql/webpack-plugin");
21
+
22
+ module.exports = {
23
+ plugins: [
24
+ new SodaGqlWebpackPlugin({
25
+ // Optional: Path to soda-gql config file
26
+ configPath: "./soda-gql.config.ts",
27
+
28
+ // Optional: Enable/disable the plugin (default: true)
29
+ enabled: process.env.NODE_ENV !== "test",
30
+
31
+ // Optional: Enable debug logging (default: false)
32
+ debug: process.env.DEBUG === "true",
33
+ }),
34
+ ],
35
+
36
+ module: {
37
+ rules: [
38
+ {
39
+ test: /\.tsx?$/,
40
+ exclude: /node_modules/,
41
+ use: [
42
+ // Your existing loaders (ts-loader, babel-loader, etc.)
43
+ "ts-loader",
44
+
45
+ // Add soda-gql loader
46
+ {
47
+ loader: "@soda-gql/webpack-plugin/loader",
48
+ options: {
49
+ // Same options as plugin
50
+ configPath: "./soda-gql.config.ts",
51
+ },
52
+ },
53
+ ],
54
+ },
55
+ ],
56
+ },
57
+ };
58
+ ```
59
+
60
+ ### With webpack-dev-server
61
+
62
+ The plugin automatically handles HMR (Hot Module Replacement) for soda-gql files.
63
+ When a model file changes, all dependent slices and operations are automatically rebuilt.
64
+
65
+ ```javascript
66
+ // webpack.config.js
67
+ module.exports = {
68
+ devServer: {
69
+ hot: true,
70
+ },
71
+ plugins: [
72
+ new SodaGqlWebpackPlugin({
73
+ debug: true, // Enable to see rebuild logs
74
+ }),
75
+ ],
76
+ // ... rest of config
77
+ };
78
+ ```
79
+
80
+ ## How It Works
81
+
82
+ 1. **Plugin Initialization**: On `beforeRun` hook, the plugin initializes and builds the initial artifact.
83
+
84
+ 2. **Watch Mode**: On `watchRun` hook, the plugin:
85
+ - Rebuilds the artifact (detects file changes automatically)
86
+ - Computes which files are affected by the changes
87
+ - Shares the updated artifact with the loader
88
+
89
+ 3. **Loader Processing**: The loader:
90
+ - Uses the pre-built artifact from the plugin (shared state)
91
+ - Transforms soda-gql code using the Babel plugin
92
+ - Adds file dependencies for proper HMR propagation
93
+
94
+ ## Options
95
+
96
+ ### SodaGqlWebpackPlugin Options
97
+
98
+ | Option | Type | Default | Description |
99
+ |--------|------|---------|-------------|
100
+ | `configPath` | `string` | `undefined` | Path to soda-gql config file |
101
+ | `enabled` | `boolean` | `true` | Enable/disable the plugin |
102
+ | `include` | `RegExp \| RegExp[]` | `undefined` | File patterns to include |
103
+ | `exclude` | `RegExp \| RegExp[]` | `undefined` | File patterns to exclude |
104
+ | `debug` | `boolean` | `false` | Enable verbose logging |
105
+
106
+ ### Loader Options
107
+
108
+ | Option | Type | Default | Description |
109
+ |--------|------|---------|-------------|
110
+ | `configPath` | `string` | `undefined` | Path to soda-gql config file |
111
+ | `enabled` | `boolean` | `true` | Enable/disable the loader |
112
+
113
+ ## Dependency Tracking
114
+
115
+ The plugin tracks dependencies between soda-gql files:
116
+
117
+ - **Models**: Base definitions (no dependencies)
118
+ - **Slices**: Reference models via `model.fragment()` and `model.normalize()`
119
+ - **Operations**: Reference slices via `slice.embed()`
120
+
121
+ When a model file changes:
122
+ 1. The model is rebuilt
123
+ 2. All slices that use the model are rebuilt
124
+ 3. All operations that use those slices are rebuilt
125
+
126
+ This ensures that changes propagate correctly through the dependency chain.
127
+
128
+ ## License
129
+
130
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1,194 @@
1
+ let __soda_gql_plugin_common = require("@soda-gql/plugin-common");
2
+ let __soda_gql_builder = require("@soda-gql/builder");
3
+ let __soda_gql_common = require("@soda-gql/common");
4
+
5
+ //#region packages/webpack-plugin/src/plugin.ts
6
+ /**
7
+ * Webpack plugin for soda-gql that handles incremental rebuilds
8
+ * when model files change during dev server execution.
9
+ */
10
+ var SodaGqlWebpackPlugin = class SodaGqlWebpackPlugin {
11
+ static pluginName = "SodaGqlWebpackPlugin";
12
+ options;
13
+ stateKey;
14
+ pluginSession = null;
15
+ currentArtifact = null;
16
+ previousArtifact = null;
17
+ pendingInvalidations = /* @__PURE__ */ new Set();
18
+ constructor(options = {}) {
19
+ this.options = options;
20
+ this.stateKey = (0, __soda_gql_plugin_common.getStateKey)(options.configPath);
21
+ }
22
+ apply(compiler) {
23
+ compiler.hooks.beforeRun.tapAsync(SodaGqlWebpackPlugin.pluginName, async (_compiler, callback) => {
24
+ try {
25
+ await this.initialize();
26
+ callback();
27
+ } catch (error) {
28
+ callback(error);
29
+ }
30
+ });
31
+ compiler.hooks.watchRun.tapAsync(SodaGqlWebpackPlugin.pluginName, async (_compiler, callback) => {
32
+ try {
33
+ await this.handleWatchRun();
34
+ callback();
35
+ } catch (error) {
36
+ callback(error);
37
+ }
38
+ });
39
+ compiler.hooks.invalid.tap(SodaGqlWebpackPlugin.pluginName, (fileName, _changeTime) => {
40
+ if (fileName) this.handleFileInvalidation(fileName);
41
+ });
42
+ compiler.hooks.watchClose.tap(SodaGqlWebpackPlugin.pluginName, () => {
43
+ this.cleanup();
44
+ });
45
+ }
46
+ /**
47
+ * Initialize plugin session and build initial artifact.
48
+ */
49
+ async initialize() {
50
+ if (this.pluginSession) return;
51
+ this.pluginSession = (0, __soda_gql_plugin_common.createPluginSession)(this.options, SodaGqlWebpackPlugin.pluginName);
52
+ if (!this.pluginSession) {
53
+ this.log("Plugin disabled or config load failed");
54
+ return;
55
+ }
56
+ (0, __soda_gql_plugin_common.setSharedPluginSession)(this.stateKey, this.pluginSession);
57
+ this.currentArtifact = await this.pluginSession.getArtifactAsync();
58
+ (0, __soda_gql_plugin_common.setSharedArtifact)(this.stateKey, this.currentArtifact);
59
+ this.log(`Initial build complete: ${Object.keys(this.currentArtifact?.elements ?? {}).length} elements`);
60
+ }
61
+ /**
62
+ * Handle watch mode run - rebuild artifact and compute affected files.
63
+ */
64
+ async handleWatchRun() {
65
+ if (!this.pluginSession) await this.initialize();
66
+ if (!this.pluginSession) return;
67
+ this.previousArtifact = this.currentArtifact;
68
+ this.currentArtifact = await this.pluginSession.getArtifactAsync();
69
+ (0, __soda_gql_plugin_common.setSharedArtifact)(this.stateKey, this.currentArtifact);
70
+ if (!this.currentArtifact) {
71
+ this.log("Failed to build artifact");
72
+ return;
73
+ }
74
+ if (this.previousArtifact && this.hasArtifactChanged()) {
75
+ const changedFiles = this.getChangedSodaGqlFiles();
76
+ const sharedState = (0, __soda_gql_plugin_common.getSharedState)(this.stateKey);
77
+ const affectedFiles = this.computeAffectedFiles(changedFiles, sharedState.moduleAdjacency);
78
+ this.log(`Changed files: ${changedFiles.size}, Affected files: ${affectedFiles.size}`);
79
+ for (const filePath of affectedFiles) this.pendingInvalidations.add(filePath);
80
+ }
81
+ }
82
+ /**
83
+ * Handle file invalidation event from webpack.
84
+ */
85
+ handleFileInvalidation(fileName) {
86
+ const normalized = (0, __soda_gql_common.normalizePath)(fileName);
87
+ if (this.isSodaGqlFile(normalized)) this.log(`soda-gql file changed: ${normalized}`);
88
+ }
89
+ /**
90
+ * Check if artifact has changed by comparing element counts and hashes.
91
+ */
92
+ hasArtifactChanged() {
93
+ if (!this.previousArtifact || !this.currentArtifact) return true;
94
+ if (Object.keys(this.previousArtifact.elements).length !== Object.keys(this.currentArtifact.elements).length) return true;
95
+ const prevElements = this.previousArtifact.elements;
96
+ const currElements = this.currentArtifact.elements;
97
+ for (const [id, element] of Object.entries(currElements)) {
98
+ const prevElement = prevElements[id];
99
+ if (!prevElement) return true;
100
+ if (element.metadata.contentHash !== prevElement.metadata.contentHash) return true;
101
+ }
102
+ return false;
103
+ }
104
+ /**
105
+ * Get files that changed between previous and current artifact.
106
+ */
107
+ getChangedSodaGqlFiles() {
108
+ const changed = /* @__PURE__ */ new Set();
109
+ if (!this.previousArtifact || !this.currentArtifact) return changed;
110
+ const prevElements = this.previousArtifact.elements;
111
+ const currElements = this.currentArtifact.elements;
112
+ for (const [id, element] of Object.entries(currElements)) {
113
+ const prevElement = prevElements[id];
114
+ const sourcePath = element.metadata.sourcePath;
115
+ if (!prevElement || prevElement.metadata.contentHash !== element.metadata.contentHash) changed.add((0, __soda_gql_common.normalizePath)(sourcePath));
116
+ }
117
+ for (const [id, element] of Object.entries(prevElements)) if (!currElements[id]) {
118
+ const sourcePath = element.metadata.sourcePath;
119
+ changed.add((0, __soda_gql_common.normalizePath)(sourcePath));
120
+ }
121
+ return changed;
122
+ }
123
+ /**
124
+ * Compute all files affected by the changed files using module adjacency.
125
+ */
126
+ computeAffectedFiles(changedFiles, moduleAdjacency) {
127
+ return (0, __soda_gql_builder.collectAffectedFiles)({
128
+ changedFiles,
129
+ removedFiles: /* @__PURE__ */ new Set(),
130
+ previousModuleAdjacency: moduleAdjacency
131
+ });
132
+ }
133
+ /**
134
+ * Check if a file path corresponds to a soda-gql source file.
135
+ */
136
+ isSodaGqlFile(filePath) {
137
+ if (!this.currentArtifact) return false;
138
+ const normalized = (0, __soda_gql_common.normalizePath)(filePath);
139
+ for (const element of Object.values(this.currentArtifact.elements)) if ((0, __soda_gql_common.normalizePath)(element.metadata.sourcePath) === normalized) return true;
140
+ return false;
141
+ }
142
+ /**
143
+ * Get pending invalidations and clear the set.
144
+ */
145
+ getPendingInvalidations() {
146
+ const invalidations = new Set(this.pendingInvalidations);
147
+ this.pendingInvalidations.clear();
148
+ return invalidations;
149
+ }
150
+ /**
151
+ * Cleanup resources.
152
+ */
153
+ cleanup() {
154
+ this.pluginSession = null;
155
+ this.currentArtifact = null;
156
+ this.previousArtifact = null;
157
+ this.pendingInvalidations.clear();
158
+ (0, __soda_gql_plugin_common.setSharedPluginSession)(this.stateKey, null);
159
+ (0, __soda_gql_plugin_common.setSharedArtifact)(this.stateKey, null);
160
+ }
161
+ /**
162
+ * Log a message if debug mode is enabled.
163
+ */
164
+ log(message) {
165
+ if (this.options.debug) console.log(`[${SodaGqlWebpackPlugin.pluginName}] ${message}`);
166
+ }
167
+ /**
168
+ * Get the current artifact (for use by loader).
169
+ */
170
+ getArtifact() {
171
+ return this.currentArtifact;
172
+ }
173
+ };
174
+
175
+ //#endregion
176
+ exports.SodaGqlWebpackPlugin = SodaGqlWebpackPlugin;
177
+ Object.defineProperty(exports, 'getSharedArtifact', {
178
+ enumerable: true,
179
+ get: function () {
180
+ return __soda_gql_plugin_common.getSharedArtifact;
181
+ }
182
+ });
183
+ Object.defineProperty(exports, 'getSharedState', {
184
+ enumerable: true,
185
+ get: function () {
186
+ return __soda_gql_plugin_common.getSharedState;
187
+ }
188
+ });
189
+ Object.defineProperty(exports, 'getStateKey', {
190
+ enumerable: true,
191
+ get: function () {
192
+ return __soda_gql_plugin_common.getStateKey;
193
+ }
194
+ });
@@ -0,0 +1,69 @@
1
+ import { n as WebpackPluginOptions, t as WebpackLoaderOptions } from "./types-Dh6GvYnd.cjs";
2
+ import { getSharedArtifact, getSharedState, getStateKey } from "@soda-gql/plugin-common";
3
+ import { BuilderArtifact } from "@soda-gql/builder";
4
+ import { Compiler } from "webpack";
5
+
6
+ //#region packages/webpack-plugin/src/plugin.d.ts
7
+
8
+ /**
9
+ * Webpack plugin for soda-gql that handles incremental rebuilds
10
+ * when model files change during dev server execution.
11
+ */
12
+ declare class SodaGqlWebpackPlugin {
13
+ static readonly pluginName = "SodaGqlWebpackPlugin";
14
+ private readonly options;
15
+ private readonly stateKey;
16
+ private pluginSession;
17
+ private currentArtifact;
18
+ private previousArtifact;
19
+ private pendingInvalidations;
20
+ constructor(options?: WebpackPluginOptions);
21
+ apply(compiler: Compiler): void;
22
+ /**
23
+ * Initialize plugin session and build initial artifact.
24
+ */
25
+ private initialize;
26
+ /**
27
+ * Handle watch mode run - rebuild artifact and compute affected files.
28
+ */
29
+ private handleWatchRun;
30
+ /**
31
+ * Handle file invalidation event from webpack.
32
+ */
33
+ private handleFileInvalidation;
34
+ /**
35
+ * Check if artifact has changed by comparing element counts and hashes.
36
+ */
37
+ private hasArtifactChanged;
38
+ /**
39
+ * Get files that changed between previous and current artifact.
40
+ */
41
+ private getChangedSodaGqlFiles;
42
+ /**
43
+ * Compute all files affected by the changed files using module adjacency.
44
+ */
45
+ private computeAffectedFiles;
46
+ /**
47
+ * Check if a file path corresponds to a soda-gql source file.
48
+ */
49
+ private isSodaGqlFile;
50
+ /**
51
+ * Get pending invalidations and clear the set.
52
+ */
53
+ getPendingInvalidations(): Set<string>;
54
+ /**
55
+ * Cleanup resources.
56
+ */
57
+ private cleanup;
58
+ /**
59
+ * Log a message if debug mode is enabled.
60
+ */
61
+ private log;
62
+ /**
63
+ * Get the current artifact (for use by loader).
64
+ */
65
+ getArtifact(): BuilderArtifact | null;
66
+ }
67
+ //#endregion
68
+ export { SodaGqlWebpackPlugin, type WebpackLoaderOptions, type WebpackPluginOptions, getSharedArtifact, getSharedState, getStateKey };
69
+ //# sourceMappingURL=index.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../src/plugin.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;AAkBA;AAUuB,cAVV,oBAAA,CAUU;EAKL,gBAAA,UAAA,GAAA,sBAAA;EAmMW,iBAAA,OAAA;EA8BZ,iBAAA,QAAA;EAAe,QAAA,aAAA;;;;wBAtOT;kBAKL;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6BAmMW;;;;;;;;;;;;iBA8BZ"}
@@ -0,0 +1,69 @@
1
+ import { n as WebpackPluginOptions, t as WebpackLoaderOptions } from "./types-o73wYce8.mjs";
2
+ import { getSharedArtifact, getSharedState, getStateKey } from "@soda-gql/plugin-common";
3
+ import { BuilderArtifact } from "@soda-gql/builder";
4
+ import { Compiler } from "webpack";
5
+
6
+ //#region packages/webpack-plugin/src/plugin.d.ts
7
+
8
+ /**
9
+ * Webpack plugin for soda-gql that handles incremental rebuilds
10
+ * when model files change during dev server execution.
11
+ */
12
+ declare class SodaGqlWebpackPlugin {
13
+ static readonly pluginName = "SodaGqlWebpackPlugin";
14
+ private readonly options;
15
+ private readonly stateKey;
16
+ private pluginSession;
17
+ private currentArtifact;
18
+ private previousArtifact;
19
+ private pendingInvalidations;
20
+ constructor(options?: WebpackPluginOptions);
21
+ apply(compiler: Compiler): void;
22
+ /**
23
+ * Initialize plugin session and build initial artifact.
24
+ */
25
+ private initialize;
26
+ /**
27
+ * Handle watch mode run - rebuild artifact and compute affected files.
28
+ */
29
+ private handleWatchRun;
30
+ /**
31
+ * Handle file invalidation event from webpack.
32
+ */
33
+ private handleFileInvalidation;
34
+ /**
35
+ * Check if artifact has changed by comparing element counts and hashes.
36
+ */
37
+ private hasArtifactChanged;
38
+ /**
39
+ * Get files that changed between previous and current artifact.
40
+ */
41
+ private getChangedSodaGqlFiles;
42
+ /**
43
+ * Compute all files affected by the changed files using module adjacency.
44
+ */
45
+ private computeAffectedFiles;
46
+ /**
47
+ * Check if a file path corresponds to a soda-gql source file.
48
+ */
49
+ private isSodaGqlFile;
50
+ /**
51
+ * Get pending invalidations and clear the set.
52
+ */
53
+ getPendingInvalidations(): Set<string>;
54
+ /**
55
+ * Cleanup resources.
56
+ */
57
+ private cleanup;
58
+ /**
59
+ * Log a message if debug mode is enabled.
60
+ */
61
+ private log;
62
+ /**
63
+ * Get the current artifact (for use by loader).
64
+ */
65
+ getArtifact(): BuilderArtifact | null;
66
+ }
67
+ //#endregion
68
+ export { SodaGqlWebpackPlugin, type WebpackLoaderOptions, type WebpackPluginOptions, getSharedArtifact, getSharedState, getStateKey };
69
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/plugin.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;AAkBA;AAUuB,cAVV,oBAAA,CAUU;EAKL,gBAAA,UAAA,GAAA,sBAAA;EAmMW,iBAAA,OAAA;EA8BZ,iBAAA,QAAA;EAAe,QAAA,aAAA;;;;wBAtOT;kBAKL;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6BAmMW;;;;;;;;;;;;iBA8BZ"}
package/dist/index.mjs ADDED
@@ -0,0 +1,177 @@
1
+ import { createPluginSession, getSharedArtifact, getSharedState, getSharedState as getSharedState$1, getStateKey, getStateKey as getStateKey$1, setSharedArtifact, setSharedPluginSession } from "@soda-gql/plugin-common";
2
+ import { collectAffectedFiles } from "@soda-gql/builder";
3
+ import { normalizePath } from "@soda-gql/common";
4
+
5
+ //#region packages/webpack-plugin/src/plugin.ts
6
+ /**
7
+ * Webpack plugin for soda-gql that handles incremental rebuilds
8
+ * when model files change during dev server execution.
9
+ */
10
+ var SodaGqlWebpackPlugin = class SodaGqlWebpackPlugin {
11
+ static pluginName = "SodaGqlWebpackPlugin";
12
+ options;
13
+ stateKey;
14
+ pluginSession = null;
15
+ currentArtifact = null;
16
+ previousArtifact = null;
17
+ pendingInvalidations = /* @__PURE__ */ new Set();
18
+ constructor(options = {}) {
19
+ this.options = options;
20
+ this.stateKey = getStateKey$1(options.configPath);
21
+ }
22
+ apply(compiler) {
23
+ compiler.hooks.beforeRun.tapAsync(SodaGqlWebpackPlugin.pluginName, async (_compiler, callback) => {
24
+ try {
25
+ await this.initialize();
26
+ callback();
27
+ } catch (error) {
28
+ callback(error);
29
+ }
30
+ });
31
+ compiler.hooks.watchRun.tapAsync(SodaGqlWebpackPlugin.pluginName, async (_compiler, callback) => {
32
+ try {
33
+ await this.handleWatchRun();
34
+ callback();
35
+ } catch (error) {
36
+ callback(error);
37
+ }
38
+ });
39
+ compiler.hooks.invalid.tap(SodaGqlWebpackPlugin.pluginName, (fileName, _changeTime) => {
40
+ if (fileName) this.handleFileInvalidation(fileName);
41
+ });
42
+ compiler.hooks.watchClose.tap(SodaGqlWebpackPlugin.pluginName, () => {
43
+ this.cleanup();
44
+ });
45
+ }
46
+ /**
47
+ * Initialize plugin session and build initial artifact.
48
+ */
49
+ async initialize() {
50
+ if (this.pluginSession) return;
51
+ this.pluginSession = createPluginSession(this.options, SodaGqlWebpackPlugin.pluginName);
52
+ if (!this.pluginSession) {
53
+ this.log("Plugin disabled or config load failed");
54
+ return;
55
+ }
56
+ setSharedPluginSession(this.stateKey, this.pluginSession);
57
+ this.currentArtifact = await this.pluginSession.getArtifactAsync();
58
+ setSharedArtifact(this.stateKey, this.currentArtifact);
59
+ this.log(`Initial build complete: ${Object.keys(this.currentArtifact?.elements ?? {}).length} elements`);
60
+ }
61
+ /**
62
+ * Handle watch mode run - rebuild artifact and compute affected files.
63
+ */
64
+ async handleWatchRun() {
65
+ if (!this.pluginSession) await this.initialize();
66
+ if (!this.pluginSession) return;
67
+ this.previousArtifact = this.currentArtifact;
68
+ this.currentArtifact = await this.pluginSession.getArtifactAsync();
69
+ setSharedArtifact(this.stateKey, this.currentArtifact);
70
+ if (!this.currentArtifact) {
71
+ this.log("Failed to build artifact");
72
+ return;
73
+ }
74
+ if (this.previousArtifact && this.hasArtifactChanged()) {
75
+ const changedFiles = this.getChangedSodaGqlFiles();
76
+ const sharedState = getSharedState$1(this.stateKey);
77
+ const affectedFiles = this.computeAffectedFiles(changedFiles, sharedState.moduleAdjacency);
78
+ this.log(`Changed files: ${changedFiles.size}, Affected files: ${affectedFiles.size}`);
79
+ for (const filePath of affectedFiles) this.pendingInvalidations.add(filePath);
80
+ }
81
+ }
82
+ /**
83
+ * Handle file invalidation event from webpack.
84
+ */
85
+ handleFileInvalidation(fileName) {
86
+ const normalized = normalizePath(fileName);
87
+ if (this.isSodaGqlFile(normalized)) this.log(`soda-gql file changed: ${normalized}`);
88
+ }
89
+ /**
90
+ * Check if artifact has changed by comparing element counts and hashes.
91
+ */
92
+ hasArtifactChanged() {
93
+ if (!this.previousArtifact || !this.currentArtifact) return true;
94
+ if (Object.keys(this.previousArtifact.elements).length !== Object.keys(this.currentArtifact.elements).length) return true;
95
+ const prevElements = this.previousArtifact.elements;
96
+ const currElements = this.currentArtifact.elements;
97
+ for (const [id, element] of Object.entries(currElements)) {
98
+ const prevElement = prevElements[id];
99
+ if (!prevElement) return true;
100
+ if (element.metadata.contentHash !== prevElement.metadata.contentHash) return true;
101
+ }
102
+ return false;
103
+ }
104
+ /**
105
+ * Get files that changed between previous and current artifact.
106
+ */
107
+ getChangedSodaGqlFiles() {
108
+ const changed = /* @__PURE__ */ new Set();
109
+ if (!this.previousArtifact || !this.currentArtifact) return changed;
110
+ const prevElements = this.previousArtifact.elements;
111
+ const currElements = this.currentArtifact.elements;
112
+ for (const [id, element] of Object.entries(currElements)) {
113
+ const prevElement = prevElements[id];
114
+ const sourcePath = element.metadata.sourcePath;
115
+ if (!prevElement || prevElement.metadata.contentHash !== element.metadata.contentHash) changed.add(normalizePath(sourcePath));
116
+ }
117
+ for (const [id, element] of Object.entries(prevElements)) if (!currElements[id]) {
118
+ const sourcePath = element.metadata.sourcePath;
119
+ changed.add(normalizePath(sourcePath));
120
+ }
121
+ return changed;
122
+ }
123
+ /**
124
+ * Compute all files affected by the changed files using module adjacency.
125
+ */
126
+ computeAffectedFiles(changedFiles, moduleAdjacency) {
127
+ return collectAffectedFiles({
128
+ changedFiles,
129
+ removedFiles: /* @__PURE__ */ new Set(),
130
+ previousModuleAdjacency: moduleAdjacency
131
+ });
132
+ }
133
+ /**
134
+ * Check if a file path corresponds to a soda-gql source file.
135
+ */
136
+ isSodaGqlFile(filePath) {
137
+ if (!this.currentArtifact) return false;
138
+ const normalized = normalizePath(filePath);
139
+ for (const element of Object.values(this.currentArtifact.elements)) if (normalizePath(element.metadata.sourcePath) === normalized) return true;
140
+ return false;
141
+ }
142
+ /**
143
+ * Get pending invalidations and clear the set.
144
+ */
145
+ getPendingInvalidations() {
146
+ const invalidations = new Set(this.pendingInvalidations);
147
+ this.pendingInvalidations.clear();
148
+ return invalidations;
149
+ }
150
+ /**
151
+ * Cleanup resources.
152
+ */
153
+ cleanup() {
154
+ this.pluginSession = null;
155
+ this.currentArtifact = null;
156
+ this.previousArtifact = null;
157
+ this.pendingInvalidations.clear();
158
+ setSharedPluginSession(this.stateKey, null);
159
+ setSharedArtifact(this.stateKey, null);
160
+ }
161
+ /**
162
+ * Log a message if debug mode is enabled.
163
+ */
164
+ log(message) {
165
+ if (this.options.debug) console.log(`[${SodaGqlWebpackPlugin.pluginName}] ${message}`);
166
+ }
167
+ /**
168
+ * Get the current artifact (for use by loader).
169
+ */
170
+ getArtifact() {
171
+ return this.currentArtifact;
172
+ }
173
+ };
174
+
175
+ //#endregion
176
+ export { SodaGqlWebpackPlugin, getSharedArtifact, getSharedState, getStateKey };
177
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":["getStateKey","getSharedState"],"sources":["../src/plugin.ts"],"sourcesContent":["import type { BuilderArtifact, BuilderArtifactElement } from \"@soda-gql/builder\";\nimport { collectAffectedFiles } from \"@soda-gql/builder\";\nimport { normalizePath } from \"@soda-gql/common\";\nimport {\n createPluginSession,\n getSharedState,\n getStateKey,\n type PluginSession,\n setSharedArtifact,\n setSharedPluginSession,\n} from \"@soda-gql/plugin-common\";\nimport type { Compiler } from \"webpack\";\nimport type { WebpackPluginOptions } from \"./types\";\n\n/**\n * Webpack plugin for soda-gql that handles incremental rebuilds\n * when model files change during dev server execution.\n */\nexport class SodaGqlWebpackPlugin {\n static readonly pluginName = \"SodaGqlWebpackPlugin\";\n\n private readonly options: WebpackPluginOptions;\n private readonly stateKey: string;\n private pluginSession: PluginSession | null = null;\n private currentArtifact: BuilderArtifact | null = null;\n private previousArtifact: BuilderArtifact | null = null;\n private pendingInvalidations: Set<string> = new Set();\n\n constructor(options: WebpackPluginOptions = {}) {\n this.options = options;\n this.stateKey = getStateKey(options.configPath);\n }\n\n apply(compiler: Compiler): void {\n // Initialize plugin session on first build\n compiler.hooks.beforeRun.tapAsync(SodaGqlWebpackPlugin.pluginName, async (_compiler, callback) => {\n try {\n await this.initialize();\n callback();\n } catch (error) {\n callback(error as Error);\n }\n });\n\n // Handle watch mode\n compiler.hooks.watchRun.tapAsync(SodaGqlWebpackPlugin.pluginName, async (_compiler, callback) => {\n try {\n await this.handleWatchRun();\n callback();\n } catch (error) {\n callback(error as Error);\n }\n });\n\n // Track file invalidations\n compiler.hooks.invalid.tap(SodaGqlWebpackPlugin.pluginName, (fileName, _changeTime) => {\n if (fileName) {\n this.handleFileInvalidation(fileName);\n }\n });\n\n // Cleanup on watch close\n compiler.hooks.watchClose.tap(SodaGqlWebpackPlugin.pluginName, () => {\n this.cleanup();\n });\n }\n\n /**\n * Initialize plugin session and build initial artifact.\n */\n private async initialize(): Promise<void> {\n if (this.pluginSession) return;\n\n this.pluginSession = createPluginSession(this.options, SodaGqlWebpackPlugin.pluginName);\n if (!this.pluginSession) {\n this.log(\"Plugin disabled or config load failed\");\n return;\n }\n\n // Share the plugin session with loader\n setSharedPluginSession(this.stateKey, this.pluginSession);\n\n // Initial artifact build\n this.currentArtifact = await this.pluginSession.getArtifactAsync();\n\n // Share artifact with loader\n setSharedArtifact(this.stateKey, this.currentArtifact);\n\n this.log(`Initial build complete: ${Object.keys(this.currentArtifact?.elements ?? {}).length} elements`);\n }\n\n /**\n * Handle watch mode run - rebuild artifact and compute affected files.\n */\n private async handleWatchRun(): Promise<void> {\n if (!this.pluginSession) {\n await this.initialize();\n }\n\n if (!this.pluginSession) return;\n\n // Store previous artifact for comparison\n this.previousArtifact = this.currentArtifact;\n\n // Rebuild artifact (BuilderService handles change detection internally)\n this.currentArtifact = await this.pluginSession.getArtifactAsync();\n\n // Share artifact with loader\n setSharedArtifact(this.stateKey, this.currentArtifact);\n\n if (!this.currentArtifact) {\n this.log(\"Failed to build artifact\");\n return;\n }\n\n // If we have a previous artifact, compute what changed\n if (this.previousArtifact && this.hasArtifactChanged()) {\n const changedFiles = this.getChangedSodaGqlFiles();\n const sharedState = getSharedState(this.stateKey);\n const affectedFiles = this.computeAffectedFiles(changedFiles, sharedState.moduleAdjacency);\n\n this.log(`Changed files: ${changedFiles.size}, Affected files: ${affectedFiles.size}`);\n\n // Store affected files for webpack to pick up\n for (const filePath of affectedFiles) {\n this.pendingInvalidations.add(filePath);\n }\n }\n }\n\n /**\n * Handle file invalidation event from webpack.\n */\n private handleFileInvalidation(fileName: string): void {\n const normalized = normalizePath(fileName);\n if (this.isSodaGqlFile(normalized)) {\n this.log(`soda-gql file changed: ${normalized}`);\n }\n }\n\n /**\n * Check if artifact has changed by comparing element counts and hashes.\n */\n private hasArtifactChanged(): boolean {\n if (!this.previousArtifact || !this.currentArtifact) return true;\n\n const prevCount = Object.keys(this.previousArtifact.elements).length;\n const newCount = Object.keys(this.currentArtifact.elements).length;\n if (prevCount !== newCount) return true;\n\n // Compare individual elements by their content hash\n const prevElements = this.previousArtifact.elements as Record<string, BuilderArtifactElement>;\n const currElements = this.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 // Compare using metadata\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 private getChangedSodaGqlFiles(): Set<string> {\n const changed = new Set<string>();\n\n if (!this.previousArtifact || !this.currentArtifact) return changed;\n\n const prevElements = this.previousArtifact.elements as Record<string, BuilderArtifactElement>;\n const currElements = this.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 /**\n * Compute all files affected by the changed files using module adjacency.\n */\n private computeAffectedFiles(changedFiles: Set<string>, moduleAdjacency: Map<string, Set<string>>): Set<string> {\n // Use the existing collectAffectedFiles from builder\n return collectAffectedFiles({\n changedFiles,\n removedFiles: new Set(),\n previousModuleAdjacency: moduleAdjacency,\n });\n }\n\n /**\n * Check if a file path corresponds to a soda-gql source file.\n */\n private isSodaGqlFile(filePath: string): boolean {\n if (!this.currentArtifact) return false;\n\n const normalized = normalizePath(filePath);\n for (const element of Object.values(this.currentArtifact.elements)) {\n if (normalizePath(element.metadata.sourcePath) === normalized) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * Get pending invalidations and clear the set.\n */\n getPendingInvalidations(): Set<string> {\n const invalidations = new Set(this.pendingInvalidations);\n this.pendingInvalidations.clear();\n return invalidations;\n }\n\n /**\n * Cleanup resources.\n */\n private cleanup(): void {\n this.pluginSession = null;\n this.currentArtifact = null;\n this.previousArtifact = null;\n this.pendingInvalidations.clear();\n setSharedPluginSession(this.stateKey, null);\n setSharedArtifact(this.stateKey, null);\n }\n\n /**\n * Log a message if debug mode is enabled.\n */\n private log(message: string): void {\n if (this.options.debug) {\n console.log(`[${SodaGqlWebpackPlugin.pluginName}] ${message}`);\n }\n }\n\n /**\n * Get the current artifact (for use by loader).\n */\n getArtifact(): BuilderArtifact | null {\n return this.currentArtifact;\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,73 @@
1
+ Object.defineProperty(exports, '__esModule', { value: true });
2
+ let __soda_gql_plugin_common = require("@soda-gql/plugin-common");
3
+ let __soda_gql_common = require("@soda-gql/common");
4
+ let __babel_core = require("@babel/core");
5
+ let __soda_gql_babel_plugin = require("@soda-gql/babel-plugin");
6
+
7
+ //#region packages/webpack-plugin/src/loader.ts
8
+ /**
9
+ * Ensure plugin session is initialized.
10
+ * First tries to use shared session from plugin, falls back to creating own.
11
+ */
12
+ const ensurePluginSession = (options) => {
13
+ const sharedSession = (0, __soda_gql_plugin_common.getSharedPluginSession)((0, __soda_gql_plugin_common.getStateKey)(options.configPath));
14
+ if (sharedSession) return sharedSession;
15
+ return (0, __soda_gql_plugin_common.createPluginSession)({
16
+ configPath: options.configPath,
17
+ enabled: options.enabled
18
+ }, "@soda-gql/webpack-plugin/loader");
19
+ };
20
+ /**
21
+ * Webpack loader that transforms soda-gql code using the babel-plugin.
22
+ */
23
+ const sodaGqlLoader = function(source, inputSourceMap) {
24
+ const callback = this.async();
25
+ const options = this.getOptions();
26
+ const filename = this.resourcePath;
27
+ const stateKey = (0, __soda_gql_plugin_common.getStateKey)(options.configPath);
28
+ (async () => {
29
+ try {
30
+ const session = ensurePluginSession(options);
31
+ if (!session) {
32
+ callback(null, source, inputSourceMap);
33
+ return;
34
+ }
35
+ let artifact = (0, __soda_gql_plugin_common.getSharedArtifact)(stateKey);
36
+ if (!artifact) artifact = await session.getArtifactAsync();
37
+ if (!artifact) {
38
+ callback(null, source, inputSourceMap);
39
+ return;
40
+ }
41
+ const normalizedPath = (0, __soda_gql_common.normalizePath)(filename);
42
+ if (!Object.values(artifact.elements).some((element) => (0, __soda_gql_common.normalizePath)(element.metadata.sourcePath) === normalizedPath)) {
43
+ callback(null, source, inputSourceMap);
44
+ return;
45
+ }
46
+ for (const element of Object.values(artifact.elements)) {
47
+ const elementPath = element.metadata.sourcePath;
48
+ if (elementPath && elementPath !== filename) this.addDependency(elementPath);
49
+ }
50
+ const result = (0, __babel_core.transformSync)(source, {
51
+ filename,
52
+ babelrc: false,
53
+ configFile: false,
54
+ plugins: [(0, __soda_gql_babel_plugin.createPluginWithArtifact)({
55
+ artifact,
56
+ config: session.config
57
+ })],
58
+ sourceMaps: true,
59
+ inputSourceMap
60
+ });
61
+ if (result?.code) callback(null, result.code, result.map);
62
+ else callback(null, source, inputSourceMap);
63
+ } catch (error) {
64
+ callback(error);
65
+ }
66
+ })();
67
+ };
68
+ var loader_default = sodaGqlLoader;
69
+ const raw = false;
70
+
71
+ //#endregion
72
+ exports.default = loader_default;
73
+ exports.raw = raw;
@@ -0,0 +1,13 @@
1
+ import { t as WebpackLoaderOptions } from "./types-Dh6GvYnd.cjs";
2
+ import { LoaderDefinitionFunction } from "webpack";
3
+
4
+ //#region packages/webpack-plugin/src/loader.d.ts
5
+
6
+ /**
7
+ * Webpack loader that transforms soda-gql code using the babel-plugin.
8
+ */
9
+ declare const sodaGqlLoader: LoaderDefinitionFunction<WebpackLoaderOptions>;
10
+ declare const raw = false;
11
+ //#endregion
12
+ export { sodaGqlLoader as default, raw };
13
+ //# sourceMappingURL=loader.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.d.cts","names":[],"sources":["../src/loader.ts"],"sourcesContent":[],"mappings":";;;;;;;AAWoD;AAyGpD,cA7EM,aA6EU,EA7EK,wBA6EL,CA7E8B,oBA6E9B,CAAA;cAAH,GAAA"}
@@ -0,0 +1,13 @@
1
+ import { t as WebpackLoaderOptions } from "./types-o73wYce8.mjs";
2
+ import { LoaderDefinitionFunction } from "webpack";
3
+
4
+ //#region packages/webpack-plugin/src/loader.d.ts
5
+
6
+ /**
7
+ * Webpack loader that transforms soda-gql code using the babel-plugin.
8
+ */
9
+ declare const sodaGqlLoader: LoaderDefinitionFunction<WebpackLoaderOptions>;
10
+ declare const raw = false;
11
+ //#endregion
12
+ export { sodaGqlLoader as default, raw };
13
+ //# sourceMappingURL=loader.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.d.mts","names":[],"sources":["../src/loader.ts"],"sourcesContent":[],"mappings":";;;;;;;AAWoD;AAyGpD,cA7EM,aA6EU,EA7EK,wBA6EL,CA7E8B,oBA6E9B,CAAA;cAAH,GAAA"}
@@ -0,0 +1,72 @@
1
+ import { createPluginSession, getSharedArtifact, getSharedPluginSession, getStateKey } from "@soda-gql/plugin-common";
2
+ import { normalizePath } from "@soda-gql/common";
3
+ import { transformSync } from "@babel/core";
4
+ import { createPluginWithArtifact } from "@soda-gql/babel-plugin";
5
+
6
+ //#region packages/webpack-plugin/src/loader.ts
7
+ /**
8
+ * Ensure plugin session is initialized.
9
+ * First tries to use shared session from plugin, falls back to creating own.
10
+ */
11
+ const ensurePluginSession = (options) => {
12
+ const sharedSession = getSharedPluginSession(getStateKey(options.configPath));
13
+ if (sharedSession) return sharedSession;
14
+ return createPluginSession({
15
+ configPath: options.configPath,
16
+ enabled: options.enabled
17
+ }, "@soda-gql/webpack-plugin/loader");
18
+ };
19
+ /**
20
+ * Webpack loader that transforms soda-gql code using the babel-plugin.
21
+ */
22
+ const sodaGqlLoader = function(source, inputSourceMap) {
23
+ const callback = this.async();
24
+ const options = this.getOptions();
25
+ const filename = this.resourcePath;
26
+ const stateKey = getStateKey(options.configPath);
27
+ (async () => {
28
+ try {
29
+ const session = ensurePluginSession(options);
30
+ if (!session) {
31
+ callback(null, source, inputSourceMap);
32
+ return;
33
+ }
34
+ let artifact = getSharedArtifact(stateKey);
35
+ if (!artifact) artifact = await session.getArtifactAsync();
36
+ if (!artifact) {
37
+ callback(null, source, inputSourceMap);
38
+ return;
39
+ }
40
+ const normalizedPath = normalizePath(filename);
41
+ if (!Object.values(artifact.elements).some((element) => normalizePath(element.metadata.sourcePath) === normalizedPath)) {
42
+ callback(null, source, inputSourceMap);
43
+ return;
44
+ }
45
+ for (const element of Object.values(artifact.elements)) {
46
+ const elementPath = element.metadata.sourcePath;
47
+ if (elementPath && elementPath !== filename) this.addDependency(elementPath);
48
+ }
49
+ const result = transformSync(source, {
50
+ filename,
51
+ babelrc: false,
52
+ configFile: false,
53
+ plugins: [createPluginWithArtifact({
54
+ artifact,
55
+ config: session.config
56
+ })],
57
+ sourceMaps: true,
58
+ inputSourceMap
59
+ });
60
+ if (result?.code) callback(null, result.code, result.map);
61
+ else callback(null, source, inputSourceMap);
62
+ } catch (error) {
63
+ callback(error);
64
+ }
65
+ })();
66
+ };
67
+ var loader_default = sodaGqlLoader;
68
+ const raw = false;
69
+
70
+ //#endregion
71
+ export { loader_default as default, raw };
72
+ //# sourceMappingURL=loader.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.mjs","names":["sodaGqlLoader: LoaderDefinitionFunction<WebpackLoaderOptions>"],"sources":["../src/loader.ts"],"sourcesContent":["import { type TransformOptions, transformSync } from \"@babel/core\";\nimport { createPluginWithArtifact } from \"@soda-gql/babel-plugin\";\nimport { normalizePath } from \"@soda-gql/common\";\nimport {\n createPluginSession,\n getSharedArtifact,\n getSharedPluginSession,\n getStateKey,\n type PluginSession,\n} from \"@soda-gql/plugin-common\";\nimport type { LoaderDefinitionFunction } from \"webpack\";\nimport type { WebpackLoaderOptions } from \"./types\";\n\n/**\n * Ensure plugin session is initialized.\n * First tries to use shared session from plugin, falls back to creating own.\n */\nconst ensurePluginSession = (options: WebpackLoaderOptions): PluginSession | null => {\n const stateKey = getStateKey(options.configPath);\n\n // Try to use shared session from plugin first\n const sharedSession = getSharedPluginSession(stateKey);\n if (sharedSession) {\n return sharedSession;\n }\n\n // Fall back to creating own session (for standalone loader usage)\n return createPluginSession(\n {\n configPath: options.configPath,\n enabled: options.enabled,\n },\n \"@soda-gql/webpack-plugin/loader\",\n );\n};\n\n/**\n * Webpack loader that transforms soda-gql code using the babel-plugin.\n */\nconst sodaGqlLoader: LoaderDefinitionFunction<WebpackLoaderOptions> = function (source, inputSourceMap) {\n const callback = this.async();\n const options = this.getOptions();\n const filename = this.resourcePath;\n const stateKey = getStateKey(options.configPath);\n\n (async () => {\n try {\n const session = ensurePluginSession(options);\n if (!session) {\n // Plugin disabled or config load failed, pass through unchanged\n callback(null, source, inputSourceMap as Parameters<typeof callback>[2]);\n return;\n }\n\n // Try to use shared artifact from plugin first (more efficient in watch mode)\n let artifact = getSharedArtifact(stateKey);\n\n // Fall back to fetching artifact if not shared\n if (!artifact) {\n artifact = await session.getArtifactAsync();\n }\n\n if (!artifact) {\n callback(null, source, inputSourceMap as Parameters<typeof callback>[2]);\n return;\n }\n\n // Check if this file contains any soda-gql elements\n const normalizedPath = normalizePath(filename);\n const hasElements = Object.values(artifact.elements).some(\n (element) => normalizePath(element.metadata.sourcePath) === normalizedPath,\n );\n\n if (!hasElements) {\n // Not a soda-gql file, pass through unchanged\n callback(null, source, inputSourceMap as Parameters<typeof callback>[2]);\n return;\n }\n\n // Add dependencies to webpack for HMR\n // This ensures webpack rebuilds this file when its dependencies change\n for (const element of Object.values(artifact.elements)) {\n const elementPath = element.metadata.sourcePath;\n if (elementPath && elementPath !== filename) {\n // Add all soda-gql source files as dependencies\n // This is a conservative approach that ensures rebuilds propagate\n this.addDependency(elementPath);\n }\n }\n\n // Transform using Babel plugin with direct artifact\n const babelOptions: TransformOptions = {\n filename,\n babelrc: false,\n configFile: false,\n plugins: [createPluginWithArtifact({ artifact, config: session.config })],\n sourceMaps: true,\n inputSourceMap: inputSourceMap as TransformOptions[\"inputSourceMap\"],\n };\n\n const result = transformSync(source, babelOptions);\n\n if (result?.code) {\n callback(null, result.code, result.map as Parameters<typeof callback>[2]);\n } else {\n callback(null, source, inputSourceMap as Parameters<typeof callback>[2]);\n }\n } catch (error) {\n callback(error as Error);\n }\n })();\n};\n\nexport default sodaGqlLoader;\n\n// Mark as non-raw (we handle string source code)\nexport const raw = false;\n"],"mappings":";;;;;;;;;;AAiBA,MAAM,uBAAuB,YAAwD;CAInF,MAAM,gBAAgB,uBAHL,YAAY,QAAQ,WAAW,CAGM;AACtD,KAAI,cACF,QAAO;AAIT,QAAO,oBACL;EACE,YAAY,QAAQ;EACpB,SAAS,QAAQ;EAClB,EACD,kCACD;;;;;AAMH,MAAMA,gBAAgE,SAAU,QAAQ,gBAAgB;CACtG,MAAM,WAAW,KAAK,OAAO;CAC7B,MAAM,UAAU,KAAK,YAAY;CACjC,MAAM,WAAW,KAAK;CACtB,MAAM,WAAW,YAAY,QAAQ,WAAW;AAEhD,EAAC,YAAY;AACX,MAAI;GACF,MAAM,UAAU,oBAAoB,QAAQ;AAC5C,OAAI,CAAC,SAAS;AAEZ,aAAS,MAAM,QAAQ,eAAiD;AACxE;;GAIF,IAAI,WAAW,kBAAkB,SAAS;AAG1C,OAAI,CAAC,SACH,YAAW,MAAM,QAAQ,kBAAkB;AAG7C,OAAI,CAAC,UAAU;AACb,aAAS,MAAM,QAAQ,eAAiD;AACxE;;GAIF,MAAM,iBAAiB,cAAc,SAAS;AAK9C,OAAI,CAJgB,OAAO,OAAO,SAAS,SAAS,CAAC,MAClD,YAAY,cAAc,QAAQ,SAAS,WAAW,KAAK,eAC7D,EAEiB;AAEhB,aAAS,MAAM,QAAQ,eAAiD;AACxE;;AAKF,QAAK,MAAM,WAAW,OAAO,OAAO,SAAS,SAAS,EAAE;IACtD,MAAM,cAAc,QAAQ,SAAS;AACrC,QAAI,eAAe,gBAAgB,SAGjC,MAAK,cAAc,YAAY;;GAcnC,MAAM,SAAS,cAAc,QATU;IACrC;IACA,SAAS;IACT,YAAY;IACZ,SAAS,CAAC,yBAAyB;KAAE;KAAU,QAAQ,QAAQ;KAAQ,CAAC,CAAC;IACzE,YAAY;IACI;IACjB,CAEiD;AAElD,OAAI,QAAQ,KACV,UAAS,MAAM,OAAO,MAAM,OAAO,IAAsC;OAEzE,UAAS,MAAM,QAAQ,eAAiD;WAEnE,OAAO;AACd,YAAS,MAAe;;KAExB;;AAGN,qBAAe;AAGf,MAAa,MAAM"}
@@ -0,0 +1,38 @@
1
+ import { PluginOptions } from "@soda-gql/plugin-common";
2
+
3
+ //#region packages/webpack-plugin/src/types.d.ts
4
+
5
+ /**
6
+ * Options for the SodaGqlWebpackPlugin.
7
+ */
8
+ type WebpackPluginOptions = PluginOptions & {
9
+ /**
10
+ * File patterns to include for transformation.
11
+ * Defaults to files matching config.include patterns.
12
+ */
13
+ readonly include?: RegExp | RegExp[];
14
+ /**
15
+ * File patterns to exclude from transformation.
16
+ */
17
+ readonly exclude?: RegExp | RegExp[];
18
+ /**
19
+ * Enable verbose logging for debugging.
20
+ */
21
+ readonly debug?: boolean;
22
+ };
23
+ /**
24
+ * Options for the webpack loader.
25
+ */
26
+ type WebpackLoaderOptions = {
27
+ /**
28
+ * Path to soda-gql config file.
29
+ */
30
+ readonly configPath?: string;
31
+ /**
32
+ * Enable/disable the loader.
33
+ */
34
+ readonly enabled?: boolean;
35
+ };
36
+ //#endregion
37
+ export { WebpackPluginOptions as n, WebpackLoaderOptions as t };
38
+ //# sourceMappingURL=types-Dh6GvYnd.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types-Dh6GvYnd.d.cts","names":[],"sources":["../src/types.ts"],"sourcesContent":[],"mappings":";;;;;;AAKA;AAAmC,KAAvB,oBAAA,GAAuB,aAAA,GAAA;EAKd;;;;EAKe,SAAA,OAAA,CAAA,EALf,MAKe,GALN,MAKM,EAAA;EAWxB;;;qBAXS,SAAS;;;;;;;;;KAWlB,oBAAA"}
@@ -0,0 +1,38 @@
1
+ import { PluginOptions } from "@soda-gql/plugin-common";
2
+
3
+ //#region packages/webpack-plugin/src/types.d.ts
4
+
5
+ /**
6
+ * Options for the SodaGqlWebpackPlugin.
7
+ */
8
+ type WebpackPluginOptions = PluginOptions & {
9
+ /**
10
+ * File patterns to include for transformation.
11
+ * Defaults to files matching config.include patterns.
12
+ */
13
+ readonly include?: RegExp | RegExp[];
14
+ /**
15
+ * File patterns to exclude from transformation.
16
+ */
17
+ readonly exclude?: RegExp | RegExp[];
18
+ /**
19
+ * Enable verbose logging for debugging.
20
+ */
21
+ readonly debug?: boolean;
22
+ };
23
+ /**
24
+ * Options for the webpack loader.
25
+ */
26
+ type WebpackLoaderOptions = {
27
+ /**
28
+ * Path to soda-gql config file.
29
+ */
30
+ readonly configPath?: string;
31
+ /**
32
+ * Enable/disable the loader.
33
+ */
34
+ readonly enabled?: boolean;
35
+ };
36
+ //#endregion
37
+ export { WebpackPluginOptions as n, WebpackLoaderOptions as t };
38
+ //# sourceMappingURL=types-o73wYce8.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types-o73wYce8.d.mts","names":[],"sources":["../src/types.ts"],"sourcesContent":[],"mappings":";;;;;;AAKA;AAAmC,KAAvB,oBAAA,GAAuB,aAAA,GAAA;EAKd;;;;EAKe,SAAA,OAAA,CAAA,EALf,MAKe,GALN,MAKM,EAAA;EAWxB;;;qBAXS,SAAS;;;;;;;;;KAWlB,oBAAA"}
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@soda-gql/webpack-plugin",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "private": false,
6
+ "license": "MIT",
7
+ "files": [
8
+ "dist"
9
+ ],
10
+ "author": {
11
+ "name": "Shota Hatada",
12
+ "email": "shota.hatada@whatasoda.me",
13
+ "url": "https://github.com/whatasoda"
14
+ },
15
+ "main": "./dist/index.mjs",
16
+ "module": "./dist/index.mjs",
17
+ "types": "./dist/index.d.mts",
18
+ "exports": {
19
+ ".": {
20
+ "@soda-gql": "./src/index.ts",
21
+ "types": "./dist/index.d.mts",
22
+ "import": "./dist/index.mjs",
23
+ "require": "./dist/index.cjs",
24
+ "default": "./dist/index.mjs"
25
+ },
26
+ "./loader": {
27
+ "@soda-gql": "./src/loader.ts",
28
+ "types": "./dist/loader.d.mts",
29
+ "import": "./dist/loader.mjs",
30
+ "require": "./dist/loader.cjs",
31
+ "default": "./dist/loader.mjs"
32
+ },
33
+ "./package.json": "./package.json"
34
+ },
35
+ "dependencies": {
36
+ "@soda-gql/builder": "0.1.0",
37
+ "@soda-gql/common": "0.1.0",
38
+ "@soda-gql/config": "0.1.0",
39
+ "@soda-gql/core": "0.1.0",
40
+ "@soda-gql/plugin-common": "0.1.0",
41
+ "@soda-gql/babel-plugin": "0.1.0",
42
+ "neverthrow": "^8.1.1"
43
+ },
44
+ "devDependencies": {},
45
+ "peerDependencies": {
46
+ "webpack": "^5.0.0"
47
+ }
48
+ }