diff --git a/CHANGELOG.md b/CHANGELOG.md index f9684679..7ac4740d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 1.30.1 + +* Properly load files in case-sensitive Windows directories with upper-case + names. + ## 1.30.0 * Fix a bug where `@at-root (without: all)` wouldn't properly remove a diff --git a/lib/src/async_compile.dart b/lib/src/async_compile.dart index cc354a8c..b1996b64 100644 --- a/lib/src/async_compile.dart +++ b/lib/src/async_compile.dart @@ -44,7 +44,7 @@ Future compileAsync(String path, (syntax == null || syntax == Syntax.forPath(path))) { importCache ??= AsyncImportCache.none(logger: logger); stylesheet = await importCache.importCanonical( - FilesystemImporter('.'), p.toUri(p.canonicalize(path)), p.toUri(path)); + FilesystemImporter('.'), p.toUri(canonicalize(path)), p.toUri(path)); } else { stylesheet = Stylesheet.parse( readFile(path), syntax ?? Syntax.forPath(path), diff --git a/lib/src/compile.dart b/lib/src/compile.dart index 0a0ecd5e..d2b49565 100644 --- a/lib/src/compile.dart +++ b/lib/src/compile.dart @@ -5,7 +5,7 @@ // DO NOT EDIT. This file was generated from async_compile.dart. // See tool/grind/synchronize.dart for details. // -// Checksum: bca3a79dd4a5c3905b07003b123172f3c876d2de +// Checksum: b2cd6037efa37e300daa45ebed20cb4b61526161 // // ignore_for_file: unused_import @@ -54,7 +54,7 @@ CompileResult compile(String path, (syntax == null || syntax == Syntax.forPath(path))) { importCache ??= ImportCache.none(logger: logger); stylesheet = importCache.importCanonical( - FilesystemImporter('.'), p.toUri(p.canonicalize(path)), p.toUri(path)); + FilesystemImporter('.'), p.toUri(canonicalize(path)), p.toUri(path)); } else { stylesheet = Stylesheet.parse( readFile(path), syntax ?? Syntax.forPath(path), diff --git a/lib/src/executable/watch.dart b/lib/src/executable/watch.dart index 0c9fe13a..3d06ae14 100644 --- a/lib/src/executable/watch.dart +++ b/lib/src/executable/watch.dart @@ -41,8 +41,8 @@ Future watch(ExecutableOptions options, StylesheetGraph graph) async { var watcher = _Watcher(options, graph); for (var source in options.sourcesToDestinations.keys) { var destination = options.sourcesToDestinations[source]; - graph.addCanonical(FilesystemImporter('.'), p.toUri(p.canonicalize(source)), - p.toUri(source), + graph.addCanonical( + FilesystemImporter('.'), p.toUri(canonicalize(source)), p.toUri(source), recanonicalize: false); var success = await watcher.compile(source, destination, ifModified: true); if (!success && options.stopOnError) { @@ -188,7 +188,7 @@ class _Watcher { } /// Returns the canonical URL for the stylesheet path [path]. - Uri _canonicalize(String path) => p.toUri(p.canonicalize(path)); + Uri _canonicalize(String path) => p.toUri(canonicalize(path)); /// Combine [WatchEvent]s that happen in quick succession. /// diff --git a/lib/src/importer/filesystem.dart b/lib/src/importer/filesystem.dart index ad8dc822..ef659231 100644 --- a/lib/src/importer/filesystem.dart +++ b/lib/src/importer/filesystem.dart @@ -23,18 +23,13 @@ class FilesystemImporter extends Importer { Uri canonicalize(Uri url) { if (url.scheme != 'file' && url.scheme != '') return null; var resolved = resolveImportPath(p.join(_loadPath, p.fromUri(url))); - return resolved == null ? null : p.toUri(p.canonicalize(resolved)); + return resolved == null ? null : p.toUri(io.canonicalize(resolved)); } ImporterResult load(Uri url) { var path = p.fromUri(url); return ImporterResult(io.readFile(path), - sourceMapUrl: - // [io.realCasePath] will short-circuit on case-sensitive - // filesystems anyway, but we still avoid calling it here so we - // don't have to re-parse the URL. - io.couldBeCaseInsensitive ? p.toUri(io.realCasePath(path)) : url, - syntax: Syntax.forPath(path)); + sourceMapUrl: url, syntax: Syntax.forPath(path)); } DateTime modificationTime(Uri url) => io.modificationTime(p.fromUri(url)); diff --git a/lib/src/io.dart b/lib/src/io.dart index aba160a5..3187b0e3 100644 --- a/lib/src/io.dart +++ b/lib/src/io.dart @@ -17,26 +17,36 @@ export 'io/interface.dart' /// /// We can't know for sure because different Mac OS systems are configured /// differently. -bool get couldBeCaseInsensitive => isWindows || isMacOS; +bool get _couldBeCaseInsensitive => isWindows || isMacOS; + +/// Returns the canonical form of `path` on disk. +String canonicalize(String path) => _couldBeCaseInsensitive + ? _realCasePath(p.absolute(p.normalize(path))) + : p.canonicalize(path); /// Returns `path` with the case updated to match the path's case on disk. /// /// This only updates `path`'s basename. It always returns `path` as-is on -/// operating systems other than Windows or Mac OS, since they almost never uses +/// operating systems other than Windows or Mac OS, since they almost never use /// case-insensitive filesystems. -String realCasePath(String path) { +String _realCasePath(String path) { // TODO(nweiz): Use an SDK function for this when dart-lang/sdk#35370 and/or - // nodejs/node#24942 are fixed. + // nodejs/node#24942 are fixed, or at least use FFI functions. - if (!couldBeCaseInsensitive) return path; + if (!_couldBeCaseInsensitive) return path; - var basename = p.basename(path); - var matches = listDir(p.dirname(path)) - .where((realPath) => equalsIgnoreCase(p.basename(realPath), basename)) - .toList(); + var realCasePath = p.rootPrefix(path); + for (var component in p.split(path.substring(realCasePath.length))) { + var matches = listDir(realCasePath) + .where((realPath) => equalsIgnoreCase(p.basename(realPath), component)) + .toList(); - // If the file doesn't exist, or if there are multiple options (meaning the - // filesystem isn't actually case-insensitive), return `path` as-is. - if (matches.length != 1) return path; - return matches.first; + realCasePath = matches.length != 1 + // If the file doesn't exist, or if there are multiple options (meaning + // the filesystem isn't actually case-insensitive), use `component` as-is. + ? p.join(realCasePath, component) + : matches[0]; + } + + return realCasePath; } diff --git a/pubspec.yaml b/pubspec.yaml index 2d589699..e8e0582f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: sass -version: 1.30.0 +version: 1.30.1-dev description: A Sass implementation in Dart. author: Sass Team homepage: https://github.com/sass/dart-sass diff --git a/test/cli/shared/source_maps.dart b/test/cli/shared/source_maps.dart index 36e4b0b2..2df6c58a 100644 --- a/test/cli/shared/source_maps.dart +++ b/test/cli/shared/source_maps.dart @@ -73,8 +73,8 @@ void sharedTests(Future runSass(Iterable arguments)) { expect( _readJson("out.css.map"), containsPair("sources", [ - p.toUri(p.canonicalize(d.path("dir/other.scss"))).toString(), - p.toUri(p.canonicalize(d.path("test.scss"))).toString() + p.toUri(canonicalize(d.path("dir/other.scss"))).toString(), + p.toUri(canonicalize(d.path("test.scss"))).toString() ])); });