diff --git a/packages/melos/lib/src/common/intellij_project.dart b/packages/melos/lib/src/common/intellij_project.dart
index 9c1e575e7..1ce217ca8 100644
--- a/packages/melos/lib/src/common/intellij_project.dart
+++ b/packages/melos/lib/src/common/intellij_project.dart
@@ -1,6 +1,7 @@
import 'dart:io';
import 'package:path/path.dart' as p;
+import 'package:xml/xml.dart' as xml;
import '../common/utils.dart' as utils;
import '../package.dart';
@@ -133,19 +134,27 @@ class IntellijProject {
return template;
}
- String ideaModuleStringForName(String moduleName, {String? relativePath}) {
+ xml.XmlElement ideaModuleElementForName(
+ String moduleName, {
+ String? relativePath,
+ }) {
var imlPath = relativePath != null
? p.normalize('$relativePath/$moduleName.iml')
: '$moduleName.iml';
+
// Use `/` instead of `\` no matter what platform is.
imlPath = imlPath.replaceAll(r'\', '/');
- final module =
- '';
- // Pad to preserve formatting on generated file.
- return module.padLeft(6);
+
+ return xml.XmlElement(
+ xml.XmlName('module'),
+ [
+ xml.XmlAttribute(
+ xml.XmlName('fileurl'),
+ 'file://\$PROJECT_DIR\$/$imlPath',
+ ),
+ xml.XmlAttribute(xml.XmlName('filepath'), '\$PROJECT_DIR\$/$imlPath'),
+ ],
+ );
}
Future forceWriteToFile(String filePath, String fileContents) async {
@@ -212,22 +221,70 @@ class IntellijProject {
);
}
+ Future> mergeExistingModules(
+ List melosModules,
+ ) async {
+ if (!fileExists(pathModulesXml)) return melosModules;
+
+ final text = await readTextFileAsync(pathModulesXml);
+ try {
+ final doc = xml.XmlDocument.parse(text);
+
+ String createNormalizedKey(xml.XmlElement e) {
+ final v =
+ e.getAttribute('filepath') ??
+ e.getAttribute('filePath') ??
+ e.getAttribute('fileurl') ??
+ e.getAttribute('fileUrl');
+ return (v ?? '')
+ .trim()
+ .replaceAll(r'\', '/')
+ .replaceFirst(
+ RegExp(r'^file://\$PROJECT_DIR\$/', caseSensitive: false),
+ '',
+ )
+ .replaceFirst(
+ RegExp(r'^\$PROJECT_DIR\$/', caseSensitive: false),
+ '',
+ );
+ }
+
+ final seen = melosModules
+ .map(createNormalizedKey)
+ .where((s) => s.isNotEmpty)
+ .toSet();
+
+ final extras = doc
+ .findAllElements('module')
+ .where(
+ (m) => seen.add(createNormalizedKey(m)),
+ )
+ .map((m) => m.copy());
+
+ return [...melosModules.map((e) => e.copy()), ...extras];
+ } catch (_) {
+ return melosModules;
+ }
+ }
+
Future writeModulesXml() async {
- final ideaModules = [];
+ final ideaModules = [];
+
for (final package in _workspace.filteredPackages.values) {
ideaModules.add(
- ideaModuleStringForName(
+ ideaModuleElementForName(
packageModuleName(package),
relativePath: package.pathRelativeToWorkspace,
),
);
}
- ideaModules.add(ideaModuleStringForName(workspaceModuleName));
+ ideaModules.add(ideaModuleElementForName(workspaceModuleName));
+ final mergedModules = await mergeExistingModules(ideaModules);
final ideaModulesXmlTemplate = await readFileTemplate('modules.xml');
final generatedModulesXml = injectTemplateVariable(
template: ideaModulesXmlTemplate,
variableName: 'modules',
- variableValue: ideaModules.join('\n'),
+ variableValue: mergedModules.join('\n'),
);
return forceWriteToFile(pathModulesXml, generatedModulesXml);
}
diff --git a/packages/melos/pubspec.yaml b/packages/melos/pubspec.yaml
index 742944451..142c8f3f1 100644
--- a/packages/melos/pubspec.yaml
+++ b/packages/melos/pubspec.yaml
@@ -44,6 +44,7 @@ dependencies:
pub_updater: ^0.5.0
pubspec_parse: ^1.5.0
string_scanner: ^1.4.1
+ xml: ^6.6.1
yaml: ^3.1.3
yaml_edit: ^2.2.2