I’m adding support for the Luau language, and started to “dig” into the modules.
There are several package managers for the language, and the most basic thing I want to support is to detect package manager configuration and exclude the Packages folder and also use the list of excluded folders to configure LSP. For the simplicity I handle only situations where project is a single module now.
I guess there is a lot of way to achieve that, but I wanted to create a LibraryEntity
and add an excluded roots to it.
I create a library and then add it to the module as a dependency (assume all the checks to avoid adding library multiple times are done as well). The code now runs as a ProjectActivity
val libraryEntitySource =
LegacyBridgeJpsEntitySourceFactory.getInstance(project)
// TODO (AleksandrSl 31/08/2025): It's null in the example, should it be?
.createEntitySourceForProjectLibrary(null)
val library = builder.addEntity(
LibraryEntity(
name = libraryName,
tableId = LibraryTableId.ProjectLibraryTableId,
roots = listOf(LibraryRoot(packagesDirUrl, LibraryRootTypeId.SOURCES)),
entitySource = libraryEntitySource
) {
this.excludedRoots = listOf(ExcludeUrlEntity(packagesDirUrl, libraryEntitySource))
}
)
libraryId = library.symbolicId
builder.modifyModuleEntity(moduleEntity) {
dependencies += LibraryDependency(libraryId, false, DependencyScope.COMPILE)
}
Doing this I encountered several things
- If my project had an
.idea
folder already this code has no effect. I see it run using a debugger, and the library is created and added to the dependencies, but no configs are created/changed. - If I remove the
.idea
folder, do File > Open on the same folder, and then close the project I see the new module being persisted and libraries created (I guess until you close the project all this data is in the memory)
The files created look like this
Module
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
<excludeFolder url="file://$MODULE_DIR$/temp" />
<excludeFolder url="file://$MODULE_DIR$/tmp" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Luau Packages" level="project" />
</component>
</module>
Library
<component name="libraryTable">
<library name="Luau Packages">
<CLASSES />
<JAVADOC />
<SOURCES>
<root url="file://$PROJECT_DIR$/Packages" />
</SOURCES>
<excluded>
<root url="file://$PROJECT_DIR$/Packages" />
</excluded>
</library>
</component>
However the folder is still not excluded
So the questions would be:
- Is this a meaningful approach?
- Is there a way to add a library to the already existing project? (e.g. if people install the new version of my plugin I want this functionality to work without destroying the old modules)
- Should I excluded the folder in the library, or just do that in the module itself? Since this exclusion is specific to a library I thought the first approach is the correct one.
- Should the whole folder be a single library? In WS the whole
node_modules
folder is considered a single library. However, in a gradle project every dependency is considered a separate library. Is there a correct way to do this, or it depends on the purpose? - Since the
Packages
folder could be created later, does it make sense to check for it’s creation inAsyncFileListener
and add to excluded roots or there is a better way?
I also experimented with creating my own modules for a monorepo project, but ended up with the same thing - modules not being persisted.