Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] feat: partial support for tsconfig target option #23

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 9 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "oxbuild"
version = "0.1.2"
version = "0.1.3"
description = "Ultra-fast typescript compiler"
authors = ["Don Isaac <don@donisaac.dev>"]
license = "MIT"
Expand Down Expand Up @@ -55,7 +55,7 @@ panic = "abort"
# The profile that 'cargo dist' will build with
[profile.dist]
inherits = "release"
lto = "thin"
lto = "thin"

# Config for 'cargo dist'
[workspace.metadata.dist]
Expand All @@ -66,7 +66,13 @@ ci = "github"
# The installers to generate for each app
installers = ["shell", "powershell", "npm"]
# Target platforms to build apps for (Rust target-triple syntax)
targets = ["aarch64-apple-darwin", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu", "x86_64-unknown-linux-musl", "x86_64-pc-windows-msvc"]
targets = [
"aarch64-apple-darwin",
"x86_64-apple-darwin",
"x86_64-unknown-linux-gnu",
"x86_64-unknown-linux-musl",
"x86_64-pc-windows-msvc",
]
# The archive format to use for windows builds (defaults .zip)
windows-archive = ".tar.gz"
# The archive format to use for non-windows builds (defaults .tar.xz)
Expand Down
1 change: 1 addition & 0 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ By default, Oxbuild will look for a tsconfig.json next to the nearest package.js
.get_matches()
}

#[derive(Debug)]
#[non_exhaustive]
pub struct CliOptions {
pub root: Root,
Expand Down
6 changes: 6 additions & 0 deletions src/cli/root.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ impl Deref for Root {
type Target = Path;

fn deref(&self) -> &Self::Target {
self.as_ref()
}
}

impl AsRef<Path> for Root {
fn as_ref(&self) -> &Path {
self.root.as_ref().unwrap_or(&self.cwd)
}
}
Expand Down
11 changes: 9 additions & 2 deletions src/compiler/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,32 @@ use std::{
path::{Path, PathBuf},
};

use oxc::transformer::TransformOptions;

#[derive(Debug, Clone)]
pub struct CompileOptions {
root_dir: PathBuf,
/// Emit .d.ts files using isolatedDeclarations.
d_ts: bool,
transform_options: TransformOptions,
}

impl Default for CompileOptions {
fn default() -> Self {
let cwd = env::current_dir().unwrap();
Self::new(cwd)
Self::new(cwd, TransformOptions::default())
}
}

impl CompileOptions {
pub fn new(root_dir: PathBuf) -> Self {
pub fn new(root_dir: PathBuf, transform_options: TransformOptions) -> Self {
assert!(root_dir.is_dir());
assert!(root_dir.is_absolute());

debug_assert_eq!(root_dir, transform_options.cwd);

Self {
transform_options,
root_dir,
d_ts: false,
}
Expand Down
111 changes: 101 additions & 10 deletions src/options.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
use crate::cli::{CliOptions, Root};
use std::{
fmt,
fs::{self},
num::NonZeroUsize,
path::PathBuf,
};

use miette::{IntoDiagnostic, Report, Result, WrapErr};
use oxc::transformer::TransformOptions;
// use package_json::{PackageJson, PackageJsonManager};
use serde::Deserialize;

use crate::cli::{CliOptions, Root};

// use crate::error::AnyError;

pub struct OxbuildOptions {
Expand All @@ -20,8 +23,7 @@ pub struct OxbuildOptions {
/// Path to output folder where compiled code will be written.
pub dist: PathBuf,
pub num_threads: NonZeroUsize,
// package_json: PackageJson,
// tsconfig: Option<PathBuf>, // TODO
pub transform_options: TransformOptions,
}

impl OxbuildOptions {
Expand Down Expand Up @@ -71,26 +73,40 @@ impl OxbuildOptions {
let dist = if let Some(out_dir) = co.and_then(|co| co.out_dir.as_ref()) {
root.resolve(out_dir)
} else {
let dist = root.join("dist").to_path_buf();
if !dist.exists() {
fs::create_dir(&dist).into_diagnostic()?;
}
// TODO: clean dist dir?
dist
root.join("dist").to_path_buf()
};
assert!(dist.is_dir()); // FIXME: handle errors

// TODO: clean dist dir?
if !dist.exists() {
fs::create_dir(&dist).into_diagnostic()?;
}
if !dist.is_dir() {
return Err(Report::msg(format!(
"Invalid output directory: '{}' is not a directory",
dist.display()
)));
}

let isolated_declarations = co
.and_then(|co| co.isolated_declarations)
// no tsconfig means they're using JavaScript. We can't emit .d.ts files in that case.
.unwrap_or(false);

let mut transform_options = tsconfig
.as_ref()
.map(|tsconfig| tsconfig.transform_options())
.transpose()?
.unwrap_or_default();

transform_options.cwd = root.to_path_buf();

Ok(Self {
root,
isolated_declarations,
src,
dist,
num_threads,
transform_options,
})
}
}
Expand All @@ -106,19 +122,94 @@ impl TsConfig {
}
}

/// [`compilerOptions`](https://www.typescriptlang.org/tsconfig/#compilerOptions) in a
/// `tsconfig.json` file.
#[derive(Debug, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
struct TsConfigCompilerOptions {
// TODO: parse more fields as needed
root_dir: Option<PathBuf>,
out_dir: Option<PathBuf>,
isolated_declarations: Option<bool>,
/// https://www.typescriptlang.org/tsconfig/#target
#[serde(default)]
target: TsConfigTarget,
}

/// https://www.typescriptlang.org/tsconfig/#target
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Deserialize)]
#[serde(rename_all = "snake_case")] // just needed for lowercasing values
pub enum TsConfigTarget {
/// Not supported by oxc
ES3,
ES5,
/// Same as es2015
ES6,
/// Same as es6
ES2015,
ES2016,
ES2017,
ES2018,
ES2019,
ES2020,
ES2021,
ES2022,
ES2023,
#[default]
ESNext,
}
impl fmt::Display for TsConfigTarget {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::ESNext => "ESNext".fmt(f),
Self::ES2023 => "ES2023".fmt(f),
Self::ES2022 => "ES2022".fmt(f),
Self::ES2021 => "ES2021".fmt(f),
Self::ES2020 => "ES2020".fmt(f),
Self::ES2019 => "ES2019".fmt(f),
Self::ES2018 => "ES2018".fmt(f),
Self::ES2017 => "ES2017".fmt(f),
Self::ES2016 => "ES2016".fmt(f),
Self::ES2015 => "ES2015".fmt(f),
Self::ES6 => "ES6".fmt(f),
Self::ES5 => "ES5".fmt(f),
Self::ES3 => "ES3".fmt(f),
}
}
}
impl TsConfigTarget {
/// Returns [`true`] if this version of ECMAScript is not supported by `oxc_transform`
fn is_unsupported(self) -> bool {
matches!(self, Self::ES3)
}
}

/// A parsed `tsconfig.json` file.
///
/// See: [TSConfig Reference](https://www.typescriptlang.org/tsconfig/)
impl TsConfig {
pub fn parse(mut source_text: String) -> Result<Self> {
json_strip_comments::strip(&mut source_text).unwrap();

serde_json::from_str(&source_text).into_diagnostic()
}

pub fn transform_options(&self) -> Result<TransformOptions> {
let co = self.compiler_options();
let target = co.map(|co| co.target).unwrap_or_default();

if target.is_unsupported() {
return Err(Report::msg(format!(
"Oxbuild does not support compiling to {target}. Please use a higher target version.",
)));
}
let mut options = TransformOptions::default();

// TODO: set presets once TransformOptions supports factories that take a target ECMAScript version
if target <= TsConfigTarget::ES2021 {
options.es2021.logical_assignment_operators = true
}

Ok(options)
}
}
8 changes: 6 additions & 2 deletions src/walk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,12 @@ pub struct WalkerBuilder {

impl WalkerBuilder {
pub fn new(options: OxbuildOptions, sender: DiagnosticSender) -> Self {
let compile_options = CompileOptions::new(options.root.deref().to_path_buf())
.with_d_ts(options.isolated_declarations);
let compile_options = CompileOptions::new(
options.root.deref().to_path_buf(),
options.transform_options.clone(),
)
.with_d_ts(options.isolated_declarations);

Self {
compile_options: Arc::new(compile_options),
options: Arc::new(options),
Expand Down