Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions napi/transform/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,10 @@ export interface ModuleRunnerTransformResult {
errors: Array<OxcError>
}

export interface PluginsOptions {
styledComponents?: StyledComponentsOptions
}

export interface ReactRefreshOptions {
/**
* Specify the identifier of the refresh registration variable.
Expand All @@ -339,6 +343,85 @@ export interface ReactRefreshOptions {
emitFullSignatures?: boolean
}

/**
* Configure how styled-components are transformed.
*
* @see {@link https://styled-components.com/docs/tooling#babel-plugin}
*/
export interface StyledComponentsOptions {
/**
* Enhances the attached CSS class name on each component with richer output to help
* identify your components in the DOM without React DevTools.
*
* @default true
*/
displayName?: boolean
/**
* Controls whether the `displayName` of a component will be prefixed with the filename
* to make the component name as unique as possible.
*
* @default true
*/
fileName?: boolean
/**
* Adds a unique identifier to every styled component to avoid checksum mismatches
* due to different class generation on the client and server during server-side rendering.
*
* @default true
*/
ssr?: boolean
/**
* Transpiles styled-components tagged template literals to a smaller representation
* than what Babel normally creates, helping to reduce bundle size.
*
* @default true
*/
transpileTemplateLiterals?: boolean
/**
* Minifies CSS content by removing all whitespace and comments from your CSS,
* keeping valuable bytes out of your bundles.
*
* @default true
*/
minify?: boolean
/**
* Enables transformation of JSX `css` prop when using styled-components.
*
* **Note: This feature is not yet implemented in oxc.**
*
* @default true
*/
cssProp?: boolean
/**
* Enables "pure annotation" to aid dead code elimination by bundlers.
*
* @default false
*/
pure?: boolean
/**
* Adds a namespace prefix to component identifiers to ensure class names are unique.
*
* Example: With `namespace: "my-app"`, generates `componentId: "my-app__sc-3rfj0a-1"`
*/
namespace?: string
/**
* List of file names that are considered meaningless for component naming purposes.
*
* When the `fileName` option is enabled and a component is in a file with a name
* from this list, the directory name will be used instead of the file name for
* the component's display name.
*
* @default ["index"]
*/
meaninglessFileNames?: Array<string>
/**
* Import paths to be considered as styled-components imports at the top level.
*
* **Note: This feature is not yet implemented in oxc.**
*/
topLevelImportPaths?: Array<string>
}

/**
* Transpile a JavaScript or TypeScript into a target ECMAScript version.
*
Expand Down Expand Up @@ -407,6 +490,8 @@ export interface TransformOptions {
inject?: Record<string, string | [string, string]>
/** Decorator plugin */
decorator?: DecoratorOptions
/** Third-party plugins to use. */
plugins?: PluginsOptions
}

export interface TransformResult {
Expand Down
120 changes: 117 additions & 3 deletions napi/transform/src/transformer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ use oxc::{
semantic::{SemanticBuilder, SemanticBuilderReturn},
span::SourceType,
transformer::{
EnvOptions, HelperLoaderMode, HelperLoaderOptions, JsxRuntime, PluginsOptions,
ProposalOptions, RewriteExtensionsMode,
EnvOptions, HelperLoaderMode, HelperLoaderOptions, JsxRuntime, ProposalOptions,
RewriteExtensionsMode,
},
transformer_plugins::{
InjectGlobalVariablesConfig, InjectImport, ModuleRunnerTransform,
Expand Down Expand Up @@ -144,6 +144,9 @@ pub struct TransformOptions {

/// Decorator plugin
pub decorator: Option<DecoratorOptions>,

/// Third-party plugins to use.
pub plugins: Option<PluginsOptions>,
}

impl TryFrom<TransformOptions> for oxc::transformer::TransformOptions {
Expand Down Expand Up @@ -182,7 +185,10 @@ impl TryFrom<TransformOptions> for oxc::transformer::TransformOptions {
helper_loader: options
.helpers
.map_or_else(HelperLoaderOptions::default, HelperLoaderOptions::from),
plugins: PluginsOptions::default(),
plugins: options
.plugins
.map(oxc::transformer::PluginsOptions::from)
.unwrap_or_default(),
})
}
}
Expand Down Expand Up @@ -389,6 +395,114 @@ impl From<DecoratorOptions> for oxc::transformer::DecoratorOptions {
}
}

/// Configure how styled-components are transformed.
///
/// @see {@link https://styled-components.com/docs/tooling#babel-plugin}
#[napi(object)]
#[derive(Default)]
pub struct StyledComponentsOptions {
/// Enhances the attached CSS class name on each component with richer output to help
/// identify your components in the DOM without React DevTools.
///
/// @default true
pub display_name: Option<bool>,

/// Controls whether the `displayName` of a component will be prefixed with the filename
/// to make the component name as unique as possible.
///
/// @default true
pub file_name: Option<bool>,

/// Adds a unique identifier to every styled component to avoid checksum mismatches
/// due to different class generation on the client and server during server-side rendering.
///
/// @default true
pub ssr: Option<bool>,

/// Transpiles styled-components tagged template literals to a smaller representation
/// than what Babel normally creates, helping to reduce bundle size.
///
/// @default true
pub transpile_template_literals: Option<bool>,

/// Minifies CSS content by removing all whitespace and comments from your CSS,
/// keeping valuable bytes out of your bundles.
///
/// @default true
pub minify: Option<bool>,

/// Enables transformation of JSX `css` prop when using styled-components.
///
/// **Note: This feature is not yet implemented in oxc.**
///
/// @default true
pub css_prop: Option<bool>,

/// Enables "pure annotation" to aid dead code elimination by bundlers.
///
/// @default false
pub pure: Option<bool>,

/// Adds a namespace prefix to component identifiers to ensure class names are unique.
///
/// Example: With `namespace: "my-app"`, generates `componentId: "my-app__sc-3rfj0a-1"`
pub namespace: Option<String>,

/// List of file names that are considered meaningless for component naming purposes.
///
/// When the `fileName` option is enabled and a component is in a file with a name
/// from this list, the directory name will be used instead of the file name for
/// the component's display name.
///
/// @default ["index"]
pub meaningless_file_names: Option<Vec<String>>,

/// Import paths to be considered as styled-components imports at the top level.
///
/// **Note: This feature is not yet implemented in oxc.**
pub top_level_import_paths: Option<Vec<String>>,
}

#[napi(object)]
#[derive(Default)]
pub struct PluginsOptions {
pub styled_components: Option<StyledComponentsOptions>,
}

impl From<PluginsOptions> for oxc::transformer::PluginsOptions {
fn from(options: PluginsOptions) -> Self {
oxc::transformer::PluginsOptions {
styled_components: options
.styled_components
.map(oxc::transformer::StyledComponentsOptions::from),
}
}
}

impl From<StyledComponentsOptions> for oxc::transformer::StyledComponentsOptions {
fn from(options: StyledComponentsOptions) -> Self {
let ops = oxc::transformer::StyledComponentsOptions::default();
oxc::transformer::StyledComponentsOptions {
display_name: options.display_name.unwrap_or(ops.display_name),
file_name: options.file_name.unwrap_or(ops.file_name),
ssr: options.ssr.unwrap_or(ops.ssr),
transpile_template_literals: options
.transpile_template_literals
.unwrap_or(ops.transpile_template_literals),
minify: options.minify.unwrap_or(ops.minify),
css_prop: options.css_prop.unwrap_or(ops.css_prop),
pure: options.pure.unwrap_or(ops.pure),
namespace: options.namespace,
meaningless_file_names: options
.meaningless_file_names
.unwrap_or(ops.meaningless_file_names),
top_level_import_paths: options
.top_level_import_paths
.unwrap_or(ops.top_level_import_paths),
}
}
}

/// Configure how TSX and JSX are transformed.
///
/// @see {@link https://babeljs.io/docs/babel-plugin-transform-react-jsx#options}
Expand Down
27 changes: 27 additions & 0 deletions napi/transform/test/transform.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -418,3 +418,30 @@ describe('typescript', () => {
});
});
});

describe('styled-components', () => {
test('matches output', () => {
const code = `
import styled, { css } from 'styled-components';

styled.div\`color: red;\`;
const v = css(["color: red;"]);
`;
const ret = transform('test.js', code, {
plugins: {
styledComponents: {
pure: true,
},
},
});
expect(ret.code).toMatchInlineSnapshot(`
"import styled, { css } from "styled-components";
styled.div.withConfig({
displayName: "test",
componentId: "sc-ziiqn7-0"
})(["color:red;"]);
const v = /* @__PURE__ */ css(["color: red;"]);
"
`);
});
});
Loading