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
50 changes: 49 additions & 1 deletion napi/playground/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,41 @@ export interface OxcDefineOptions {
define: Record<string, string>
}

export interface OxcFormatterOptions {
/** Use tabs instead of spaces (default: false) */
useTabs?: boolean
/** Number of spaces per indentation level (default: 2) */
tabWidth?: number
/** Line ending type: "lf" | "crlf" | "cr" (default: "lf") */
endOfLine?: string
/** Maximum line width (default: 80) */
printWidth?: number
/** Use single quotes instead of double quotes (default: false) */
singleQuote?: boolean
/** Use single quotes in JSX (default: false) */
jsxSingleQuote?: boolean
/** When to add quotes around object properties: "as-needed" | "preserve" (default: "as-needed") */
quoteProps?: string
/** Print trailing commas: "all" | "es5" | "none" (default: "all") */
trailingComma?: string
/** Print semicolons (default: true) */
semi?: boolean
/** Include parentheses around arrow function parameters: "always" | "avoid" (default: "always") */
arrowParens?: string
/** Print spaces between brackets in object literals (default: true) */
bracketSpacing?: boolean
/** Put > of multi-line elements at the end of the last line (default: false) */
bracketSameLine?: boolean
/** Object wrapping style: "preserve" | "collapse" | "always" (default: "preserve") */
objectWrap?: string
/** Put each attribute on its own line (default: false) */
singleAttributePerLine?: boolean
/** Operator position: "start" | "end" (default: "end") */
experimentalOperatorPosition?: string
/** Sort imports configuration */
experimentalSortImports?: OxcSortImportsOptions
}

export interface OxcInjectOptions {
/** Map of variable name to module source or [source, specifier] */
inject: Record<string, string | [string, string]>
Expand All @@ -90,6 +125,7 @@ export interface OxcOptions {
run: OxcRunOptions
parser: OxcParserOptions
linter?: OxcLinterOptions
formatter?: OxcFormatterOptions
transformer?: OxcTransformerOptions
isolatedDeclarations?: OxcIsolatedDeclarationsOptions
codegen?: OxcCodegenOptions
Expand All @@ -111,7 +147,6 @@ export interface OxcParserOptions {
export interface OxcRunOptions {
lint: boolean
formatter: boolean
formatterIr: boolean
transform: boolean
isolatedDeclarations: boolean
whitespace: boolean
Expand All @@ -122,6 +157,19 @@ export interface OxcRunOptions {
cfg: boolean
}

export interface OxcSortImportsOptions {
/** Partition imports by newlines (default: false) */
partitionByNewline?: boolean
/** Partition imports by comments (default: false) */
partitionByComment?: boolean
/** Sort side effects imports (default: false) */
sortSideEffects?: boolean
/** Sort order: "asc" | "desc" (default: "asc") */
order?: string
/** Ignore case when sorting (default: true) */
ignoreCase?: boolean
}

export interface OxcTransformerOptions {
target?: string
useDefineForClassFields: boolean
Expand Down
168 changes: 143 additions & 25 deletions napi/playground/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,11 @@ use oxc::{
syntax::reference::ReferenceFlags,
transformer::{TransformOptions, Transformer},
};
use oxc_formatter::{FormatOptions, Formatter};
use oxc_formatter::{
ArrowParentheses, AttributePosition, BracketSameLine, BracketSpacing, Expand, FormatOptions,
Formatter, IndentStyle, IndentWidth, LineEnding, LineWidth, OperatorPosition, QuoteProperties,
QuoteStyle, Semicolons, SortImports, SortOrder, TrailingCommas,
};
use oxc_linter::{
ConfigStore, ConfigStoreBuilder, ContextSubHost, ExternalPluginStore, LintOptions, Linter,
ModuleRecord, Oxlintrc,
Expand All @@ -41,8 +45,9 @@ use oxc_transformer_plugins::{
};

use crate::options::{
OxcControlFlowOptions, OxcDefineOptions, OxcInjectOptions, OxcIsolatedDeclarationsOptions,
OxcLinterOptions, OxcOptions, OxcParserOptions, OxcRunOptions, OxcTransformerOptions,
OxcControlFlowOptions, OxcDefineOptions, OxcFormatterOptions, OxcInjectOptions,
OxcIsolatedDeclarationsOptions, OxcLinterOptions, OxcOptions, OxcParserOptions, OxcRunOptions,
OxcTransformerOptions,
};

mod options;
Expand Down Expand Up @@ -100,6 +105,7 @@ impl Oxc {
run: ref run_options,
parser: ref parser_options,
linter: ref linter_options,
formatter: ref formatter_options,
transformer: ref transform_options,
control_flow: ref control_flow_options,
isolated_declarations: ref isolated_declarations_options,
Expand All @@ -109,6 +115,7 @@ impl Oxc {
..
} = options;
let linter_options = linter_options.clone().unwrap_or_default();
let formatter_options = formatter_options.clone().unwrap_or_default();
let transform_options = transform_options.clone().unwrap_or_default();
let control_flow_options = control_flow_options.clone().unwrap_or_default();
let codegen_options = codegen_options.clone().unwrap_or_default();
Expand Down Expand Up @@ -142,14 +149,7 @@ impl Oxc {
&allocator,
);

// Phase 4: Run formatter
let parse_options = ParseOptions {
parse_regular_expression: true,
allow_return_outside_function: parser_options.allow_return_outside_function,
preserve_parens: parser_options.preserve_parens,
allow_v8_intrinsics: parser_options.allow_v8_intrinsics,
};
self.run_formatter(run_options, parse_options, &source_text, source_type);
self.run_formatter(run_options, &source_text, source_type, &formatter_options);

let mut scoping = semantic.into_scoping();

Expand Down Expand Up @@ -414,30 +414,148 @@ impl Oxc {
}
}

fn convert_formatter_options(options: &OxcFormatterOptions) -> FormatOptions {
let mut format_options = FormatOptions::default();

if let Some(use_tabs) = options.use_tabs {
format_options.indent_style =
if use_tabs { IndentStyle::Tab } else { IndentStyle::Space };
}

if let Some(tab_width) = options.tab_width
&& let Ok(width) = IndentWidth::try_from(tab_width)
{
format_options.indent_width = width;
}

if let Some(ref end_of_line) = options.end_of_line
&& let Ok(ending) = end_of_line.parse::<LineEnding>()
{
format_options.line_ending = ending;
}

if let Some(print_width) = options.print_width
&& let Ok(width) = LineWidth::try_from(print_width)
{
format_options.line_width = width;
}

if let Some(single_quote) = options.single_quote {
format_options.quote_style =
if single_quote { QuoteStyle::Single } else { QuoteStyle::Double };
}

if let Some(jsx_single_quote) = options.jsx_single_quote {
format_options.jsx_quote_style =
if jsx_single_quote { QuoteStyle::Single } else { QuoteStyle::Double };
}

if let Some(ref quote_props) = options.quote_props
&& let Ok(props) = quote_props.parse::<QuoteProperties>()
{
format_options.quote_properties = props;
}

if let Some(ref trailing_comma) = options.trailing_comma
&& let Ok(commas) = trailing_comma.parse::<TrailingCommas>()
{
format_options.trailing_commas = commas;
}

if let Some(semi) = options.semi {
format_options.semicolons =
if semi { Semicolons::Always } else { Semicolons::AsNeeded };
}

if let Some(ref arrow_parens) = options.arrow_parens {
let normalized =
if arrow_parens == "avoid" { "as-needed" } else { arrow_parens.as_str() };
if let Ok(parens) = normalized.parse::<ArrowParentheses>() {
format_options.arrow_parentheses = parens;
}
}

if let Some(bracket_spacing) = options.bracket_spacing {
format_options.bracket_spacing = BracketSpacing::from(bracket_spacing);
}

if let Some(bracket_same_line) = options.bracket_same_line {
format_options.bracket_same_line = BracketSameLine::from(bracket_same_line);
}

if let Some(single_attribute_per_line) = options.single_attribute_per_line {
format_options.attribute_position = if single_attribute_per_line {
AttributePosition::Multiline
} else {
AttributePosition::Auto
};
}

if let Some(ref object_wrap) = options.object_wrap {
let normalized = match object_wrap.as_str() {
"preserve" => "auto",
"collapse" => "never",
_ => object_wrap.as_str(),
};
if let Ok(expand) = normalized.parse::<Expand>() {
format_options.expand = expand;
}
}

if let Some(ref position) = options.experimental_operator_position
&& let Ok(op_position) = position.parse::<OperatorPosition>()
{
format_options.experimental_operator_position = op_position;
}

if let Some(ref sort_imports_config) = options.experimental_sort_imports {
let order = sort_imports_config
.order
.as_ref()
.and_then(|o| o.parse::<SortOrder>().ok())
.unwrap_or_default();

format_options.experimental_sort_imports = Some(SortImports {
partition_by_newline: sort_imports_config.partition_by_newline.unwrap_or(false),
partition_by_comment: sort_imports_config.partition_by_comment.unwrap_or(false),
sort_side_effects: sort_imports_config.sort_side_effects.unwrap_or(false),
order,
ignore_case: sort_imports_config.ignore_case.unwrap_or(true),
});
}

format_options
}

fn run_formatter(
&mut self,
run_options: &OxcRunOptions,
parser_options: ParseOptions,
source_text: &str,
source_type: SourceType,
formatter_options: &OxcFormatterOptions,
) {
let allocator = Allocator::default();
if run_options.formatter || run_options.formatter_ir {
if run_options.formatter {
let ret = Parser::new(&allocator, source_text, source_type)
.with_options(ParseOptions { preserve_parens: false, ..parser_options })
.with_options(ParseOptions {
preserve_parens: false,
allow_return_outside_function: true,
allow_v8_intrinsics: true,
parse_regular_expression: false,
})
.parse();

let formatter = Formatter::new(&allocator, FormatOptions::default());
self.formatter_formatted_text = formatter.build(&ret.program);

// if run_options.formatter_ir.unwrap_or_default() {
// let formatter_doc = formatter.doc(&ret.program).to_string();
// self.formatter_ir_text = {
// let ret =
// Parser::new(&allocator, &formatter_doc, SourceType::default()).parse();
// Formatter::new(&allocator, FormatOptions::default()).build(&ret.program)
// };
// }
let format_options = Self::convert_formatter_options(formatter_options);
let formatter = Formatter::new(&allocator, format_options);
let formatted = formatter.format(&ret.program);
if run_options.formatter {
self.formatter_formatted_text = match formatted.print() {
Ok(printer) => printer.into_code(),
Err(err) => err.to_string(),
};

self.formatter_ir_text = formatted.into_document().to_string();
}
}
}

Expand Down
54 changes: 53 additions & 1 deletion napi/playground/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub struct OxcOptions {
pub run: OxcRunOptions,
pub parser: OxcParserOptions,
pub linter: Option<OxcLinterOptions>,
pub formatter: Option<OxcFormatterOptions>,
pub transformer: Option<OxcTransformerOptions>,
pub isolated_declarations: Option<OxcIsolatedDeclarationsOptions>,
pub codegen: Option<OxcCodegenOptions>,
Expand All @@ -23,7 +24,6 @@ pub struct OxcOptions {
pub struct OxcRunOptions {
pub lint: bool,
pub formatter: bool,
pub formatter_ir: bool,
pub transform: bool,
pub isolated_declarations: bool,
pub whitespace: bool,
Expand Down Expand Up @@ -112,3 +112,55 @@ pub struct OxcMangleOptions {
#[napi(object)]
#[derive(Clone, Copy, Default)]
pub struct OxcCompressOptions;

#[napi(object)]
#[derive(Default, Clone)]
pub struct OxcFormatterOptions {
/// Use tabs instead of spaces (default: false)
pub use_tabs: Option<bool>,
/// Number of spaces per indentation level (default: 2)
pub tab_width: Option<u8>,
/// Line ending type: "lf" | "crlf" | "cr" (default: "lf")
pub end_of_line: Option<String>,
/// Maximum line width (default: 80)
pub print_width: Option<u16>,
/// Use single quotes instead of double quotes (default: false)
pub single_quote: Option<bool>,
/// Use single quotes in JSX (default: false)
pub jsx_single_quote: Option<bool>,
/// When to add quotes around object properties: "as-needed" | "preserve" (default: "as-needed")
pub quote_props: Option<String>,
/// Print trailing commas: "all" | "es5" | "none" (default: "all")
pub trailing_comma: Option<String>,
/// Print semicolons (default: true)
pub semi: Option<bool>,
/// Include parentheses around arrow function parameters: "always" | "avoid" (default: "always")
pub arrow_parens: Option<String>,
/// Print spaces between brackets in object literals (default: true)
pub bracket_spacing: Option<bool>,
/// Put > of multi-line elements at the end of the last line (default: false)
pub bracket_same_line: Option<bool>,
/// Object wrapping style: "preserve" | "collapse" | "always" (default: "preserve")
pub object_wrap: Option<String>,
/// Put each attribute on its own line (default: false)
pub single_attribute_per_line: Option<bool>,
/// Operator position: "start" | "end" (default: "end")
pub experimental_operator_position: Option<String>,
/// Sort imports configuration
pub experimental_sort_imports: Option<OxcSortImportsOptions>,
}

#[napi(object)]
#[derive(Default, Clone)]
pub struct OxcSortImportsOptions {
/// Partition imports by newlines (default: false)
pub partition_by_newline: Option<bool>,
/// Partition imports by comments (default: false)
pub partition_by_comment: Option<bool>,
/// Sort side effects imports (default: false)
pub sort_side_effects: Option<bool>,
/// Sort order: "asc" | "desc" (default: "asc")
pub order: Option<String>,
/// Ignore case when sorting (default: true)
pub ignore_case: Option<bool>,
}
Loading