mirror of
https://github.com/luau-lang/rfcs.git
synced 2025-05-04 10:43:48 +01:00
Add amended-alias-resolution.md
This commit is contained in:
parent
549e468c6d
commit
cc05433f2d
1 changed files with 165 additions and 0 deletions
165
docs/amended-alias-resolution.md
Normal file
165
docs/amended-alias-resolution.md
Normal file
|
@ -0,0 +1,165 @@
|
||||||
|
# Amended Alias Syntax and Resolution Semantics
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
We need to finalize a syntax for aliases in require statements and determine intuitive resolution semantics as we prepare to build a package management system.
|
||||||
|
|
||||||
|
## Motivation
|
||||||
|
|
||||||
|
As we prepare to build an official Luau package management system, we need unambiguous alias functionality.
|
||||||
|
|
||||||
|
In an [old RFC for require-by-string](https://github.com/luau-lang/luau/pull/969), we removed the `@` prefix from aliases altogether and opted for the following design:
|
||||||
|
the path `require("my/module")` is resolved by first checking if `my` is an alias defined in a `.luaurc` file.
|
||||||
|
If so, we perform a string substitution on `my`.
|
||||||
|
If not, we try to resolve `my/module` relative to the requiring file.
|
||||||
|
|
||||||
|
This design changed in the next few months and became what we now have in [require-by-string-aliases.md](require-by-string-aliases.md):
|
||||||
|
when requiring an alias, it must be prepended with the `@` prefix.
|
||||||
|
Otherwise, we will assume the given string does not contain an alias and will resolve it relative to the requiring file.
|
||||||
|
|
||||||
|
There have been disagreements about which approach is more appropriate, so this RFC will serve as a way to document the ongoing discussion and the final decision we make.
|
||||||
|
|
||||||
|
## Design and Drawbacks
|
||||||
|
|
||||||
|
Below, we have multiple different options for syntax and resolution semantics.
|
||||||
|
For each design, assume that the following `.luaurc` file is defined:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"aliases": {
|
||||||
|
"libs": "/My/Libraries/Directory"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Additionally, assume that we have a file named `dependency.luau` located in these two locations:
|
||||||
|
1. `./libs/dependency.luau`
|
||||||
|
2. `/My/Libraries/Directory/dependency.luau`
|
||||||
|
|
||||||
|
### (1) Make explicit prefixes necessary
|
||||||
|
|
||||||
|
#### (1a) Make aliases explicit with `@` prefix
|
||||||
|
|
||||||
|
We make usage of the `@` prefix necessary in aliased require statements.
|
||||||
|
This means that any unprefixed string is always treated as an unaliased path.
|
||||||
|
```luau
|
||||||
|
-- Requires ./libs/dependency.luau
|
||||||
|
require("libs/dependency")
|
||||||
|
|
||||||
|
-- Requires ./libs/dependency.luau
|
||||||
|
require("./libs/dependency")
|
||||||
|
|
||||||
|
-- Requires /My/Libraries/Directory/dependency.luau
|
||||||
|
require("@libs/dependency")
|
||||||
|
```
|
||||||
|
|
||||||
|
The main drawback of this approach is that projects with many _external_ dependencies will have many `@` symbols appearing within require statements.
|
||||||
|
|
||||||
|
This is similar to [Lune's approach](https://lune-org.github.io/docs/getting-started/2-introduction/8-modules#file-require-statements) and is identical to the approach currently described in [require-by-string-aliases.md](require-by-string-aliases.md).
|
||||||
|
|
||||||
|
#### (1b) Make relative paths explicit with either `./` or `../` prefix
|
||||||
|
|
||||||
|
We make usage of either the `./` or `../` prefix necessary in relative require statements.
|
||||||
|
This means that any unprefixed string is always treated as an aliased path.
|
||||||
|
```luau
|
||||||
|
-- Requires /My/Libraries/Directory/dependency.luau
|
||||||
|
require("libs/dependency")
|
||||||
|
|
||||||
|
-- Requires ./libs/dependency.luau
|
||||||
|
require("./libs/dependency")
|
||||||
|
|
||||||
|
-- Requires /My/Libraries/Directory/dependency.luau
|
||||||
|
require("@libs/dependency")
|
||||||
|
```
|
||||||
|
|
||||||
|
The main drawback of this approach is that projects with many _internal_ dependencies will have many `./` and `../` symbols appearing within require statements.
|
||||||
|
|
||||||
|
This is similar to [darklua's approach](https://darklua.com/docs/path-require-mode/).
|
||||||
|
|
||||||
|
### (2) Strongly recommend explicit prefixes
|
||||||
|
|
||||||
|
We don't make the `@`, `./`, or `../` prefixes necessary, but we strongly recommend them.
|
||||||
|
|
||||||
|
This can be accomplished by allowing unprefixed paths but throwing an error if they resolve to multiple files.
|
||||||
|
There is a performance cost to this: currently, if we successfully match a given path to a file, we stop searching for other matches.
|
||||||
|
If this approach were implemented, we would need to search for at most two matching files in case we need to throw an error.
|
||||||
|
In the case of multiple nested directories with their own `.luaurc` files and `paths` arrays, this search could take linear time with respect to the total number of paths defined, in the worst case.
|
||||||
|
|
||||||
|
Of course, developers could use the `@`, `./`, or `../` prefixes to explicitly specify a search type and eliminate this performance cost.
|
||||||
|
```luau
|
||||||
|
-- Error: ./libs/dependency.luau exists and .luaurc defines a distinct "libs" alias
|
||||||
|
-- Would require ./libs/dependency.luau if "libs" alias did not exist
|
||||||
|
-- Would require /My/Libraries/Directory/dependency.luau if ./libs/dependency.luau did not exist
|
||||||
|
require("libs/dependency")
|
||||||
|
|
||||||
|
-- Requires ./libs/dependency.luau
|
||||||
|
require("./libs/dependency")
|
||||||
|
|
||||||
|
-- Requires /My/Libraries/Directory/dependency.luau
|
||||||
|
require("@libs/dependency")
|
||||||
|
```
|
||||||
|
|
||||||
|
This is a compromise between approaches (1a) and (1b) that sacrifices performance for compatibility and readability.
|
||||||
|
|
||||||
|
## Alternatives
|
||||||
|
|
||||||
|
### All explicit prefixes necessary
|
||||||
|
|
||||||
|
An alternative approach is to combine approaches (1a) and (1b) and always require prefixes for disambiguation.
|
||||||
|
In other words, this is similar to approach (2), but any unprefixed path will _always_ result in an error.
|
||||||
|
This avoids the performance cost of approach (2), but it is not backwards compatible with existing code.
|
||||||
|
```luau
|
||||||
|
-- Error: must have either "@", "./", or "../" prefix
|
||||||
|
require("libs/dependency")
|
||||||
|
|
||||||
|
-- Requires ./libs/dependency.luau
|
||||||
|
require("./libs/dependency")
|
||||||
|
|
||||||
|
-- Requires /My/Libraries/Directory/dependency.luau
|
||||||
|
require("@libs/dependency")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Use implicit resolution ordering to avoid `@` prefix
|
||||||
|
|
||||||
|
If we choose to remove the `@` prefix, we must define an implicit resolution ordering.
|
||||||
|
In essence, aliases must either represent overrides or fallbacks for relative path resolution.
|
||||||
|
A drawback of this approach is that implicit resolution ordering can be confusing, and people disagree on which ordering is best.
|
||||||
|
|
||||||
|
#### Aliases as overrides
|
||||||
|
|
||||||
|
If aliases were overrides, we would have the following behavior.
|
||||||
|
This is identical to the [old RFC](https://github.com/luau-lang/luau/pull/969)'s behavior.
|
||||||
|
```luau
|
||||||
|
-- Requires /My/Libraries/Directory/dependency.luau
|
||||||
|
require("libs/dependency")
|
||||||
|
|
||||||
|
-- Requires ./libs/dependency.luau
|
||||||
|
require("./libs/dependency")
|
||||||
|
|
||||||
|
-- Error: there is no "@libs" alias in .luaurc
|
||||||
|
require("@libs/dependency")
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Aliases as fallbacks
|
||||||
|
|
||||||
|
If aliases were fallbacks, we would have the following behavior.
|
||||||
|
```luau
|
||||||
|
-- Requires ./libs/dependency.luau
|
||||||
|
require("libs/dependency")
|
||||||
|
|
||||||
|
-- Requires ./libs/dependency.luau
|
||||||
|
require("./libs/dependency")
|
||||||
|
|
||||||
|
-- Error: there is no "@libs" alias in .luaurc
|
||||||
|
require("@libs/dependency")
|
||||||
|
```
|
||||||
|
|
||||||
|
If `./libs/dependency.luau` did not exist, however, we would have the following:
|
||||||
|
```luau
|
||||||
|
-- Requires /My/Libraries/Directory/dependency.luau
|
||||||
|
require("libs/dependency")
|
||||||
|
|
||||||
|
-- Error: does not exist
|
||||||
|
require("./libs/dependency")
|
||||||
|
|
||||||
|
-- Error: there is no "@libs" alias in .luaurc
|
||||||
|
require("@libs/dependency")
|
||||||
|
```
|
Loading…
Add table
Reference in a new issue