@soda-gql/swc-transformer 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 +79 -0
- package/dist/index.cjs +915 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +91 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +91 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +913 -0
- package/dist/index.mjs.map +1 -0
- package/dist/native.cjs +254 -0
- package/dist/native.cjs.map +1 -0
- package/dist/native.d.cts +46 -0
- package/dist/native.d.cts.map +1 -0
- package/dist/native.d.mts +46 -0
- package/dist/native.d.mts.map +1 -0
- package/dist/native.mjs +256 -0
- package/dist/native.mjs.map +1 -0
- package/package.json +81 -0
- package/src/index.ts +290 -0
- package/src/lib.rs +87 -0
- package/src/native/index.d.ts +42 -0
- package/src/native/index.js +316 -0
- package/src/native/swc-transformer.linux-x64-gnu.node +0 -0
- package/src/transform/analysis.rs +240 -0
- package/src/transform/imports.rs +285 -0
- package/src/transform/metadata.rs +371 -0
- package/src/transform/mod.rs +7 -0
- package/src/transform/runtime.rs +197 -0
- package/src/transform/transformer.rs +438 -0
- package/src/types/artifact.rs +107 -0
- package/src/types/config.rs +72 -0
- package/src/types/error.rs +132 -0
- package/src/types/mod.rs +12 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
//! BuilderArtifact types ported from @soda-gql/builder.
|
|
2
|
+
//!
|
|
3
|
+
//! These types represent the pre-built metadata generated by the builder
|
|
4
|
+
//! service during static analysis.
|
|
5
|
+
|
|
6
|
+
use serde::{Deserialize, Serialize};
|
|
7
|
+
use std::collections::HashMap;
|
|
8
|
+
|
|
9
|
+
/// Canonical identifier for a GQL definition.
|
|
10
|
+
/// Format: "filepath:scope.path"
|
|
11
|
+
pub type CanonicalId = String;
|
|
12
|
+
|
|
13
|
+
/// Metadata about the source of an artifact element.
|
|
14
|
+
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
15
|
+
#[serde(rename_all = "camelCase")]
|
|
16
|
+
pub struct BuilderArtifactElementMetadata {
|
|
17
|
+
pub source_path: String,
|
|
18
|
+
pub content_hash: String,
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/// Prebuild data for a Fragment artifact.
|
|
22
|
+
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
23
|
+
#[serde(rename_all = "camelCase")]
|
|
24
|
+
pub struct FragmentPrebuild {
|
|
25
|
+
pub typename: String,
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/// Prebuild data for an Operation artifact.
|
|
29
|
+
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
30
|
+
#[serde(rename_all = "camelCase")]
|
|
31
|
+
pub struct OperationPrebuild {
|
|
32
|
+
pub operation_type: String,
|
|
33
|
+
pub operation_name: String,
|
|
34
|
+
pub variable_names: Vec<String>,
|
|
35
|
+
/// GraphQL document AST (complex object, not a string)
|
|
36
|
+
pub document: serde_json::Value,
|
|
37
|
+
#[serde(default)]
|
|
38
|
+
pub metadata: Option<serde_json::Value>,
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/// A single artifact element from the builder.
|
|
42
|
+
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
43
|
+
#[serde(tag = "type", rename_all = "camelCase")]
|
|
44
|
+
pub enum BuilderArtifactElement {
|
|
45
|
+
#[serde(rename = "fragment")]
|
|
46
|
+
Fragment {
|
|
47
|
+
id: CanonicalId,
|
|
48
|
+
metadata: BuilderArtifactElementMetadata,
|
|
49
|
+
prebuild: FragmentPrebuild,
|
|
50
|
+
},
|
|
51
|
+
#[serde(rename = "operation")]
|
|
52
|
+
Operation {
|
|
53
|
+
id: CanonicalId,
|
|
54
|
+
metadata: BuilderArtifactElementMetadata,
|
|
55
|
+
prebuild: OperationPrebuild,
|
|
56
|
+
},
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
#[allow(dead_code)]
|
|
60
|
+
impl BuilderArtifactElement {
|
|
61
|
+
/// Get the canonical ID of this element.
|
|
62
|
+
pub fn id(&self) -> &str {
|
|
63
|
+
match self {
|
|
64
|
+
Self::Fragment { id, .. } => id,
|
|
65
|
+
Self::Operation { id, .. } => id,
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/// Get the element type as a string.
|
|
70
|
+
pub fn element_type(&self) -> &'static str {
|
|
71
|
+
match self {
|
|
72
|
+
Self::Fragment { .. } => "fragment",
|
|
73
|
+
Self::Operation { .. } => "operation",
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/// Build statistics from the builder service.
|
|
79
|
+
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
80
|
+
pub struct BuilderArtifactStats {
|
|
81
|
+
pub hits: usize,
|
|
82
|
+
pub misses: usize,
|
|
83
|
+
pub skips: usize,
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/// Build report from the builder service.
|
|
87
|
+
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
88
|
+
#[serde(rename_all = "camelCase")]
|
|
89
|
+
pub struct BuilderArtifactReport {
|
|
90
|
+
pub duration_ms: u64,
|
|
91
|
+
pub warnings: Vec<String>,
|
|
92
|
+
pub stats: BuilderArtifactStats,
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/// The complete artifact generated by the builder service.
|
|
96
|
+
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
97
|
+
pub struct BuilderArtifact {
|
|
98
|
+
pub elements: HashMap<CanonicalId, BuilderArtifactElement>,
|
|
99
|
+
pub report: BuilderArtifactReport,
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
impl BuilderArtifact {
|
|
103
|
+
/// Look up an element by its canonical ID.
|
|
104
|
+
pub fn get(&self, id: &str) -> Option<&BuilderArtifactElement> {
|
|
105
|
+
self.elements.get(id)
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
//! Configuration types for the transformer.
|
|
2
|
+
|
|
3
|
+
use serde::{Deserialize, Serialize};
|
|
4
|
+
|
|
5
|
+
/// Configuration for the transformer.
|
|
6
|
+
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
7
|
+
#[serde(rename_all = "camelCase")]
|
|
8
|
+
pub struct TransformConfig {
|
|
9
|
+
/// Aliases used to identify graphql-system imports.
|
|
10
|
+
/// e.g., ["@/graphql-system", "./graphql-system"]
|
|
11
|
+
pub graphql_system_aliases: Vec<String>,
|
|
12
|
+
|
|
13
|
+
/// Whether to generate CommonJS output.
|
|
14
|
+
/// If false, generates ESM output.
|
|
15
|
+
#[serde(default)]
|
|
16
|
+
pub is_cjs: bool,
|
|
17
|
+
|
|
18
|
+
/// The canonical path to the graphql-system file.
|
|
19
|
+
/// When the source file matches this path, it will be stubbed out.
|
|
20
|
+
/// This is resolved by the TypeScript wrapper and passed to Rust.
|
|
21
|
+
#[serde(default)]
|
|
22
|
+
pub graphql_system_path: Option<String>,
|
|
23
|
+
|
|
24
|
+
/// Whether to generate source maps.
|
|
25
|
+
/// If true, a source map will be included in the output.
|
|
26
|
+
#[serde(default)]
|
|
27
|
+
pub source_map: bool,
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
impl Default for TransformConfig {
|
|
31
|
+
fn default() -> Self {
|
|
32
|
+
Self {
|
|
33
|
+
graphql_system_aliases: vec!["@/graphql-system".to_string()],
|
|
34
|
+
is_cjs: false,
|
|
35
|
+
graphql_system_path: None,
|
|
36
|
+
source_map: false,
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/// Input for a single file transformation (JSON-based, for one-shot transform).
|
|
42
|
+
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
43
|
+
#[serde(rename_all = "camelCase")]
|
|
44
|
+
pub struct TransformInput {
|
|
45
|
+
/// The source code to transform.
|
|
46
|
+
pub source_code: String,
|
|
47
|
+
|
|
48
|
+
/// The file path of the source.
|
|
49
|
+
pub source_path: String,
|
|
50
|
+
|
|
51
|
+
/// JSON-serialized BuilderArtifact.
|
|
52
|
+
pub artifact_json: String,
|
|
53
|
+
|
|
54
|
+
/// Transformation configuration.
|
|
55
|
+
pub config: TransformConfig,
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/// Input for a single file transformation with pre-parsed artifact.
|
|
59
|
+
/// Used by SwcTransformer to avoid repeated JSON parsing.
|
|
60
|
+
pub struct TransformInputRef<'a> {
|
|
61
|
+
/// The source code to transform.
|
|
62
|
+
pub source_code: String,
|
|
63
|
+
|
|
64
|
+
/// The file path of the source.
|
|
65
|
+
pub source_path: String,
|
|
66
|
+
|
|
67
|
+
/// Pre-parsed BuilderArtifact reference.
|
|
68
|
+
pub artifact: &'a super::BuilderArtifact,
|
|
69
|
+
|
|
70
|
+
/// Transformation configuration.
|
|
71
|
+
pub config: TransformConfig,
|
|
72
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
//! Structured error types for the transformer.
|
|
2
|
+
//!
|
|
3
|
+
//! This module defines error types that match @soda-gql/plugin-common/errors.ts
|
|
4
|
+
//! for consistent error reporting across TypeScript and Rust implementations.
|
|
5
|
+
|
|
6
|
+
use serde::{Deserialize, Serialize};
|
|
7
|
+
|
|
8
|
+
/// Stage where the error occurred.
|
|
9
|
+
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
|
|
10
|
+
#[serde(rename_all = "lowercase")]
|
|
11
|
+
pub enum ErrorStage {
|
|
12
|
+
Analysis,
|
|
13
|
+
Transform,
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/// Base structure for all plugin errors.
|
|
17
|
+
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
18
|
+
#[serde(rename_all = "camelCase")]
|
|
19
|
+
pub struct PluginError {
|
|
20
|
+
/// Always "PluginError" for type discrimination.
|
|
21
|
+
#[serde(rename = "type")]
|
|
22
|
+
pub error_type: String,
|
|
23
|
+
|
|
24
|
+
/// Error code for programmatic handling.
|
|
25
|
+
pub code: String,
|
|
26
|
+
|
|
27
|
+
/// Human-readable error message.
|
|
28
|
+
pub message: String,
|
|
29
|
+
|
|
30
|
+
/// Stage where the error occurred.
|
|
31
|
+
pub stage: ErrorStage,
|
|
32
|
+
|
|
33
|
+
/// Additional context about the error.
|
|
34
|
+
#[serde(skip_serializing_if = "Option::is_none")]
|
|
35
|
+
pub filename: Option<String>,
|
|
36
|
+
|
|
37
|
+
/// Canonical ID if applicable.
|
|
38
|
+
#[serde(skip_serializing_if = "Option::is_none")]
|
|
39
|
+
pub canonical_id: Option<String>,
|
|
40
|
+
|
|
41
|
+
/// Artifact type if applicable.
|
|
42
|
+
#[serde(skip_serializing_if = "Option::is_none")]
|
|
43
|
+
pub artifact_type: Option<String>,
|
|
44
|
+
|
|
45
|
+
/// Builder type if applicable.
|
|
46
|
+
#[serde(skip_serializing_if = "Option::is_none")]
|
|
47
|
+
pub builder_type: Option<String>,
|
|
48
|
+
|
|
49
|
+
/// Argument name if applicable.
|
|
50
|
+
#[serde(skip_serializing_if = "Option::is_none")]
|
|
51
|
+
pub arg_name: Option<String>,
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
impl PluginError {
|
|
55
|
+
/// Create a "metadata not found" error.
|
|
56
|
+
pub fn metadata_not_found(filename: &str) -> Self {
|
|
57
|
+
Self {
|
|
58
|
+
error_type: "PluginError".to_string(),
|
|
59
|
+
code: "SODA_GQL_METADATA_NOT_FOUND".to_string(),
|
|
60
|
+
message: format!("No metadata found for gql call in '{}'", filename),
|
|
61
|
+
stage: ErrorStage::Analysis,
|
|
62
|
+
filename: Some(filename.to_string()),
|
|
63
|
+
canonical_id: None,
|
|
64
|
+
artifact_type: None,
|
|
65
|
+
builder_type: None,
|
|
66
|
+
arg_name: None,
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/// Create an "artifact not found" error.
|
|
71
|
+
pub fn artifact_not_found(filename: &str, canonical_id: &str) -> Self {
|
|
72
|
+
Self {
|
|
73
|
+
error_type: "PluginError".to_string(),
|
|
74
|
+
code: "SODA_GQL_ANALYSIS_ARTIFACT_NOT_FOUND".to_string(),
|
|
75
|
+
message: format!(
|
|
76
|
+
"No artifact found for canonical ID '{}' in '{}'",
|
|
77
|
+
canonical_id, filename
|
|
78
|
+
),
|
|
79
|
+
stage: ErrorStage::Analysis,
|
|
80
|
+
filename: Some(filename.to_string()),
|
|
81
|
+
canonical_id: Some(canonical_id.to_string()),
|
|
82
|
+
artifact_type: None,
|
|
83
|
+
builder_type: None,
|
|
84
|
+
arg_name: None,
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/// Create a "missing builder arg" error.
|
|
89
|
+
pub fn missing_builder_arg(filename: &str, builder_type: &str, arg_name: &str) -> Self {
|
|
90
|
+
Self {
|
|
91
|
+
error_type: "PluginError".to_string(),
|
|
92
|
+
code: "SODA_GQL_TRANSFORM_MISSING_BUILDER_ARG".to_string(),
|
|
93
|
+
message: format!(
|
|
94
|
+
"Missing required builder argument '{}' for {} in '{}'",
|
|
95
|
+
arg_name, builder_type, filename
|
|
96
|
+
),
|
|
97
|
+
stage: ErrorStage::Transform,
|
|
98
|
+
filename: Some(filename.to_string()),
|
|
99
|
+
canonical_id: None,
|
|
100
|
+
artifact_type: None,
|
|
101
|
+
builder_type: Some(builder_type.to_string()),
|
|
102
|
+
arg_name: Some(arg_name.to_string()),
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/// Format the error into a human-readable message.
|
|
107
|
+
pub fn format(&self) -> String {
|
|
108
|
+
format!("[{}] ({:?}) {}", self.code, self.stage, self.message)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/// Collection of errors from a transformation.
|
|
113
|
+
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
|
114
|
+
pub struct TransformErrors {
|
|
115
|
+
pub errors: Vec<PluginError>,
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
impl TransformErrors {
|
|
119
|
+
pub fn new() -> Self {
|
|
120
|
+
Self { errors: Vec::new() }
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
pub fn push(&mut self, error: PluginError) {
|
|
124
|
+
// Log the error to stderr for visibility
|
|
125
|
+
eprintln!("[swc-transformer] {}", error.format());
|
|
126
|
+
self.errors.push(error);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
pub fn is_empty(&self) -> bool {
|
|
130
|
+
self.errors.is_empty()
|
|
131
|
+
}
|
|
132
|
+
}
|
package/src/types/mod.rs
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
//! Type definitions for the SWC transformer.
|
|
2
|
+
//!
|
|
3
|
+
//! These types mirror the TypeScript definitions from @soda-gql/builder
|
|
4
|
+
//! and @soda-gql/config packages.
|
|
5
|
+
|
|
6
|
+
pub mod artifact;
|
|
7
|
+
pub mod config;
|
|
8
|
+
pub mod error;
|
|
9
|
+
|
|
10
|
+
pub use artifact::*;
|
|
11
|
+
pub use config::*;
|
|
12
|
+
pub use error::*;
|