Skip to content

Commit 1abb645

Browse files
committed
Implement full native tokens plugin, use common code
1 parent 3f6a3b8 commit 1abb645

File tree

12 files changed

+510
-148
lines changed

12 files changed

+510
-148
lines changed

.changeset/red-balloons-sing.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@atlaspack/rust': minor
3+
---
4+
5+
Implement atlaspack_plugin_transformer_tokens as a fully native plugin

Cargo.lock

Lines changed: 30 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/atlaspack/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ atlaspack_plugin_transformer_raw = { path = "../atlaspack_plugin_transformer_raw
2626
atlaspack_plugin_transformer_css = { path = "../atlaspack_plugin_transformer_css" }
2727
atlaspack_plugin_transformer_yaml = { path = "../atlaspack_plugin_transformer_yaml" }
2828
atlaspack_plugin_transformer_svg = { path = "../atlaspack_plugin_transformer_svg" }
29+
atlaspack_plugin_transformer_tokens = { path = "../atlaspack_plugin_transformer_tokens" }
2930
atlaspack_plugin_rpc = { path = "../atlaspack_plugin_rpc" }
3031
atlaspack_sourcemap = { path = "../atlaspack_sourcemap" }
3132
atlaspack-resolver = { path = "../../packages/utils/node-resolver-rs" }

crates/atlaspack/src/plugins/config_plugins.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ use atlaspack_plugin_transformer_js::AtlaspackJsTransformerPlugin;
2727
use atlaspack_plugin_transformer_json::AtlaspackJsonTransformerPlugin;
2828
use atlaspack_plugin_transformer_raw::AtlaspackRawTransformerPlugin;
2929
use atlaspack_plugin_transformer_svg::AtlaspackSvgTransformerPlugin;
30+
use atlaspack_plugin_transformer_tokens::AtlaspackTokensTransformerPlugin;
3031
use atlaspack_plugin_transformer_yaml::AtlaspackYamlTransformerPlugin;
3132

3233
use super::Plugins;
@@ -238,6 +239,9 @@ impl Plugins for ConfigPlugins {
238239
Arc::new(AtlaspackYamlTransformerPlugin::new(&self.ctx))
239240
}
240241
"@atlaspack/transformer-svg" => Arc::new(AtlaspackSvgTransformerPlugin::new(&self.ctx)),
242+
"@atlaspack/transformer-tokens" => {
243+
Arc::new(AtlaspackTokensTransformerPlugin::new(&self.ctx)?)
244+
}
241245
_ => self.rpc_worker.create_transformer(&self.ctx, transformer)?,
242246
})
243247
})?;
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
[package]
2+
name = "atlaspack_atlaskit_tokens"
3+
version = "0.1.0"
4+
authors = ["Marcin Szczepanski <[email protected]>"]
5+
edition = { workspace = true }
6+
description = "Transformation for Atlaskit tokens for the Atlaspack Bundler"
7+
8+
[lints]
9+
workspace = true
10+
11+
[dependencies]
12+
atlaspack-js-swc-core = { path = "../../packages/transformers/js/core" }
13+
swc_atlaskit_tokens = { workspace = true, default-features = false }
14+
swc_core = { workspace = true }
15+
anyhow = { workspace = true }
16+
serde = { workspace = true, features = ["derive"] }
17+
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
use anyhow::{Context, Result, anyhow};
2+
use atlaspack_js_swc_core::{
3+
Config, emit, parse, utils::ErrorBuffer, utils::error_buffer_to_diagnostics,
4+
};
5+
use serde::Serialize;
6+
use swc_atlaskit_tokens::{
7+
design_system_tokens_visitor, token_map::get_or_load_token_map_from_json,
8+
};
9+
use swc_core::{
10+
common::{
11+
FileName, SourceMap,
12+
errors::{self, Handler},
13+
source_map::SourceMapGenConfig,
14+
sync::Lrc,
15+
},
16+
ecma::ast::{Module, ModuleItem, Program},
17+
};
18+
19+
#[derive(Clone)]
20+
pub struct TokensPluginOptions {
21+
pub token_data_path: String,
22+
pub should_use_auto_fallback: bool,
23+
pub should_force_auto_fallback: bool,
24+
pub force_auto_fallback_exemptions: Vec<String>,
25+
pub default_theme: String,
26+
}
27+
28+
#[derive(Clone)]
29+
pub struct TokensConfig {
30+
pub filename: String,
31+
pub project_root: String,
32+
pub is_source: bool,
33+
pub source_maps: bool,
34+
pub tokens_options: TokensPluginOptions,
35+
}
36+
37+
#[derive(Clone, Debug, Serialize)]
38+
pub struct TokensPluginResult {
39+
pub code: String,
40+
pub map: Option<String>,
41+
}
42+
43+
// Exclude macro expansions from source maps.
44+
struct SourceMapConfig;
45+
impl SourceMapGenConfig for SourceMapConfig {
46+
fn file_name_to_source(&self, f: &FileName) -> String {
47+
f.to_string()
48+
}
49+
50+
fn skip(&self, f: &FileName) -> bool {
51+
matches!(f, FileName::MacroExpansion | FileName::Internal(..))
52+
}
53+
}
54+
55+
/// Process tokens in a single piece of code - designed to be called from somewhere that orchestrates it
56+
pub fn process_tokens_sync(code: &str, config: &TokensConfig) -> Result<TokensPluginResult> {
57+
if code.trim().is_empty() {
58+
return Err(anyhow!("Empty code input"));
59+
}
60+
61+
let swc_config = Config {
62+
is_type_script: true,
63+
is_jsx: true,
64+
decorators: false,
65+
..Default::default()
66+
};
67+
68+
let error_buffer = ErrorBuffer::default();
69+
let handler = Handler::with_emitter(true, false, Box::new(error_buffer.clone()));
70+
errors::HANDLER.set(&handler, || {
71+
let source_map = Lrc::new(SourceMap::default());
72+
73+
// Parse and handle parsing errors
74+
let (module, comments) = match parse(
75+
code,
76+
&config.project_root,
77+
&config.filename,
78+
&source_map,
79+
&swc_config,
80+
) {
81+
Ok(result) => result,
82+
Err(_parsing_errors) => {
83+
let diagnostics = error_buffer_to_diagnostics(&error_buffer, &source_map);
84+
let error_msg = diagnostics
85+
.iter()
86+
.map(|d| &d.message)
87+
.cloned()
88+
.collect::<Vec<_>>()
89+
.join("\n");
90+
return Err(anyhow!("Parse error: {}", error_msg));
91+
}
92+
};
93+
94+
let module = match module {
95+
Program::Module(module) => Program::Module(module),
96+
Program::Script(script) => Program::Module(Module {
97+
span: script.span,
98+
shebang: None,
99+
body: script.body.into_iter().map(ModuleItem::Stmt).collect(),
100+
}),
101+
};
102+
103+
let token_map = get_or_load_token_map_from_json(Some(&config.tokens_options.token_data_path))
104+
.with_context(|| {
105+
format!(
106+
"Failed to load token map from: {}",
107+
config.tokens_options.token_data_path
108+
)
109+
})?;
110+
111+
let mut passes = design_system_tokens_visitor(
112+
comments.clone(),
113+
config.tokens_options.should_use_auto_fallback,
114+
config.tokens_options.should_force_auto_fallback,
115+
config.tokens_options.force_auto_fallback_exemptions.clone(),
116+
config.tokens_options.default_theme.clone(),
117+
!config.is_source,
118+
token_map.as_ref().map(|t| t.as_ref()),
119+
);
120+
let module = module.apply(&mut passes);
121+
let module_result = module
122+
.module()
123+
.ok_or_else(|| anyhow!("Failed to get transformed module"))?;
124+
let (code_bytes, line_pos_buffer) = emit(
125+
source_map.clone(),
126+
comments,
127+
&module_result,
128+
config.source_maps,
129+
Some(false), // Preserve Unicode characters in tokens
130+
)
131+
.with_context(|| "Failed to emit transformed code")?;
132+
133+
let code =
134+
String::from_utf8(code_bytes).with_context(|| "Failed to convert emitted code to UTF-8")?;
135+
let map_json = if config.source_maps && !line_pos_buffer.is_empty() {
136+
let mut output_map_buffer = vec![];
137+
if source_map
138+
.build_source_map_with_config(&line_pos_buffer, None, SourceMapConfig)
139+
.to_writer(&mut output_map_buffer)
140+
.is_ok()
141+
{
142+
Some(String::from_utf8(output_map_buffer).unwrap_or_default())
143+
} else {
144+
None
145+
}
146+
} else {
147+
None
148+
};
149+
150+
Ok(TokensPluginResult {
151+
code,
152+
map: map_json,
153+
})
154+
})
155+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
[package]
2+
name = "atlaspack_plugin_transformer_tokens"
3+
version = "0.1.0"
4+
authors = ["Marcin Szczepanski <[email protected]>"]
5+
edition = { workspace = true }
6+
description = "Atlaskit Tokens transformer plugin for the Atlaspack Bundler"
7+
8+
[lints]
9+
workspace = true
10+
11+
[dependencies]
12+
atlaspack_atlaskit_tokens = { path = "../atlaspack_atlaskit_tokens" }
13+
atlaspack_core = { path = "../atlaspack_core" }
14+
atlaspack_filesystem = { path = "../atlaspack_filesystem" }
15+
atlaspack_sourcemap = { path = "../atlaspack_sourcemap" }
16+
anyhow = { workspace = true }
17+
async-trait = { workspace = true }
18+
parcel_sourcemap = { workspace = true }
19+
serde = { workspace = true, features = ["derive"] }
20+
serde_json = { workspace = true }
21+
22+
23+
[dev-dependencies]
24+
atlaspack_filesystem = { path = "../atlaspack_filesystem" }
25+
pretty_assertions = { workspace = true }
26+
tokio = { workspace = true, features = ["full"] }
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
pub use tokens_transformer::AtlaspackTokensTransformerPlugin;
2+
3+
mod tokens_transformer;
4+
mod tokens_transformer_config;

0 commit comments

Comments
 (0)