Skip to content

Commit a6a8f33

Browse files
Support bundling with Luau types (#249)
Correctly preserve types when bundling Luau Note that because of how Luau currently works, types that uses `typeof` (like `type Example = typeof(variable)`) can't be hoisted correctly if they include a local variable.
1 parent d33962c commit a6a8f33

25 files changed

+985
-287
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Changelog
22

3+
* support Luau types when bundling ([#249](https://github.com/seaofvoices/darklua/pull/249))
4+
35
## 0.15.0
46

57
* improve file watching: re-process specific files, sourcemap changes re-process the project, bundling re-starts whenever a dependent file changes ([#239](https://github.com/seaofvoices/darklua/pull/239))

Cargo.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ durationfmt = "0.1.1"
3232
elsa = "1.10.0"
3333
env_logger = "0.11.5"
3434
full_moon = { version = "1.0.0", features = ["roblox"] }
35+
indexmap = "2.7.0"
3536
json5 = "0.4.1"
3637
log = "0.4.22"
3738
pathdiff = "0.2.3"

src/nodes/arguments.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ impl Arguments {
138138
}
139139
}
140140

141-
pub(crate) fn shift_token_line(&mut self, amount: usize) {
141+
pub(crate) fn shift_token_line(&mut self, amount: isize) {
142142
match self {
143143
Arguments::Tuple(tuple) => tuple.shift_token_line(amount),
144144
Arguments::String(_) | Arguments::Table(_) => {}

src/nodes/expressions/interpolated_string.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ impl InterpolationSegment {
133133
}
134134
}
135135

136-
pub(crate) fn shift_token_line(&mut self, amount: usize) {
136+
pub(crate) fn shift_token_line(&mut self, amount: isize) {
137137
match self {
138138
InterpolationSegment::String(segment) => segment.shift_token_line(amount),
139139
InterpolationSegment::Value(segment) => segment.shift_token_line(amount),

src/nodes/expressions/number.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ impl NumberExpression {
273273
}
274274
}
275275

276-
pub(crate) fn shift_token_line(&mut self, amount: usize) {
276+
pub(crate) fn shift_token_line(&mut self, amount: isize) {
277277
match self {
278278
NumberExpression::Decimal(number) => number.shift_token_line(amount),
279279
NumberExpression::Hex(number) => number.shift_token_line(amount),

src/nodes/expressions/table.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ impl TableEntry {
180180
}
181181
}
182182

183-
pub(crate) fn shift_token_line(&mut self, amount: usize) {
183+
pub(crate) fn shift_token_line(&mut self, amount: isize) {
184184
match self {
185185
TableEntry::Field(entry) => entry.shift_token_line(amount),
186186
TableEntry::Index(entry) => entry.shift_token_line(amount),

src/nodes/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ macro_rules! impl_token_fns {
7878
)*)?
7979
}
8080

81-
pub(crate) fn shift_token_line(&mut self, amount: usize) {
81+
pub(crate) fn shift_token_line(&mut self, amount: isize) {
8282
$(
8383
self.$field.shift_token_line(amount);
8484
)*

src/nodes/statements/type_declaration.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,14 @@ impl TypeDeclarationStatement {
6969
self.exported = true;
7070
}
7171

72+
#[inline]
73+
pub fn remove_exported(&mut self) {
74+
self.exported = false;
75+
if let Some(tokens) = self.tokens.as_mut() {
76+
tokens.export.take();
77+
}
78+
}
79+
7280
#[inline]
7381
pub fn is_exported(&self) -> bool {
7482
self.exported
@@ -195,7 +203,7 @@ impl TypeDeclarationStatement {
195203
}
196204
}
197205

198-
pub(crate) fn shift_token_line(&mut self, amount: usize) {
206+
pub(crate) fn shift_token_line(&mut self, amount: isize) {
199207
self.name.shift_token_line(amount);
200208
if let Some(tokens) = &mut self.tokens {
201209
tokens.shift_token_line(amount);

src/nodes/token.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -263,10 +263,12 @@ impl Token {
263263
}
264264
}
265265

266-
pub(crate) fn shift_token_line(&mut self, amount: usize) {
266+
pub(crate) fn shift_token_line(&mut self, amount: isize) {
267267
match &mut self.position {
268268
Position::LineNumberReference { line_number, .. }
269-
| Position::LineNumber { line_number, .. } => *line_number += amount,
269+
| Position::LineNumber { line_number, .. } => {
270+
*line_number = line_number.saturating_add_signed(amount);
271+
}
270272
Position::Any { .. } => {}
271273
}
272274
}

src/nodes/types/table.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ impl TableEntryType {
207207
}
208208
}
209209

210-
pub(crate) fn shift_token_line(&mut self, amount: usize) {
210+
pub(crate) fn shift_token_line(&mut self, amount: isize) {
211211
match self {
212212
TableEntryType::Property(property) => property.shift_token_line(amount),
213213
TableEntryType::Literal(literal) => literal.shift_token_line(amount),

src/process/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,5 @@ pub use node_counter::NodeCounter;
1818
pub use node_processor::{NodePostProcessor, NodeProcessor};
1919
pub use post_visitor::{DefaultPostVisitor, NodePostVisitor};
2020
pub(crate) use scope_visitor::IdentifierTracker;
21-
pub use scope_visitor::{Scope, ScopeVisitor};
21+
pub use scope_visitor::{Scope, ScopePostVisitor, ScopeVisitor};
2222
pub use visitors::{DefaultVisitor, NodeVisitor};

src/process/scope_visitor.rs

+221
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use crate::process::utils::is_valid_identifier;
66
use crate::process::{NodeProcessor, NodeVisitor};
77

88
use super::utils::{identifier_permutator, Permutator};
9+
use super::{NodePostProcessor, NodePostVisitor};
910

1011
/// Defines methods to interact with the concept of lexical scoping. The struct implementing this
1112
/// trait should be able to keep track of identifiers when used along the ScopeVisitor.
@@ -228,6 +229,226 @@ impl<T: NodeProcessor + Scope> NodeVisitor<T> for ScopeVisitor {
228229
}
229230
}
230231

232+
/// A visitor that can be used only with a NodeProcessor that also implements the Scope trait.
233+
pub struct ScopePostVisitor;
234+
235+
impl ScopePostVisitor {
236+
fn visit_block_without_push<T: NodeProcessor + NodePostProcessor + Scope>(
237+
block: &mut Block,
238+
scope: &mut T,
239+
) {
240+
scope.process_block(block);
241+
242+
block
243+
.iter_mut_statements()
244+
.for_each(|statement| Self::visit_statement(statement, scope));
245+
246+
if let Some(last_statement) = block.mutate_last_statement() {
247+
Self::visit_last_statement(last_statement, scope);
248+
};
249+
scope.process_after_block(block);
250+
}
251+
}
252+
253+
impl<T: NodeProcessor + NodePostProcessor + Scope> NodePostVisitor<T> for ScopePostVisitor {
254+
fn visit_block(block: &mut Block, scope: &mut T) {
255+
scope.push();
256+
Self::visit_block_without_push(block, scope);
257+
scope.pop();
258+
}
259+
260+
fn visit_local_assign(statement: &mut LocalAssignStatement, scope: &mut T) {
261+
scope.process_local_assign_statement(statement);
262+
263+
statement
264+
.iter_mut_values()
265+
.for_each(|value| Self::visit_expression(value, scope));
266+
267+
for r#type in statement
268+
.iter_mut_variables()
269+
.filter_map(TypedIdentifier::mutate_type)
270+
{
271+
Self::visit_type(r#type, scope);
272+
}
273+
274+
statement.for_each_assignment(|variable, expression| {
275+
scope.insert_local(variable.mutate_name(), expression)
276+
});
277+
278+
scope.process_after_local_assign_statement(statement);
279+
}
280+
281+
fn visit_function_expression(function: &mut FunctionExpression, scope: &mut T) {
282+
scope.process_function_expression(function);
283+
284+
for r#type in function
285+
.iter_mut_parameters()
286+
.filter_map(TypedIdentifier::mutate_type)
287+
{
288+
Self::visit_type(r#type, scope);
289+
}
290+
291+
if let Some(variadic_type) = function.mutate_variadic_type() {
292+
Self::visit_function_variadic_type(variadic_type, scope);
293+
}
294+
295+
if let Some(return_type) = function.mutate_return_type() {
296+
Self::visit_function_return_type(return_type, scope);
297+
}
298+
299+
scope.push();
300+
function
301+
.mutate_parameters()
302+
.iter_mut()
303+
.for_each(|parameter| scope.insert(parameter.mutate_name()));
304+
305+
scope.process_scope(function.mutate_block(), None);
306+
307+
Self::visit_block(function.mutate_block(), scope);
308+
scope.pop();
309+
310+
scope.process_after_function_expression(function);
311+
}
312+
313+
fn visit_function_statement(statement: &mut FunctionStatement, scope: &mut T) {
314+
scope.process_function_statement(statement);
315+
scope.process_variable_expression(statement.mutate_function_name().mutate_identifier());
316+
317+
for r#type in statement
318+
.iter_mut_parameters()
319+
.filter_map(TypedIdentifier::mutate_type)
320+
{
321+
Self::visit_type(r#type, scope);
322+
}
323+
324+
if let Some(variadic_type) = statement.mutate_variadic_type() {
325+
Self::visit_function_variadic_type(variadic_type, scope);
326+
}
327+
328+
if let Some(return_type) = statement.mutate_return_type() {
329+
Self::visit_function_return_type(return_type, scope);
330+
}
331+
332+
scope.push();
333+
if statement.get_name().has_method() {
334+
scope.insert_self();
335+
}
336+
statement
337+
.mutate_parameters()
338+
.iter_mut()
339+
.for_each(|parameter| scope.insert(parameter.mutate_name()));
340+
341+
scope.process_scope(statement.mutate_block(), None);
342+
343+
Self::visit_block(statement.mutate_block(), scope);
344+
scope.pop();
345+
346+
scope.process_after_function_statement(statement);
347+
}
348+
349+
fn visit_local_function(statement: &mut LocalFunctionStatement, scope: &mut T) {
350+
scope.process_local_function_statement(statement);
351+
352+
scope.insert_local_function(statement);
353+
354+
for r#type in statement
355+
.iter_mut_parameters()
356+
.filter_map(TypedIdentifier::mutate_type)
357+
{
358+
Self::visit_type(r#type, scope);
359+
}
360+
361+
if let Some(variadic_type) = statement.mutate_variadic_type() {
362+
Self::visit_function_variadic_type(variadic_type, scope);
363+
}
364+
365+
if let Some(return_type) = statement.mutate_return_type() {
366+
Self::visit_function_return_type(return_type, scope);
367+
}
368+
369+
scope.push();
370+
statement
371+
.mutate_parameters()
372+
.iter_mut()
373+
.for_each(|parameter| scope.insert(parameter.mutate_name()));
374+
375+
scope.process_scope(statement.mutate_block(), None);
376+
377+
Self::visit_block(statement.mutate_block(), scope);
378+
scope.pop();
379+
380+
scope.process_after_local_function_statement(statement);
381+
}
382+
383+
fn visit_generic_for(statement: &mut GenericForStatement, scope: &mut T) {
384+
scope.process_generic_for_statement(statement);
385+
386+
statement
387+
.iter_mut_expressions()
388+
.for_each(|expression| Self::visit_expression(expression, scope));
389+
390+
scope.push();
391+
statement
392+
.iter_mut_identifiers()
393+
.for_each(|identifier| scope.insert(identifier.mutate_name()));
394+
395+
for r#type in statement
396+
.iter_mut_identifiers()
397+
.filter_map(TypedIdentifier::mutate_type)
398+
{
399+
Self::visit_type(r#type, scope);
400+
}
401+
402+
scope.process_scope(statement.mutate_block(), None);
403+
404+
Self::visit_block(statement.mutate_block(), scope);
405+
scope.pop();
406+
407+
scope.process_after_generic_for_statement(statement);
408+
}
409+
410+
fn visit_numeric_for(statement: &mut NumericForStatement, scope: &mut T) {
411+
scope.process_numeric_for_statement(statement);
412+
413+
Self::visit_expression(statement.mutate_start(), scope);
414+
Self::visit_expression(statement.mutate_end(), scope);
415+
416+
if let Some(step) = statement.mutate_step() {
417+
Self::visit_expression(step, scope);
418+
};
419+
420+
if let Some(r#type) = statement.mutate_identifier().mutate_type() {
421+
Self::visit_type(r#type, scope);
422+
}
423+
424+
scope.push();
425+
scope.insert(statement.mutate_identifier().mutate_name());
426+
427+
scope.process_scope(statement.mutate_block(), None);
428+
429+
Self::visit_block(statement.mutate_block(), scope);
430+
scope.pop();
431+
432+
scope.process_after_numeric_for_statement(statement);
433+
}
434+
435+
fn visit_repeat_statement(statement: &mut RepeatStatement, scope: &mut T) {
436+
scope.process_repeat_statement(statement);
437+
438+
scope.push();
439+
440+
let (block, condition) = statement.mutate_block_and_condition();
441+
scope.process_scope(block, Some(condition));
442+
443+
Self::visit_block_without_push(statement.mutate_block(), scope);
444+
Self::visit_expression(statement.mutate_condition(), scope);
445+
446+
scope.pop();
447+
448+
scope.process_after_repeat_statement(statement);
449+
}
450+
}
451+
231452
#[derive(Debug, Clone, Default)]
232453
pub(crate) struct IdentifierTracker {
233454
identifiers: Vec<HashSet<String>>,

src/rules/append_text_comment.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ impl Rule for AppendTextComment {
9797
}
9898

9999
let shift_lines = text.lines().count();
100-
ShiftTokenLine::new(shift_lines).flawless_process(block, context);
100+
ShiftTokenLine::new(shift_lines as isize).flawless_process(block, context);
101101

102102
match self.location {
103103
AppendLocation::Start => {

src/rules/bundle/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
pub(crate) mod path_require_mode;
2+
mod rename_type_declaration;
23
mod require_mode;
34

45
use std::path::Path;
@@ -9,6 +10,7 @@ use crate::rules::{
910
};
1011
use crate::Parser;
1112

13+
pub(crate) use rename_type_declaration::RenameTypeDeclarationProcessor;
1214
pub use require_mode::BundleRequireMode;
1315
use wax::Pattern;
1416

0 commit comments

Comments
 (0)