Preserve windows line endings in pubspec.lock if they are already there (#2489)
diff --git a/lib/src/entrypoint.dart b/lib/src/entrypoint.dart
index 7cff07c..04a7ead 100644
--- a/lib/src/entrypoint.dart
+++ b/lib/src/entrypoint.dart
@@ -7,6 +7,7 @@
import 'dart:io';
import 'package:collection/collection.dart';
+import 'package:meta/meta.dart';
// ignore: deprecated_member_use
import 'package:package_config/packages_file.dart' as packages_file;
import 'package:path/path.dart' as p;
@@ -743,10 +744,18 @@
}
/// Saves a list of concrete package versions to the `pubspec.lock` file.
+ ///
+ /// Will use Windows line endings (`\r\n`) if a `pubspec.lock` exists, and
+ /// uses that.
void _saveLockFile(SolveResult result) {
_lockFile = result.lockFile;
- var lockFilePath = root.path('pubspec.lock');
- writeTextFile(lockFilePath, _lockFile.serialize(root.dir));
+
+ final windowsLineEndings = fileExists(lockFilePath) &&
+ detectWindowsLineEndings(readTextFile(lockFilePath));
+
+ final serialized = _lockFile.serialize(root.dir);
+ writeTextFile(lockFilePath,
+ windowsLineEndings ? serialized.replaceAll('\n', '\r\n') : serialized);
}
/// If the entrypoint uses the old-style `.pub` cache directory, migrates it
@@ -766,3 +775,22 @@
renameDir(oldPath, newPath);
}
}
+
+/// Returns `true` if the [text] looks like it uses windows line endings.
+///
+/// The heuristic used is to count all `\n` in the text and if stricly more than
+/// half of them are preceded by `\r` we report `true`.
+@visibleForTesting
+bool detectWindowsLineEndings(String text) {
+ var index = -1;
+ var unixNewlines = 0;
+ var windowsNewlines = 0;
+ while ((index = text.indexOf('\n', index + 1)) != -1) {
+ if (index != 0 && text[index - 1] == '\r') {
+ windowsNewlines++;
+ } else {
+ unixNewlines++;
+ }
+ }
+ return windowsNewlines > unixNewlines;
+}
diff --git a/test/get/preserve_lock_file_line_endings_test.dart b/test/get/preserve_lock_file_line_endings_test.dart
new file mode 100644
index 0000000..15502a6
--- /dev/null
+++ b/test/get/preserve_lock_file_line_endings_test.dart
@@ -0,0 +1,74 @@
+// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:pub/src/entrypoint.dart';
+import 'package:test/test.dart';
+import 'package:path/path.dart' as path;
+
+import '../descriptor.dart' as d;
+import '../test_pub.dart';
+
+Future<void> main() async {
+ test('pub get creates lock file with unix line endings if none exist',
+ () async {
+ await d.appDir().create();
+
+ await pubGet();
+
+ await d
+ .file(path.join(appPath, 'pubspec.lock'),
+ allOf(contains('\n'), isNot(contains('\r\n'))))
+ .validate();
+ });
+
+ test('pub get preserves line endings of lock file', () async {
+ await d.appDir().create();
+
+ await pubGet();
+
+ final lockFile = d.file(path.join(appPath, 'pubspec.lock')).io;
+ lockFile.writeAsStringSync(
+ lockFile.readAsStringSync().replaceAll('\n', '\r\n'));
+ await d.dir(appPath, [d.file('pubspec.lock', contains('\r\n'))]).validate();
+
+ await pubGet();
+
+ await d.dir(appPath, [d.file('pubspec.lock', contains('\r\n'))]).validate();
+ });
+
+ test('windows line endings detection', () {
+ expect(detectWindowsLineEndings(''), false);
+ expect(detectWindowsLineEndings('\n'), false);
+ expect(detectWindowsLineEndings('\r'), false);
+ expect(detectWindowsLineEndings('\r\n'), true);
+ expect(detectWindowsLineEndings('\n\r\n'), false);
+ expect(detectWindowsLineEndings('\r\n\n'), false);
+ expect(detectWindowsLineEndings('\r\n\r\n'), true);
+ expect(detectWindowsLineEndings('\n\n'), false);
+ expect(detectWindowsLineEndings('abcd\n'), false);
+ expect(detectWindowsLineEndings('abcd\nefg'), false);
+ expect(detectWindowsLineEndings('abcd\nefg\n'), false);
+ expect(detectWindowsLineEndings('\r\n'), true);
+ expect(detectWindowsLineEndings('abcd\r\nefg\n'), false);
+ expect(detectWindowsLineEndings('abcd\r\nefg\nhij\r\n'), true);
+ expect(detectWindowsLineEndings('''
+packages:\r
+ bar:\r
+ dependency: transitive\r
+ description: "bar desc"\r
+ source: fake\r
+ version: "1.2.3"\r
+sdks: {}\r
+'''), true);
+ expect(detectWindowsLineEndings('''
+packages:
+ bar:
+ dependency: transitive
+ description: "bar desc"
+ source: fake
+ version: "1.2.3"
+sdks: {}
+'''), false);
+ });
+}