mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IJ-MR-175479 IJ-CR-146078 IJPL-209476 guava as product module
GitOrigin-RevId: 695080bfc70661c5755dae20202ced5e7fa58652
This commit is contained in:
committed by
intellij-monorepo-bot
parent
195269d7bd
commit
e0831b8cf3
Generated
+1
@@ -636,6 +636,7 @@
|
||||
<module fileurl="file://$PROJECT_DIR$/libraries/dokka/intellij.libraries.dokka.iml" filepath="$PROJECT_DIR$/libraries/dokka/intellij.libraries.dokka.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/libraries/grpc/intellij.libraries.grpc.iml" filepath="$PROJECT_DIR$/libraries/grpc/intellij.libraries.grpc.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/libraries/grpc-netty-shaded/intellij.libraries.grpc.netty.shaded.iml" filepath="$PROJECT_DIR$/libraries/grpc-netty-shaded/intellij.libraries.grpc.netty.shaded.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/libraries/guava/intellij.libraries.guava.iml" filepath="$PROJECT_DIR$/libraries/guava/intellij.libraries.guava.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/libraries/hamcrest/intellij.libraries.hamcrest.iml" filepath="$PROJECT_DIR$/libraries/hamcrest/intellij.libraries.hamcrest.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/libraries/hamcrest-more-matchers/intellij.libraries.hamcrest.more.matchers.iml" filepath="$PROJECT_DIR$/libraries/hamcrest-more-matchers/intellij.libraries.hamcrest.more.matchers.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/libraries/junit4/intellij.libraries.junit4.iml" filepath="$PROJECT_DIR$/libraries/junit4/intellij.libraries.junit4.iml" />
|
||||
|
||||
@@ -347,6 +347,7 @@ libraries/detekt-compose-rules
|
||||
libraries/dokka
|
||||
libraries/grpc
|
||||
libraries/grpc-netty-shaded
|
||||
libraries/guava
|
||||
libraries/hamcrest
|
||||
libraries/hamcrest-more-matchers
|
||||
libraries/io
|
||||
|
||||
@@ -0,0 +1,190 @@
|
||||
# Product Embedded Library Modules
|
||||
|
||||
This directory contains product-embedded library modules that make third-party libraries available throughout the IntelliJ platform.
|
||||
|
||||
## Creating a New Library Module
|
||||
|
||||
To create a new product-embedded module for a library (e.g., for a library named "example-lib"):
|
||||
|
||||
### 1. Create Module Structure
|
||||
|
||||
Create the following directory structure:
|
||||
```
|
||||
community/libraries/example-lib/
|
||||
├── intellij.libraries.example.lib.iml
|
||||
└── resources/
|
||||
└── intellij.libraries.example.lib.xml
|
||||
```
|
||||
|
||||
### 2. Create Module Definition File (`.iml`)
|
||||
|
||||
File: `intellij.libraries.example.lib.iml`
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/resources" type="java-resource" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" exported="" name="ExampleLibrary" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
```
|
||||
|
||||
**Key points:**
|
||||
- The `name` attribute in `<orderEntry type="library">` must match the library name defined in `.idea/libraries/ExampleLibrary.xml`
|
||||
- If the module depends on other embedded modules, add them as additional `<orderEntry type="module">` entries (see `junit4` example)
|
||||
|
||||
### 3. Create Plugin Descriptor
|
||||
|
||||
File: `resources/intellij.libraries.example.lib.xml`
|
||||
|
||||
```xml
|
||||
<idea-plugin>
|
||||
</idea-plugin>
|
||||
```
|
||||
|
||||
**Note:** If your module depends on other library modules, add them:
|
||||
```xml
|
||||
<idea-plugin>
|
||||
<dependencies>
|
||||
<module name="intellij.libraries.hamcrest"/>
|
||||
</dependencies>
|
||||
</idea-plugin>
|
||||
```
|
||||
|
||||
### 4. Register in Essential Libraries (if needed)
|
||||
|
||||
If the library should be loaded as an essential embedded module, add it to:
|
||||
|
||||
File: `community/platform/platform-resources/src/META-INF/essential-libs.xml`
|
||||
|
||||
```xml
|
||||
<module name="intellij.libraries.example.lib" loading="embedded"/>
|
||||
```
|
||||
|
||||
Add the entry in alphabetical order.
|
||||
|
||||
### 5. Register in Project Modules
|
||||
|
||||
Add the module to both `modules.xml` files in alphabetical order:
|
||||
|
||||
#### Root project file: `.idea/modules.xml`
|
||||
```xml
|
||||
<module fileurl="file://$PROJECT_DIR$/community/libraries/example-lib/intellij.libraries.example.lib.iml"
|
||||
filepath="$PROJECT_DIR$/community/libraries/example-lib/intellij.libraries.example.lib.iml" />
|
||||
```
|
||||
|
||||
#### Community project file: `community/.idea/modules.xml`
|
||||
```xml
|
||||
<module fileurl="file://$PROJECT_DIR$/libraries/example-lib/intellij.libraries.example.lib.iml"
|
||||
filepath="$PROJECT_DIR$/libraries/example-lib/intellij.libraries.example.lib.iml" />
|
||||
```
|
||||
|
||||
**Important:**
|
||||
- Root `modules.xml` uses full path with `community/` prefix
|
||||
- Community `modules.xml` uses relative path without `community/` prefix
|
||||
|
||||
### 6. Build File
|
||||
|
||||
**Do NOT create `BUILD.bazel` manually** - it will be auto-generated by the build system.
|
||||
|
||||
### 7. Verify Changes
|
||||
|
||||
After creating the module, run the following tests to ensure project structure consistency:
|
||||
|
||||
- **`IdeaUltimatePluginStructureTest`** - Verifies the plugin structure for IDEA Ultimate
|
||||
- **`CommunityProjectConsistencyTest`** - Verifies community project consistency
|
||||
|
||||
Run tests using:
|
||||
```bash
|
||||
./tests.cmd -Dintellij.build.test.patterns=IdeaUltimatePluginStructureTest
|
||||
./tests.cmd -Dintellij.build.test.patterns=CommunityProjectConsistencyTest
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
- **Simple library:** `hamcrest/` - single library dependency
|
||||
- **Library with dependencies:** `junit4/` - depends on hamcrest module
|
||||
- **Multiple libraries:** `grpc/` - exports multiple related libraries
|
||||
|
||||
## Naming Conventions
|
||||
|
||||
- **Directory name:** Use kebab-case (e.g., `example-lib`, `grpc-netty-shaded`)
|
||||
- **Module name:** Use dot-separated format (e.g., `intellij.libraries.example.lib`, `intellij.libraries.grpc.netty.shaded`)
|
||||
- **IML filename:** Should match the module name (e.g., `intellij.libraries.example.lib.iml`)
|
||||
- **Plugin XML filename:** Should match the module name (e.g., `intellij.libraries.example.lib.xml`)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before creating a library module, ensure:
|
||||
1. The library is already defined in `.idea/libraries/` as a project library
|
||||
2. The library artifacts are available in the Maven repository or bundled appropriately
|
||||
|
||||
## Migrating Existing Library References to Library Modules
|
||||
|
||||
When converting a project-level library to an embedded library module, you need to update all `.iml` files that reference the library.
|
||||
|
||||
### Automated Migration Script
|
||||
|
||||
A generic Node.js script is available to automate the migration of library references across the entire project:
|
||||
|
||||
**Location:** `community/libraries/replace-lib-with-module.js`
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
# Dry run to preview changes for Guava
|
||||
node community/libraries/replace-lib-with-module.js --old-lib=Guava --new-module=intellij.libraries.guava --dry-run
|
||||
|
||||
# Apply Guava replacement
|
||||
node community/libraries/replace-lib-with-module.js --old-lib=Guava --new-module=intellij.libraries.guava
|
||||
|
||||
# Replace a different library
|
||||
node community/libraries/replace-lib-with-module.js --old-lib=Jackson --new-module=intellij.libraries.jackson
|
||||
|
||||
# Add custom exclude directories
|
||||
node community/libraries/replace-lib-with-module.js --old-lib=Guava --new-module=intellij.libraries.guava --exclude=custom-dir
|
||||
|
||||
# Show help
|
||||
node community/libraries/replace-lib-with-module.js --help
|
||||
```
|
||||
|
||||
**Script Features:**
|
||||
- Parses `.idea/modules.xml` to find all IML files
|
||||
- Supports multiple replacement patterns:
|
||||
- Regular scope: `<orderEntry type="library" name="Guava" ... />` → `<orderEntry type="module" module-name="intellij.libraries.guava" />`
|
||||
- TEST scope: `<orderEntry type="library" scope="TEST" name="Guava" ... />` → `<orderEntry type="module" ... scope="TEST" />`
|
||||
- TEST scope with exported attribute
|
||||
- Excludes specified directories (default: `fleet/`, `toolbox/`)
|
||||
- Provides detailed statistics of files modified and replacements made
|
||||
- Dry-run mode to preview changes without modifying files
|
||||
|
||||
**Example Output:**
|
||||
```
|
||||
Configuration:
|
||||
Old library: Guava
|
||||
New module: intellij.libraries.guava
|
||||
Excluded dirs: fleet, toolbox
|
||||
Dry run: false
|
||||
|
||||
Found 15000 module files in modules.xml
|
||||
|
||||
Processing IML files:
|
||||
✓ platform/ssh/intellij.platform.ssh.iml (1 regular)
|
||||
✓ community/android/testartifacts/intellij.android.testartifacts.iml (1 regular)
|
||||
...
|
||||
|
||||
Summary:
|
||||
Files scanned: 14500
|
||||
Files modified: 450
|
||||
Files skipped: 550
|
||||
Replacements by type:
|
||||
Regular scope: 396
|
||||
TEST scope: 109
|
||||
TEST scope (exported): 3
|
||||
Total: 508
|
||||
```
|
||||
@@ -0,0 +1,18 @@
|
||||
### auto-generated section `build intellij.libraries.guava` start
|
||||
load("@rules_jvm//:jvm.bzl", "jvm_library", "resourcegroup")
|
||||
|
||||
resourcegroup(
|
||||
name = "guava_resources",
|
||||
srcs = glob(["resources/**/*"]),
|
||||
strip_prefix = "resources"
|
||||
)
|
||||
|
||||
jvm_library(
|
||||
name = "guava",
|
||||
visibility = ["//visibility:public"],
|
||||
srcs = glob([], allow_empty = True),
|
||||
resources = [":guava_resources"],
|
||||
exports = ["@lib//:guava"],
|
||||
runtime_deps = ["@lib//:guava"]
|
||||
)
|
||||
### auto-generated section `build intellij.libraries.guava` end
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/resources" type="java-resource" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" exported="" name="Guava" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -0,0 +1,2 @@
|
||||
<idea-plugin>
|
||||
</idea-plugin>
|
||||
@@ -0,0 +1,267 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Generic script to replace library references with module references in IntelliJ project IML files.
|
||||
*
|
||||
* Usage:
|
||||
* node replace-lib-with-module.js --old-lib=LibName --new-module=intellij.libraries.libname [options]
|
||||
*
|
||||
* Options:
|
||||
* --old-lib=NAME Library name to replace (required)
|
||||
* --new-module=NAME Target module name (required)
|
||||
* --dry-run Show what would be changed without modifying files
|
||||
* --exclude=DIR Additional directory to exclude (can be used multiple times, default: fleet, toolbox)
|
||||
* --help Show this help message
|
||||
*
|
||||
* Examples:
|
||||
* # Dry run for Guava
|
||||
* node replace-lib-with-module.js --old-lib=Guava --new-module=intellij.libraries.guava --dry-run
|
||||
*
|
||||
* # Apply Guava replacement
|
||||
* node replace-lib-with-module.js --old-lib=Guava --new-module=intellij.libraries.guava
|
||||
*
|
||||
* # Replace another library with custom exclusions
|
||||
* node replace-lib-with-module.js --old-lib=Jackson --new-module=intellij.libraries.jackson --exclude=custom-dir
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
// Parse command line arguments
|
||||
const args = process.argv.slice(2);
|
||||
const config = {
|
||||
dryRun: args.includes('--dry-run'),
|
||||
oldLib: null,
|
||||
newModule: null,
|
||||
excludeDirs: ['fleet', 'toolbox'],
|
||||
showHelp: args.includes('--help')
|
||||
};
|
||||
|
||||
// Parse custom arguments
|
||||
for (const arg of args) {
|
||||
if (arg.startsWith('--old-lib=')) {
|
||||
config.oldLib = arg.split('=')[1];
|
||||
} else if (arg.startsWith('--new-module=')) {
|
||||
config.newModule = arg.split('=')[1];
|
||||
} else if (arg.startsWith('--exclude=')) {
|
||||
const dir = arg.split('=')[1];
|
||||
if (!config.excludeDirs.includes(dir)) {
|
||||
config.excludeDirs.push(dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (config.showHelp) {
|
||||
console.log(`
|
||||
Generic script to replace library references with module references in IntelliJ project IML files.
|
||||
|
||||
Usage:
|
||||
node replace-lib-with-module.js --old-lib=LibName --new-module=intellij.libraries.libname [options]
|
||||
|
||||
Options:
|
||||
--old-lib=NAME Library name to replace (required)
|
||||
--new-module=NAME Target module name (required)
|
||||
--dry-run Show what would be changed without modifying files
|
||||
--exclude=DIR Additional directory to exclude (can be used multiple times, default: fleet, toolbox)
|
||||
--help Show this help message
|
||||
|
||||
Examples:
|
||||
# Dry run for Guava
|
||||
node replace-lib-with-module.js --old-lib=Guava --new-module=intellij.libraries.guava --dry-run
|
||||
|
||||
# Apply Guava replacement
|
||||
node replace-lib-with-module.js --old-lib=Guava --new-module=intellij.libraries.guava
|
||||
|
||||
# Replace another library with custom exclusions
|
||||
node replace-lib-with-module.js --old-lib=Jackson --new-module=intellij.libraries.jackson --exclude=custom-dir
|
||||
`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// Validate required arguments
|
||||
if (!config.oldLib || !config.newModule) {
|
||||
console.error('Error: Both --old-lib and --new-module are required arguments.\n');
|
||||
console.log('Usage: node replace-lib-with-module.js --old-lib=LibName --new-module=intellij.libraries.libname [options]');
|
||||
console.log('Run with --help for more information.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const PROJECT_ROOT = path.join(__dirname, '..', '..');
|
||||
const MODULES_XML = path.join(PROJECT_ROOT, '.idea', 'modules.xml');
|
||||
|
||||
console.log('Configuration:');
|
||||
console.log(` Old library: ${config.oldLib}`);
|
||||
console.log(` New module: ${config.newModule}`);
|
||||
console.log(` Excluded dirs: ${config.excludeDirs.join(', ')}`);
|
||||
console.log(` Dry run: ${config.dryRun}`);
|
||||
console.log('');
|
||||
|
||||
// Statistics
|
||||
const stats = {
|
||||
filesScanned: 0,
|
||||
filesModified: 0,
|
||||
filesSkipped: 0,
|
||||
replacements: {
|
||||
regular: 0,
|
||||
testScope: 0,
|
||||
testScopeExported: 0
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Extract IML file paths from modules.xml
|
||||
*/
|
||||
function extractImlPaths(modulesXmlPath) {
|
||||
const content = fs.readFileSync(modulesXmlPath, 'utf-8');
|
||||
const filepathRegex = /filepath="\$PROJECT_DIR\$\/([^"]+)"/g;
|
||||
const paths = [];
|
||||
let match;
|
||||
|
||||
while ((match = filepathRegex.exec(content)) !== null) {
|
||||
paths.push(match[1]);
|
||||
}
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if path should be excluded
|
||||
*/
|
||||
function shouldExclude(filePath) {
|
||||
for (const excludeDir of config.excludeDirs) {
|
||||
if (filePath.startsWith(excludeDir + '/') || filePath.includes('/' + excludeDir + '/')) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create replacement patterns based on library and module names
|
||||
*/
|
||||
function createReplacementPatterns(oldLib, newModule) {
|
||||
return [
|
||||
{
|
||||
name: 'regular',
|
||||
// <orderEntry type="library" name="Guava" level="project" />
|
||||
pattern: new RegExp(
|
||||
`<orderEntry type="library" name="${oldLib}" level="project" />`,
|
||||
'g'
|
||||
),
|
||||
replacement: `<orderEntry type="module" module-name="${newModule}" />`
|
||||
},
|
||||
{
|
||||
name: 'testScope',
|
||||
// <orderEntry type="library" scope="TEST" name="Guava" level="project" />
|
||||
pattern: new RegExp(
|
||||
`<orderEntry type="library" scope="TEST" name="${oldLib}" level="project" />`,
|
||||
'g'
|
||||
),
|
||||
replacement: `<orderEntry type="module" module-name="${newModule}" scope="TEST" />`
|
||||
},
|
||||
{
|
||||
name: 'testScopeExported',
|
||||
// <orderEntry type="library" exported="" scope="TEST" name="Guava" level="project" />
|
||||
pattern: new RegExp(
|
||||
`<orderEntry type="library" exported="" scope="TEST" name="${oldLib}" level="project" />`,
|
||||
'g'
|
||||
),
|
||||
replacement: `<orderEntry type="module" module-name="${newModule}" exported="" scope="TEST" />`
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a single IML file
|
||||
*/
|
||||
function processImlFile(filePath) {
|
||||
const absolutePath = path.join(PROJECT_ROOT, filePath);
|
||||
|
||||
if (!fs.existsSync(absolutePath)) {
|
||||
console.log(` ⚠ File not found: ${filePath}`);
|
||||
stats.filesSkipped++;
|
||||
return;
|
||||
}
|
||||
|
||||
let content = fs.readFileSync(absolutePath, 'utf-8');
|
||||
let modified = false;
|
||||
const fileReplacements = {};
|
||||
|
||||
const patterns = createReplacementPatterns(config.oldLib, config.newModule);
|
||||
|
||||
for (const { name, pattern, replacement } of patterns) {
|
||||
const matches = content.match(pattern);
|
||||
if (matches && matches.length > 0) {
|
||||
fileReplacements[name] = matches.length;
|
||||
stats.replacements[name] += matches.length;
|
||||
content = content.replace(pattern, replacement);
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
|
||||
stats.filesScanned++;
|
||||
|
||||
if (modified) {
|
||||
stats.filesModified++;
|
||||
|
||||
const replacementSummary = Object.entries(fileReplacements)
|
||||
.map(([type, count]) => `${count} ${type}`)
|
||||
.join(', ');
|
||||
|
||||
console.log(` ✓ ${filePath} (${replacementSummary})`);
|
||||
|
||||
if (!config.dryRun) {
|
||||
fs.writeFileSync(absolutePath, content, 'utf-8');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Main execution
|
||||
*/
|
||||
function main() {
|
||||
console.log('Reading modules from .idea/modules.xml...\n');
|
||||
|
||||
const imlPaths = extractImlPaths(MODULES_XML);
|
||||
console.log(`Found ${imlPaths.length} module files in modules.xml\n`);
|
||||
|
||||
console.log('Processing IML files:\n');
|
||||
|
||||
for (const imlPath of imlPaths) {
|
||||
if (shouldExclude(imlPath)) {
|
||||
stats.filesSkipped++;
|
||||
continue;
|
||||
}
|
||||
|
||||
processImlFile(imlPath);
|
||||
}
|
||||
|
||||
// Print summary
|
||||
console.log('\n' + '='.repeat(60));
|
||||
console.log('Summary:');
|
||||
console.log('='.repeat(60));
|
||||
console.log(`Files scanned: ${stats.filesScanned}`);
|
||||
console.log(`Files modified: ${stats.filesModified}`);
|
||||
console.log(`Files skipped: ${stats.filesSkipped}`);
|
||||
console.log('');
|
||||
console.log('Replacements by type:');
|
||||
console.log(` Regular scope: ${stats.replacements.regular}`);
|
||||
console.log(` TEST scope: ${stats.replacements.testScope}`);
|
||||
console.log(` TEST scope (exported): ${stats.replacements.testScopeExported}`);
|
||||
console.log(` Total: ${stats.replacements.regular + stats.replacements.testScope + stats.replacements.testScopeExported}`);
|
||||
|
||||
if (config.dryRun) {
|
||||
console.log('\n⚠ DRY RUN MODE - No files were modified');
|
||||
console.log('Run without --dry-run to apply changes');
|
||||
} else {
|
||||
console.log('\n✓ All changes applied successfully');
|
||||
}
|
||||
}
|
||||
|
||||
// Run the script
|
||||
try {
|
||||
main();
|
||||
} catch (error) {
|
||||
console.error('Error:', error.message);
|
||||
process.exit(1);
|
||||
}
|
||||
@@ -117,8 +117,6 @@ private val predefinedMergeRules = listOf<Pair<String, (String, FrontendModuleFi
|
||||
"groovy.jar" to { it, _ -> it.startsWith("org.codehaus.groovy:") },
|
||||
"jsch-agent.jar" to { it, _ -> it.startsWith("jsch-agent") },
|
||||
rdJarName to { it, _ -> it.startsWith("rd-") },
|
||||
// separate file to use in Gradle Daemon classpath
|
||||
"guava.jar" to { it, _ -> it == "Guava" },
|
||||
"opentelemetry.jar" to { it, _ -> it == "opentelemetry" || it == "opentelemetry-semconv" || it.startsWith("opentelemetry-exporter-otlp") },
|
||||
"bouncy-castle.jar" to { it, _ -> it.startsWith("bouncy-castle-") },
|
||||
PRODUCT_BACKEND_JAR to { name, filter -> (name.startsWith("License") || name.startsWith("jetbrains.codeWithMe.lobby.server.")) && filter.isBackendProjectLibrary(name) },
|
||||
|
||||
@@ -5,5 +5,7 @@
|
||||
|
||||
<module name="intellij.libraries.kotlinx.serialization.core" loading="embedded"/>
|
||||
<module name="intellij.libraries.kotlinx.serialization.json" loading="embedded"/>
|
||||
|
||||
<module name="intellij.libraries.guava" loading="embedded"/>
|
||||
</content>
|
||||
</idea-plugin>
|
||||
Reference in New Issue
Block a user