@lithia-js/native 1.0.0-canary.2 → 1.0.0-canary.4

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/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # @lithia-js/native
2
2
 
3
+ ## 1.0.0-canary.4
4
+
5
+ ### Patch Changes
6
+
7
+ - fb9cee1: Enhanced production build support, fixed import module bug on production environment, fixed stdio inherit bug on `create-lithia`
8
+ - 00d8f11: fixed available events after connection
9
+
10
+ ## 1.0.0-canary.3
11
+
12
+ ### Patch Changes
13
+
14
+ - d536a2f: Fixed exports from @lithia-js/native
15
+
3
16
  ## 1.0.0-canary.2
4
17
 
5
18
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lithia-js/native",
3
- "version": "1.0.0-canary.2",
3
+ "version": "1.0.0-canary.4",
4
4
  "private": false,
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -40,25 +40,25 @@
40
40
  "access": "public",
41
41
  "provenance": true
42
42
  },
43
- "optionalDependencies": {
44
- "@lithia-js/native-darwin-x64": "1.0.0-canary.2",
45
- "@lithia-js/native-win32-x64-msvc": "1.0.0-canary.2",
46
- "@lithia-js/native-linux-x64-gnu": "1.0.0-canary.2",
47
- "@lithia-js/native-linux-x64-musl": "1.0.0-canary.2",
48
- "@lithia-js/native-linux-arm-gnueabihf": "1.0.0-canary.2",
49
- "@lithia-js/native-linux-arm64-gnu": "1.0.0-canary.2",
50
- "@lithia-js/native-darwin-arm64": "1.0.0-canary.2",
51
- "@lithia-js/native-win32-ia32-msvc": "1.0.0-canary.2",
52
- "@lithia-js/native-linux-arm64-musl": "1.0.0-canary.2",
53
- "@lithia-js/native-win32-arm64-msvc": "1.0.0-canary.2"
54
- },
55
43
  "exports": {
56
44
  ".": {
57
- "types": "./dist/lib.d.ts",
58
- "require": "./dist/lib.js",
59
- "import": "./dist/lib.js"
45
+ "types": "./index.d.ts",
46
+ "require": "./index.js",
47
+ "import": "./index.js"
60
48
  }
61
49
  },
50
+ "optionalDependencies": {
51
+ "@lithia-js/native-darwin-x64": "1.0.0-canary.4",
52
+ "@lithia-js/native-win32-x64-msvc": "1.0.0-canary.4",
53
+ "@lithia-js/native-linux-x64-gnu": "1.0.0-canary.4",
54
+ "@lithia-js/native-linux-x64-musl": "1.0.0-canary.4",
55
+ "@lithia-js/native-linux-arm-gnueabihf": "1.0.0-canary.4",
56
+ "@lithia-js/native-linux-arm64-gnu": "1.0.0-canary.4",
57
+ "@lithia-js/native-darwin-arm64": "1.0.0-canary.4",
58
+ "@lithia-js/native-win32-ia32-msvc": "1.0.0-canary.4",
59
+ "@lithia-js/native-linux-arm64-musl": "1.0.0-canary.4",
60
+ "@lithia-js/native-win32-arm64-msvc": "1.0.0-canary.4"
61
+ },
62
62
  "scripts": {
63
63
  "artifacts": "napi artifacts --npm-dir npm",
64
64
  "build:rs": "napi build --platform --release"
package/Cargo.toml DELETED
@@ -1,31 +0,0 @@
1
- [package]
2
- name = "lithia_native"
3
- version = "5.0.0"
4
- edition = "2021"
5
-
6
- [lib]
7
- path = "src/lib.rs"
8
- crate-type = ["cdylib"]
9
-
10
- [dependencies]
11
- napi = { version = "3.8.2", features = ["napi9"] }
12
- napi-derive = { version = "3.5.1" }
13
- walkdir = "2.5.0"
14
- globset = "0.4.18"
15
- rayon = "1.11"
16
- swc_common = { version = "18.0.1", features = ["concurrent", "sourcemap"] }
17
- swc_ecma_codegen = "22.0.0"
18
- swc_ecma_parser = "33.0.0"
19
- swc_ecma_transforms_base = "36.0.1"
20
- swc_ecma_transforms_typescript = "39.0.1"
21
- swc_ecma_transforms_module = "39.0.0"
22
- serde_json = "1.0.149"
23
- swc_ecma_ast = "20.0.0"
24
- swc_ecma_visit = "20.0.0"
25
- swc_atoms = "9.0.0"
26
- pathdiff = "0.2"
27
- regex = { version = "1.12.2" }
28
- serde = { version = "1.0.228", features = ["derive"] }
29
-
30
- [dev-dependencies]
31
- tempfile = "3.24.0"
@@ -1,493 +0,0 @@
1
- use std::io::Write;
2
- use std::path::{Path, PathBuf};
3
-
4
- use swc_atoms::Wtf8Atom;
5
- use swc_common::{
6
- comments::SingleThreadedComments,
7
- errors::{EmitterWriter, Handler},
8
- source_map::SourceMapGenConfig,
9
- sync::Lrc,
10
- BytePos, FileName, Globals, LineCol, Mark, SourceMap, GLOBALS,
11
- };
12
- use swc_ecma_codegen::{text_writer::JsWriter, Emitter as CodegenEmitter};
13
- use swc_ecma_parser::{lexer::Lexer, Parser, StringInput, Syntax, TsSyntax};
14
- use swc_ecma_transforms_base::{fixer::fixer, hygiene::hygiene, resolver};
15
- use swc_ecma_transforms_module::{common_js, path::Resolver as PathResolverEnum};
16
- use swc_ecma_transforms_typescript::strip;
17
-
18
- use swc_ecma_ast::{CallExpr, Callee, Expr, ExprOrSpread, Lit, Module, ModuleDecl, ModuleItem};
19
- use swc_ecma_visit::{VisitMut, VisitMutWith};
20
-
21
- use crate::builder::sourcemap::write_sourcemap_and_code;
22
-
23
- use super::tsconfig::TsConfigOptions;
24
-
25
- /// Simple, thread-safe buffer used to capture diagnostics emitted by SWC.
26
- ///
27
- /// The `ErrorBuffer` implements `std::io::Write` and stores emitted bytes
28
- /// in a shared `Arc<Mutex<Vec<u8>>>`. The native builder uses this buffer to
29
- /// capture human-readable error output from SWC and return it to the host
30
- /// instead of writing directly to stderr.
31
- #[derive(Clone)]
32
- struct ErrorBuffer {
33
- buffer: std::sync::Arc<std::sync::Mutex<Vec<u8>>>,
34
- }
35
-
36
- impl ErrorBuffer {
37
- fn new() -> Self {
38
- Self {
39
- buffer: std::sync::Arc::new(std::sync::Mutex::new(Vec::new())),
40
- }
41
- }
42
-
43
- fn get_content(&self) -> String {
44
- self.buffer
45
- .lock()
46
- .ok()
47
- .and_then(|buf| String::from_utf8(buf.clone()).ok())
48
- .unwrap_or_default()
49
- }
50
- }
51
-
52
- impl Write for ErrorBuffer {
53
- fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
54
- self.buffer
55
- .lock()
56
- .map_err(|_| std::io::Error::other("Lock failed"))?
57
- .write(buf)
58
- }
59
-
60
- fn flush(&mut self) -> std::io::Result<()> {
61
- self.buffer
62
- .lock()
63
- .map_err(|_| std::io::Error::other("Lock failed"))?
64
- .flush()
65
- }
66
- }
67
-
68
- /// Small `SourceMapGenConfig` implementation used when emitting source maps.
69
- ///
70
- /// It provides basic filename resolution and enables inlining the original
71
- /// sources content into the generated `.map` file. Keeping the original
72
- /// content in `sourcesContent` simplifies debugging in the runtime where the
73
- /// physical source files may not be available.
74
- struct SourceMapConfigImpl;
75
-
76
- impl SourceMapGenConfig for SourceMapConfigImpl {
77
- fn file_name_to_source(&self, f: &FileName) -> String {
78
- f.to_string()
79
- }
80
-
81
- fn inline_sources_content(&self, _: &FileName) -> bool {
82
- true
83
- }
84
- }
85
-
86
- /// Visitor that rewrites import/require/import() specifiers according to
87
- /// base_url + paths mapping (simple implementation).
88
- struct PathsRewriter {
89
- base_url: PathBuf,
90
- // Vec of (from_pattern, to_targets)
91
- paths: Vec<(String, Vec<String>)>,
92
- // directory of current file being compiled (for generating relative specifiers)
93
- file_dir: PathBuf,
94
- }
95
-
96
- impl PathsRewriter {
97
- fn resolve_using_paths(&self, spec: &str) -> Option<String> {
98
- // If spec is relative or absolute, don't touch
99
- if spec.starts_with('.') || spec.starts_with('/') {
100
- return None;
101
- }
102
-
103
- // Try each mapping
104
- for (from, to_list) in &self.paths {
105
- if from.contains('*') {
106
- // wildcard pattern
107
- // support exactly one '*' (like the real resolver)
108
- if from.as_bytes().iter().filter(|&&c| c == b'*').count() != 1 {
109
- continue;
110
- }
111
- let pos = from.find('*').unwrap();
112
- let prefix = &from[..pos];
113
- if !spec.starts_with(prefix) {
114
- continue;
115
- }
116
- let extra = &spec[prefix.len()..];
117
-
118
- for target in to_list {
119
- let replaced = target.replace('*', extra);
120
- // Candidate paths to check on disk
121
- // 1) base_url / replaced
122
- // 2) base_url / ./replaced
123
- // Also try common extensions
124
- if let Some(abs_found) = try_find_file_on_disk(&self.base_url.join(&replaced)) {
125
- // produce a path relative from file_dir to abs_found
126
- let rel = make_relative_or_prefixed(&self.file_dir, &abs_found);
127
- return Some(rel);
128
- }
129
-
130
- if let Some(abs_found) =
131
- try_find_file_on_disk(&self.base_url.join(format!("./{}", replaced)))
132
- {
133
- let rel = make_relative_or_prefixed(&self.file_dir, &abs_found);
134
- return Some(rel);
135
- }
136
-
137
- // As fallback, if single target and prefix not empty, mimic SWC's behavior:
138
- // return "./replaced" even if file not found (matches JS tests expectation).
139
- if to_list.len() == 1 && !prefix.is_empty() {
140
- let mut replaced_for_import = replaced.clone();
141
- if !replaced_for_import.starts_with("./")
142
- && !replaced_for_import.starts_with('/')
143
- {
144
- replaced_for_import = format!("./{}", replaced_for_import);
145
- }
146
- return Some(replaced_for_import);
147
- }
148
- }
149
- } else {
150
- // exact match
151
- if spec != from {
152
- continue;
153
- }
154
- // to_list must have exactly one entry (TypeScript rule)
155
- let target = &to_list[0];
156
- let tp = Path::new(target);
157
- if tp.is_absolute() {
158
- if let Some(abs_found) = try_find_file_on_disk(tp) {
159
- let rel = make_relative_or_prefixed(&self.file_dir, &abs_found);
160
- return Some(rel);
161
- }
162
- // absolute but not found, still return as-is
163
- return Some(target.clone());
164
- }
165
-
166
- // relative to base_url
167
- if let Some(abs_found) = try_find_file_on_disk(&self.base_url.join(target)) {
168
- let rel = make_relative_or_prefixed(&self.file_dir, &abs_found);
169
- return Some(rel);
170
- }
171
-
172
- // fallback: return "./target"
173
- let mut replaced_for_import = target.clone();
174
- if !replaced_for_import.starts_with("./") && !replaced_for_import.starts_with('/') {
175
- replaced_for_import = format!("./{}", replaced_for_import);
176
- }
177
- return Some(replaced_for_import);
178
- }
179
- }
180
-
181
- // If no mapping matched, try baseUrl + spec (baseUrl resolution)
182
- let candidate = self.base_url.join(spec);
183
- if let Some(abs_found) = try_find_file_on_disk(&candidate) {
184
- let rel = make_relative_or_prefixed(&self.file_dir, &abs_found);
185
- return Some(rel);
186
- }
187
-
188
- None
189
- }
190
- }
191
-
192
- impl VisitMut for PathsRewriter {
193
- fn visit_mut_module(&mut self, n: &mut Module) {
194
- // Walk module items and rewrite specifiers where applicable
195
- n.visit_mut_children_with(self);
196
- }
197
-
198
- fn visit_mut_module_item(&mut self, n: &mut ModuleItem) {
199
- match n {
200
- ModuleItem::ModuleDecl(decl) => match decl {
201
- ModuleDecl::Import(import_decl) => {
202
- let orig = import_decl.src.value.to_string_lossy().to_string();
203
- if let Some(new_spec) = self.resolve_using_paths(&orig) {
204
- import_decl.src.value = Wtf8Atom::from(new_spec);
205
- import_decl.src.raw = None;
206
- }
207
- }
208
- ModuleDecl::ExportAll(export_all) => {
209
- let orig = export_all.src.value.to_string_lossy().to_string();
210
- if let Some(new_spec) = self.resolve_using_paths(&orig) {
211
- export_all.src.value = Wtf8Atom::from(new_spec);
212
- export_all.src.raw = None;
213
- }
214
- }
215
- ModuleDecl::ExportDecl(_) => {}
216
- ModuleDecl::ExportNamed(named) => {
217
- if let Some(src) = &mut named.src {
218
- let orig = src.value.to_string_lossy().to_string();
219
- if let Some(new_spec) = self.resolve_using_paths(&orig) {
220
- src.value = Wtf8Atom::from(new_spec);
221
- src.raw = None;
222
- }
223
- }
224
- }
225
- _ => {}
226
- },
227
- ModuleItem::Stmt(stmt) => {
228
- // For statements, we still need to inspect call expressions (require)
229
- stmt.visit_mut_children_with(self);
230
- }
231
- }
232
- }
233
-
234
- fn visit_mut_expr(&mut self, n: &mut Expr) {
235
- // dynamic import: import("x")
236
- if let Expr::Call(CallExpr { callee, args, .. }) = n {
237
- // CommonJS require: require("x")
238
- #[allow(clippy::collapsible_match)]
239
- if let Callee::Expr(callee_expr) = callee {
240
- if let Expr::Ident(ident) = &**callee_expr {
241
- if &*ident.sym == "require" {
242
- if let Some(ExprOrSpread { expr, .. }) = args.get_mut(0) {
243
- if let Expr::Lit(Lit::Str(s)) = &mut **expr {
244
- let orig = s.value.to_string_lossy().to_string();
245
- if let Some(new_spec) = self.resolve_using_paths(&orig) {
246
- s.value = Wtf8Atom::from(new_spec);
247
- s.raw = None;
248
- }
249
- }
250
- }
251
- }
252
- }
253
- }
254
- }
255
-
256
- // For other expressions, recurse
257
- n.visit_mut_children_with(self);
258
- }
259
- }
260
-
261
- /// Try to find a file on disk for a candidate path. We try:
262
- /// - the exact path
263
- /// - path + .ts, .tsx, .js, .jsx, .d.ts
264
- fn try_find_file_on_disk(candidate: &Path) -> Option<PathBuf> {
265
- if candidate.exists() && candidate.is_file() {
266
- return Some(std::fs::canonicalize(candidate).unwrap_or_else(|_| candidate.to_path_buf()));
267
- }
268
-
269
- static EXTS: [&str; 5] = ["ts", "tsx", "js", "jsx", "d.ts"];
270
- for ext in &EXTS {
271
- let mut p = candidate.to_path_buf();
272
- // if candidate already has an extension, skip adding another
273
- if candidate.extension().is_some() {
274
- // already has extension and didn't exist, skip
275
- continue;
276
- }
277
- p.set_extension(ext);
278
- if p.exists() && p.is_file() {
279
- return Some(std::fs::canonicalize(&p).unwrap_or(p));
280
- }
281
- }
282
-
283
- // try index files if candidate is a directory
284
- if candidate.is_dir() {
285
- for ext in &["ts", "tsx", "js", "jsx"] {
286
- let mut idx = candidate.to_path_buf();
287
- idx.push(format!("index.{}", ext));
288
- if idx.exists() && idx.is_file() {
289
- return Some(std::fs::canonicalize(&idx).unwrap_or(idx));
290
- }
291
- }
292
- }
293
-
294
- None
295
- }
296
-
297
- /// Produce a specifier string to use in imports:
298
- /// - If target is inside file_dir parent, produce a relative path (e.g., "./sub/foo")
299
- /// - Otherwise produce a path prefixed with "./" and the path relative to base (fallback).
300
- fn make_relative_or_prefixed(file_dir: &Path, target: &Path) -> String {
301
- // Canonicalize both paths where possible so diff_paths returns a clean relative path
302
- let target_abs = std::fs::canonicalize(target).unwrap_or_else(|_| target.to_path_buf());
303
- let file_dir_abs = std::fs::canonicalize(file_dir).unwrap_or_else(|_| file_dir.to_path_buf());
304
-
305
- // Try to compute a relative path from file_dir to target
306
- let rel =
307
- pathdiff::diff_paths(&target_abs, &file_dir_abs).unwrap_or_else(|| target_abs.clone());
308
-
309
- let mut s = rel.to_string_lossy().to_string().replace('\\', "/");
310
-
311
- // Ensure relative paths start with ./
312
- if !s.starts_with('.') && !s.starts_with('/') {
313
- s = format!("./{}", s);
314
- }
315
-
316
- // Strip common source extensions (keep behaviour consistent with TS/SWC expectations)
317
- if s.ends_with(".d.ts") {
318
- s.truncate(s.len() - 5);
319
- } else if s.ends_with(".tsx") || s.ends_with(".jsx") {
320
- s.truncate(s.len() - 4);
321
- } else if s.ends_with(".ts") || s.ends_with(".js") {
322
- s.truncate(s.len() - 3);
323
- }
324
-
325
- s
326
- }
327
-
328
- /// TypeScript to JavaScript compiler backed by SWC.
329
- ///
330
- /// `TypeScriptCompiler` wraps SWC parsing, transforms and codegen to produce
331
- /// JavaScript output and an optional source map. It is configured using
332
- /// `TsConfigOptions` so the host can control whether source maps are
333
- /// emitted and which ECMAScript target is selected.
334
- pub struct TypeScriptCompiler {
335
- ts_config: TsConfigOptions,
336
- }
337
-
338
- impl TypeScriptCompiler {
339
- /// Create a new compiler configured by `ts_config`.
340
- pub fn new(ts_config: TsConfigOptions) -> Self {
341
- Self { ts_config }
342
- }
343
-
344
- /// Compile a single TypeScript file to JavaScript.
345
- pub fn compile_file(&self, input: &Path, output: &Path) -> Result<(), String> {
346
- let cm: Lrc<SourceMap> = Default::default();
347
-
348
- // Capture errors in a buffer instead of printing to stderr
349
- let error_buffer = ErrorBuffer::new();
350
- let error_buffer_clone = error_buffer.clone();
351
-
352
- let emitter = EmitterWriter::new(Box::new(error_buffer), Some(cm.clone()), false, true);
353
- let handler = Handler::with_emitter(true, false, Box::new(emitter));
354
-
355
- let fm = cm
356
- .load_file(input)
357
- .map_err(|e| format!("Failed to load input {}: {}", input.display(), e))?;
358
-
359
- let comments = SingleThreadedComments::default();
360
-
361
- // Parse TypeScript (no TSX support - backend only)
362
- let lexer = Lexer::new(
363
- Syntax::Typescript(TsSyntax {
364
- tsx: false,
365
- ..Default::default()
366
- }),
367
- self.ts_config.target,
368
- StringInput::from(&*fm),
369
- Some(&comments),
370
- );
371
-
372
- let mut parser = Parser::new_from(lexer);
373
-
374
- for e in parser.take_errors() {
375
- e.into_diagnostic(&handler).emit();
376
- }
377
-
378
- let module = parser.parse_program().map_err(|e| {
379
- e.into_diagnostic(&handler).emit();
380
-
381
- let error_msg = error_buffer_clone.get_content();
382
- if error_msg.is_empty() {
383
- format!("Failed to parse {}", input.display())
384
- } else {
385
- format!("\n{}", error_msg.trim())
386
- }
387
- })?;
388
-
389
- // Apply transformations and generate code + optional sourcemap
390
- let globals = Globals::default();
391
- let (code, map_opt) = GLOBALS
392
- .set(&globals, || {
393
- self.transform_and_generate(module, &cm, &comments, input)
394
- })
395
- .map_err(|e| format!("Transformation error: {:?}", e))?;
396
-
397
- // Write output with optional sourcemap
398
- if self.ts_config.emit_sourcemap {
399
- if let Some(map) = map_opt {
400
- write_sourcemap_and_code(output, &code, Some(map))?;
401
- } else {
402
- std::fs::write(output, code)
403
- .map_err(|e| format!("Failed to write output {}: {}", output.display(), e))?;
404
- }
405
- } else {
406
- std::fs::write(output, code)
407
- .map_err(|e| format!("Failed to write output {}: {}", output.display(), e))?;
408
- }
409
-
410
- Ok(())
411
- }
412
-
413
- /// Apply SWC transforms (resolver, strip, CommonJS conversion, hygiene)
414
- /// and perform code generation.
415
- ///
416
- /// Now accepts `input` to compute relative paths when rewriting imports.
417
- fn transform_and_generate(
418
- &self,
419
- module: swc_ecma_ast::Program,
420
- cm: &Lrc<SourceMap>,
421
- comments: &SingleThreadedComments,
422
- input: &Path,
423
- ) -> Result<(String, Option<String>), String> {
424
- let unresolved_mark = Mark::new();
425
- let top_level_mark = Mark::new();
426
-
427
- // Start from Program (owned)
428
- let mut program = module;
429
-
430
- // If ts_config defines base_url or paths, run our paths rewriter
431
- if let Some(base_url) = &self.ts_config.base_url {
432
- let compiled_paths = &self.ts_config.paths; // Used direct field access
433
- if !compiled_paths.is_empty() {
434
- let rewriter = PathsRewriter {
435
- base_url: base_url.clone(),
436
- paths: compiled_paths.clone(),
437
- file_dir: input
438
- .parent()
439
- .map(PathBuf::from)
440
- .unwrap_or_else(|| PathBuf::from(".")),
441
- };
442
-
443
- // visit mutably to rewrite specifiers
444
- let mut rewriter = rewriter;
445
- program.visit_mut_with(&mut rewriter);
446
- }
447
- }
448
-
449
- // agora aplique as transforms normais
450
- let module = program.apply(resolver(unresolved_mark, top_level_mark, true));
451
- let module = module.apply(strip(unresolved_mark, top_level_mark));
452
- let module = module.apply(common_js(
453
- PathResolverEnum::Default,
454
- unresolved_mark,
455
- swc_ecma_transforms_module::util::Config::default(),
456
- swc_ecma_transforms_module::common_js::FeatureFlag::default(),
457
- ));
458
- let module = module.apply(hygiene());
459
- let program = module.apply(fixer(Some(comments)));
460
-
461
- // NOTE: buffer de mappings como Vec<(BytePos, LineCol)>
462
- let mut src_map_buf: Vec<(BytePos, LineCol)> = Vec::new();
463
- let mut code_buf: Vec<u8> = Vec::new();
464
-
465
- {
466
- let js_writer = JsWriter::new(cm.clone(), "\n", &mut code_buf, Some(&mut src_map_buf));
467
- let mut emitter = CodegenEmitter {
468
- cfg: Default::default(),
469
- cm: cm.clone(),
470
- comments: Some(comments),
471
- wr: Box::new(js_writer),
472
- };
473
-
474
- emitter
475
- .emit_program(&program)
476
- .map_err(|e| format!("codegen emit error: {:?}", e))?;
477
- }
478
-
479
- let code = String::from_utf8(code_buf).map_err(|e| format!("code not utf8: {:?}", e))?;
480
-
481
- let map = if !src_map_buf.is_empty() {
482
- let sm = cm.build_source_map(&src_map_buf, None, SourceMapConfigImpl);
483
- let mut s = Vec::new();
484
- sm.to_writer(&mut s)
485
- .map_err(|e| format!("failed to write source map file: {:?}", e))?;
486
- Some(String::from_utf8(s).map_err(|e| format!("source map not utf8: {:?}", e))?)
487
- } else {
488
- None
489
- };
490
-
491
- Ok((code, map))
492
- }
493
- }
@@ -1,67 +0,0 @@
1
- use std::path::PathBuf;
2
-
3
- use super::tsconfig::{parse_tsconfig, TsConfigOptions};
4
-
5
- /// Build configuration for the TypeScript compiler.
6
- ///
7
- /// `BuildConfig` holds paths and parsed TypeScript options used by the
8
- /// native builder. It also provides convenient helpers to compute output
9
- /// locations and string representations used in other modules.
10
- #[derive(Clone, Debug)]
11
- pub struct BuildConfig {
12
- /// Root directory containing the source files to compile.
13
- pub source_root: PathBuf,
14
-
15
- /// Output root directory where compiled assets will be written.
16
- pub out_root: PathBuf,
17
-
18
- /// Parsed tsconfig options used to configure the compiler.
19
- pub ts_config: TsConfigOptions,
20
-
21
- /// Glob patterns that should be ignored when scanning source files.
22
- pub ignore_patterns: Vec<String>,
23
- }
24
-
25
- impl BuildConfig {
26
- /// Create a new `BuildConfig` from `source_root` and `out_root` strings.
27
- ///
28
- /// This function attempts to parse `tsconfig.json` in the current
29
- /// working directory to populate compiler options. It returns an error
30
- /// string when parsing fails.
31
- pub fn new(source_root: String, out_root: String) -> Result<Self, String> {
32
- let tsconfig_path = PathBuf::from("tsconfig.json");
33
- let ts_config = parse_tsconfig(Some(&tsconfig_path))?;
34
-
35
- let source_root = PathBuf::from(source_root);
36
- let out_root = PathBuf::from(out_root);
37
-
38
- Ok(Self {
39
- source_root,
40
- out_root,
41
- ts_config,
42
- ignore_patterns: vec![".test.ts".to_string(), ".spec.ts".to_string()],
43
- })
44
- }
45
-
46
- /// Return the `source_root` as a UTF-8 lossily converted `String`.
47
- pub fn source_root_str(&self) -> String {
48
- self.source_root.to_string_lossy().to_string()
49
- }
50
-
51
- /// Return the `out_root` as a UTF-8 lossily converted `String`.
52
- pub fn output_path_str(&self) -> String {
53
- self.out_root.to_string_lossy().to_string()
54
- }
55
-
56
- /// Compute the output path for a given input relative path.
57
- ///
58
- /// The function appends `input_relative` to the `out_root` and changes
59
- /// the extension to `.js` (e.g. `src/users/route.ts` ->
60
- /// `<out_root>/src/users/route.js`).
61
- pub fn compute_output_path(&self, input_relative: &str) -> PathBuf {
62
- let mut out_path = self.out_root.clone();
63
- out_path.push(input_relative);
64
- out_path.set_extension("js");
65
- out_path
66
- }
67
- }