@lithia-js/native 1.0.0-canary.2 → 1.0.0-canary.3
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 +6 -0
- package/package.json +14 -14
- package/src/builder/compiler.rs +0 -493
- package/src/builder/config.rs +0 -67
- package/src/builder/mod.rs +0 -126
- package/src/builder/sourcemap.rs +0 -36
- package/src/builder/tests.rs +0 -78
- package/src/builder/tsconfig.rs +0 -100
- package/src/builder/types.rs +0 -53
- package/src/events/convention.rs +0 -36
- package/src/events/mod.rs +0 -87
- package/src/events/processor.rs +0 -113
- package/src/events/tests.rs +0 -41
- package/src/events/transformer.rs +0 -78
- package/src/lib.rs +0 -54
- package/src/router/convention.rs +0 -239
- package/src/router/mod.rs +0 -118
- package/src/router/processor.rs +0 -201
- package/src/router/transformer.rs +0 -281
- package/src/scanner/mod.rs +0 -345
package/CHANGELOG.md
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lithia-js/native",
|
|
3
|
-
"version": "1.0.0-canary.
|
|
3
|
+
"version": "1.0.0-canary.3",
|
|
4
4
|
"private": false,
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -41,22 +41,22 @@
|
|
|
41
41
|
"provenance": true
|
|
42
42
|
},
|
|
43
43
|
"optionalDependencies": {
|
|
44
|
-
"@lithia-js/native-darwin-x64": "1.0.0-canary.
|
|
45
|
-
"@lithia-js/native-win32-x64-msvc": "1.0.0-canary.
|
|
46
|
-
"@lithia-js/native-linux-x64-gnu": "1.0.0-canary.
|
|
47
|
-
"@lithia-js/native-linux-x64-musl": "1.0.0-canary.
|
|
48
|
-
"@lithia-js/native-linux-arm-gnueabihf": "1.0.0-canary.
|
|
49
|
-
"@lithia-js/native-linux-arm64-gnu": "1.0.0-canary.
|
|
50
|
-
"@lithia-js/native-darwin-arm64": "1.0.0-canary.
|
|
51
|
-
"@lithia-js/native-win32-ia32-msvc": "1.0.0-canary.
|
|
52
|
-
"@lithia-js/native-linux-arm64-musl": "1.0.0-canary.
|
|
53
|
-
"@lithia-js/native-win32-arm64-msvc": "1.0.0-canary.
|
|
44
|
+
"@lithia-js/native-darwin-x64": "1.0.0-canary.3",
|
|
45
|
+
"@lithia-js/native-win32-x64-msvc": "1.0.0-canary.3",
|
|
46
|
+
"@lithia-js/native-linux-x64-gnu": "1.0.0-canary.3",
|
|
47
|
+
"@lithia-js/native-linux-x64-musl": "1.0.0-canary.3",
|
|
48
|
+
"@lithia-js/native-linux-arm-gnueabihf": "1.0.0-canary.3",
|
|
49
|
+
"@lithia-js/native-linux-arm64-gnu": "1.0.0-canary.3",
|
|
50
|
+
"@lithia-js/native-darwin-arm64": "1.0.0-canary.3",
|
|
51
|
+
"@lithia-js/native-win32-ia32-msvc": "1.0.0-canary.3",
|
|
52
|
+
"@lithia-js/native-linux-arm64-musl": "1.0.0-canary.3",
|
|
53
|
+
"@lithia-js/native-win32-arm64-msvc": "1.0.0-canary.3"
|
|
54
54
|
},
|
|
55
55
|
"exports": {
|
|
56
56
|
".": {
|
|
57
|
-
"types": "./
|
|
58
|
-
"require": "./
|
|
59
|
-
"import": "./
|
|
57
|
+
"types": "./index.d.ts",
|
|
58
|
+
"require": "./index.js",
|
|
59
|
+
"import": "./index.js"
|
|
60
60
|
}
|
|
61
61
|
},
|
|
62
62
|
"scripts": {
|
package/src/builder/compiler.rs
DELETED
|
@@ -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
|
-
}
|
package/src/builder/config.rs
DELETED
|
@@ -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
|
-
}
|