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

Let an alias represent more than one directory #17423

Closed
4 tasks done
Nefcanto opened this issue Jun 8, 2024 · 6 comments
Closed
4 tasks done

Let an alias represent more than one directory #17423

Nefcanto opened this issue Jun 8, 2024 · 6 comments

Comments

@Nefcanto
Copy link

Nefcanto commented Jun 8, 2024

Description

The original discussion is here: #17344

Let us be able to create an alias, and configure it to represent more than one directory.

Example:

import { Stats, ApproveReject } from "Courses"

The Courses alias should be resolved for this file-system hierarchy:

src
    Modules
        Courses
             Admin
                   ApproveReject.jsx
            Common
                   Stats.jsx

The reason is that we break a complex UI into components, and then break those components into different directories. One directory is the components shared between roles. For example, the instructor should be able to see stats, the admin should see stats, and students should see stats for a given course. So the Stats.jsx file/component goes into the Common directory.

Then we put each role's components into the role's directory. For example, the ApproveReject.jsx file/component should be only accessible to admins.

We have an SPL that creates these mappings in a dynamically created docker-compose.yml file. For example, when our developers work on instructor.mooc.tld, they would be given these directories:

src
    Modules
        Courses
            Instructor
                 Create.jsx
            Common
                   Stats.jsx

This directory structure is created dynamically using our SPL. This means that the developer inside the docker container does not have access to ApproveReject.jsx when he is developing for the instructor panel.

Right now we need to create two aliases called CoursesCommon and CoursesAdmin. We don't have a problem with the length of these aliases or working with two aliases. The problem is that the internal knowledge is leaked out.

Our developers should know what component is shared and what is not. This becomes tricky as the number of components is increases. And this makes refactoring hard if we decide that a component should not be shared anymore or should be shared from now on.

Suggested solution

Right now you take a simple object for aliases:

    return {
        resolve: {
            alias: {
                "CoursesCommon": "src/Courses/Common/Exports.jsx",
                "CoursesAdmin": "src/Courses/Admin/Exports.jsx"
            },
            preserveSymlinks: true
        },

I recommend that you also take an array of strings for the value of an alias:

    return {
        resolve: {
            alias: {
                "Courses": [
                      "src/Courses/Common/Exports.jsx",
                      "src/Courses/Admin/Exports.jsx"
                ]
            },
            preserveSymlinks: true
        },

Alternative

No response

Additional context

No response

Validations

@bluwy
Copy link
Member

bluwy commented Jun 10, 2024

I'm not sure if alias makes sense for this usecase. How it works today is that it simply replaces the import path so that other plugins can resolve later, so for instance:

"CoursesCommon": "src/Courses/Common/Exports.jsx"

When you do import { ... } from "CoursesCommon", it replaces as import { ... } from "src/Courses/Common/Exports.jsx" (technically, paths like this usually won't work in runtime and you need to specify the absolute path instead).

If you have multiple paths, what exactly would it be replaced as? Especially given that you can import multiple named exports from a single specifier.

What this looks like is perhaps a dynamic version of barrel files, where importing "Courses" would resolve to a virtual module with:

export * from "src/Courses/Admin/Exports.jsx"
export * from "src/Courses/Common/Exports.jsx"

Which you should be able to create a plugin that creates virtual modules like this. And in this case, I think it's less related to an "alias".

Usually for features like this, we recommend to create an external Vite plugin first, and if there's many interest, we can consider having this builtin, so for now I'll close this.

@bluwy bluwy closed this as not planned Won't fix, can't repro, duplicate, stale Jun 10, 2024
@Nefcanto
Copy link
Author

@bluwy, it seems that Webpack supported this feature almost 5 years ago. Out of curiosity, what architectural difference is there to prevent it? I understand you mentioned you replaced an alias with the path it represents. But I'm curious about what the true spirit behind aliases could be.

The way I see it, an alias is a layer of abstraction. It's not a direct one-to-one map to the underlying structure. It's there to enable us to do more things (that's what abstraction does). In the ideal world, an alias should even represent a function to dynamically find those specified modules.

Anyway, thank you for the "orchestrator barrel" idea. That might solve our problem.

@bluwy
Copy link
Member

bluwy commented Jun 10, 2024

IIUC webpack's array support is "try each item until the first matches", which means a single alias will still map to a single file path only, so I don't think it helps your usecase here? Both webpack and Vite has the same one-to-one map structure.

In the ideal world, an alias should even represent a function to dynamically find those specified modules.

It does with customResolver, but you'd still map a single specifier to another, not multiple.

@Nefcanto
Copy link
Author

@bluwy, that's exactly what I want. When I say import { Stats } from "Courses" I want it to search for the first path and if it's found then it returns it. But if nothing is found, then it searches the second path and so on.

That's what that comment says:

const config = {
  resolve: {
    alias: {
      mySingleAlias: '/path/to/alias',
      myMultiAlias: [
        '/path/to/try/first',
        '/path/to/try/second',
        '/path/to/try/last',
      ],
    },
  },
};

I read the customResolver link you sent, and this sentence:

to provide separate module resolution for an individual alias

seems to be what we need. Yet I failed to understand the rest of the docs.

Do you think it would serve our purpose? I will write import { FirstComponent, SecondComponent } from "SomeAlias". Will I be able to resolve the FirstComponent from one directory and the SecondComponent from another directory using the customResolver?

@bluwy
Copy link
Member

bluwy commented Jun 10, 2024

that's exactly what I want. When I say import { Stats } from "Courses" I want it to search for the first path and if it's found then it returns it.

But that doesn't help if you want to import Stats, ApproveReject that comes from two different files? Checking path by path still means you'd resolve to one path, and there's no one path that would export both Stats, ApproveReject.

Do you think it would serve our purpose? I will write import { FirstComponent, SecondComponent } from "SomeAlias". Will I be able to resolve the FirstComponent from one directory and the SecondComponent from another directory using the customResolver?

I don't think it will, with the same reasoning above.


The thing with path resolution is that you're checking if "some plugins can handle a specifier" or "the specifier matches a filesystem path". Path resolution doesn't involve parsing the file, and checking what it exports.

@Nefcanto
Copy link
Author

So, in that case the only option that remains is to merge both barrel files dynamically as you have mentioned. A dynamic barrel. Thank you.

@github-actions github-actions bot locked and limited conversation to collaborators Jun 25, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

2 participants