From c62fe6a520b277a7faafa327f2ec5c78edf06827 Mon Sep 17 00:00:00 2001 From: "Carlos (Goodwine)" <2022649+Goodwine@users.noreply.github.com> Date: Tue, 10 Oct 2023 11:18:31 -0700 Subject: [PATCH] Fix crash in browser when running alongside NextJS (#2114) * Fix crash in browser when running alongside NextJS Fixes https://github.com/sass/dart-sass/issues/2113 * update sass_api pubspec --- CHANGELOG.md | 7 +++ lib/src/async_compile.dart | 1 + lib/src/async_import_cache.dart | 1 + lib/src/compile.dart | 3 +- lib/src/deprecation.dart | 2 +- lib/src/import_cache.dart | 3 +- lib/src/io/interface.dart | 9 ---- lib/src/io/js.dart | 69 +++++++++++++---------------- lib/src/io/vm.dart | 6 --- lib/src/js/compile.dart | 4 +- lib/src/js/legacy.dart | 4 +- lib/src/value/color.dart | 2 +- lib/src/visitor/async_evaluate.dart | 2 +- lib/src/visitor/evaluate.dart | 4 +- pkg/sass_api/CHANGELOG.md | 4 ++ pkg/sass_api/pubspec.yaml | 4 +- pubspec.yaml | 4 +- test/ensure_npm_package.dart | 3 +- 18 files changed, 62 insertions(+), 70 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a9b65948..5ff0ed357 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## 1.69.2 + +### JS API + +* Fix a bug where Sass crashed when running in the browser if there was a global + variable named `process`. + ## 1.69.1 * No user-visible changes. diff --git a/lib/src/async_compile.dart b/lib/src/async_compile.dart index 0d95a5dd7..daa2233db 100644 --- a/lib/src/async_compile.dart +++ b/lib/src/async_compile.dart @@ -4,6 +4,7 @@ import 'dart:convert'; +import 'package:cli_pkg/js.dart'; import 'package:path/path.dart' as p; import 'ast/sass.dart'; diff --git a/lib/src/async_import_cache.dart b/lib/src/async_import_cache.dart index 33fc26cce..9b08e5597 100644 --- a/lib/src/async_import_cache.dart +++ b/lib/src/async_import_cache.dart @@ -2,6 +2,7 @@ // MIT-style license that can be found in the LICENSE file or at // https://opensource.org/licenses/MIT. +import 'package:cli_pkg/js.dart'; import 'package:collection/collection.dart'; import 'package:meta/meta.dart'; import 'package:package_config/package_config_types.dart'; diff --git a/lib/src/compile.dart b/lib/src/compile.dart index 2b24def1b..b951c8036 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: c2982db43bcd56f81cab3f51b5669e0edd3cfafb +// Checksum: 5178e366228bde7854df12221393857bb3022628 // // ignore_for_file: unused_import @@ -13,6 +13,7 @@ export 'async_compile.dart'; import 'dart:convert'; +import 'package:cli_pkg/js.dart'; import 'package:path/path.dart' as p; import 'ast/sass.dart'; diff --git a/lib/src/deprecation.dart b/lib/src/deprecation.dart index 687e0ac43..2724d0afe 100644 --- a/lib/src/deprecation.dart +++ b/lib/src/deprecation.dart @@ -2,10 +2,10 @@ // MIT-style license that can be found in the LICENSE file or at // https://opensource.org/licenses/MIT. +import 'package:cli_pkg/js.dart'; import 'package:collection/collection.dart'; import 'package:pub_semver/pub_semver.dart'; -import 'io.dart'; import 'util/nullable.dart'; /// A deprecated feature in the language. diff --git a/lib/src/import_cache.dart b/lib/src/import_cache.dart index bc526d7a3..397e676aa 100644 --- a/lib/src/import_cache.dart +++ b/lib/src/import_cache.dart @@ -5,10 +5,11 @@ // DO NOT EDIT. This file was generated from async_import_cache.dart. // See tool/grind/synchronize.dart for details. // -// Checksum: ff52307a3bc93358ddc46f1e76120894fa3e071f +// Checksum: 342e907cf10e1dd80d7045fc32db43c74376654e // // ignore_for_file: unused_import +import 'package:cli_pkg/js.dart'; import 'package:collection/collection.dart'; import 'package:meta/meta.dart'; import 'package:package_config/package_config_types.dart'; diff --git a/lib/src/io/interface.dart b/lib/src/io/interface.dart index be0ec574c..29182eb45 100644 --- a/lib/src/io/interface.dart +++ b/lib/src/io/interface.dart @@ -19,15 +19,6 @@ bool get isMacOS => throw ''; /// Returns whether or not stdout is connected to an interactive terminal. bool get hasTerminal => throw ''; -/// Whether we're running as JS (browser or Node.js). -const bool isJS = false; - -/// Whether we're running as Node.js (not browser or Dart VM). -bool get isNode => throw ''; - -/// Whether we're running as browser (not Node.js or Dart VM). -bool get isBrowser => throw ''; - /// Whether this process is connected to a terminal that supports ANSI escape /// sequences. bool get supportsAnsiEscapes => throw ''; diff --git a/lib/src/io/js.dart b/lib/src/io/js.dart index c806dc181..94e4f1a13 100644 --- a/lib/src/io/js.dart +++ b/lib/src/io/js.dart @@ -4,11 +4,12 @@ import 'dart:async'; import 'dart:convert'; -import 'dart:js_util'; +import 'package:cli_pkg/js.dart'; import 'package:js/js.dart'; import 'package:node_interop/fs.dart'; import 'package:node_interop/node_interop.dart' hide process; +import 'package:node_interop/util.dart'; import 'package:path/path.dart' as p; import 'package:source_span/source_span.dart'; import 'package:watcher/watcher.dart'; @@ -17,7 +18,12 @@ import '../exception.dart'; import '../js/chokidar.dart'; @JS('process') -external final Process? process; // process is null in the browser +external final Process? _nodeJsProcess; // process is null in the browser + +/// The Node.JS [Process] global variable. +/// +/// This value is `null` when running the script is not run from Node.JS +Process? get _process => isNodeJs ? _nodeJsProcess : null; class FileSystemException { final String message; @@ -29,7 +35,7 @@ class FileSystemException { } void safePrint(Object? message) { - if (process case var process?) { + if (_process case var process?) { process.stdout.write("${message ?? ''}\n"); } else { console.log(message ?? ''); @@ -37,7 +43,7 @@ void safePrint(Object? message) { } void printError(Object? message) { - if (process case var process?) { + if (_process case var process?) { process.stderr.write("${message ?? ''}\n"); } else { console.error(message ?? ''); @@ -45,7 +51,7 @@ void printError(Object? message) { } String readFile(String path) { - if (!isNode) { + if (!isNodeJs) { throw UnsupportedError("readFile() is only supported on Node.js"); } // TODO(nweiz): explicitly decode the bytes as UTF-8 like we do in the VM when @@ -69,7 +75,7 @@ Object? _readFile(String path, [String? encoding]) => _systemErrorToFileSystemException(() => fs.readFileSync(path, encoding)); void writeFile(String path, String contents) { - if (!isNode) { + if (!isNodeJs) { throw UnsupportedError("writeFile() is only supported on Node.js"); } return _systemErrorToFileSystemException( @@ -77,15 +83,15 @@ void writeFile(String path, String contents) { } void deleteFile(String path) { - if (!isNode) { + if (!isNodeJs) { throw UnsupportedError("deleteFile() is only supported on Node.js"); } return _systemErrorToFileSystemException(() => fs.unlinkSync(path)); } Future readStdin() async { - var process_ = process; - if (process_ == null) { + var process = _process; + if (process == null) { throw UnsupportedError("readStdin() is only supported on Node.js"); } var completer = Completer(); @@ -96,15 +102,15 @@ Future readStdin() async { }); // Node defaults all buffers to 'utf8'. var sink = utf8.decoder.startChunkedConversion(innerSink); - process_.stdin.on('data', allowInterop(([Object? chunk]) { + process.stdin.on('data', allowInterop(([Object? chunk]) { sink.add(chunk as List); })); - process_.stdin.on('end', allowInterop(([Object? _]) { + process.stdin.on('end', allowInterop(([Object? _]) { // Callback for 'end' receives no args. assert(_ == null); sink.close(); })); - process_.stdin.on('error', allowInterop(([Object? e]) { + process.stdin.on('error', allowInterop(([Object? e]) { printError('Failed to read from stdin'); printError(e); completer.completeError(e!); @@ -121,7 +127,7 @@ String _cleanErrorMessage(JsSystemError error) { } bool fileExists(String path) { - if (!isNode) { + if (!isNodeJs) { throw UnsupportedError("fileExists() is only supported on Node.js"); } return _systemErrorToFileSystemException(() { @@ -142,7 +148,7 @@ bool fileExists(String path) { } bool dirExists(String path) { - if (!isNode) { + if (!isNodeJs) { throw UnsupportedError("dirExists() is only supported on Node.js"); } return _systemErrorToFileSystemException(() { @@ -163,7 +169,7 @@ bool dirExists(String path) { } void ensureDir(String path) { - if (!isNode) { + if (!isNodeJs) { throw UnsupportedError("ensureDir() is only supported on Node.js"); } return _systemErrorToFileSystemException(() { @@ -180,7 +186,7 @@ void ensureDir(String path) { } Iterable listDir(String path, {bool recursive = false}) { - if (!isNode) { + if (!isNodeJs) { throw UnsupportedError("listDir() is only supported on Node.js"); } return _systemErrorToFileSystemException(() { @@ -202,7 +208,7 @@ Iterable listDir(String path, {bool recursive = false}) { } DateTime modificationTime(String path) { - if (!isNode) { + if (!isNodeJs) { throw UnsupportedError("modificationTime() is only supported on Node.js"); } return _systemErrorToFileSystemException(() => @@ -210,7 +216,7 @@ DateTime modificationTime(String path) { } String? getEnvironmentVariable(String name) { - var env = process?.env; + var env = _process?.env; return env == null ? null : getProperty(env as Object, name) as String?; } @@ -229,36 +235,21 @@ T _systemErrorToFileSystemException(T callback()) { /// from `node_interop` declares `isTTY` as always non-nullably available, but /// in practice it's undefined if stdout isn't a TTY. /// See: https://github.com/pulyaevskiy/node-interop/issues/93 -bool get hasTerminal => process?.stdout.isTTY == true; - -bool get isWindows => process?.platform == 'win32'; - -bool get isMacOS => process?.platform == 'darwin'; - -const bool isJS = true; - -/// The fs module object, used to check whether this has been loaded as Node. -/// -/// It's safest to check for a library we load in manually rather than one -/// that's ambiently available so that we don't get into a weird state in -/// environments like VS Code that support some Node.js libraries but don't load -/// Node.js entrypoints for dependencies. -@JS('fs') -external final Object? _fsNullable; +bool get hasTerminal => _process?.stdout.isTTY == true; -bool get isNode => _fsNullable != null; +bool get isWindows => _process?.platform == 'win32'; -bool get isBrowser => isJS && !isNode; +bool get isMacOS => _process?.platform == 'darwin'; // Node seems to support ANSI escapes on all terminals. bool get supportsAnsiEscapes => hasTerminal; -int get exitCode => process?.exitCode ?? 0; +int get exitCode => _process?.exitCode ?? 0; -set exitCode(int code) => process?.exitCode = code; +set exitCode(int code) => _process?.exitCode = code; Future> watchDir(String path, {bool poll = false}) { - if (!isNode) { + if (!isNodeJs) { throw UnsupportedError("watchDir() is only supported on Node.js"); } var watcher = chokidar.watch( diff --git a/lib/src/io/vm.dart b/lib/src/io/vm.dart index cba88e40f..1d5c7b561 100644 --- a/lib/src/io/vm.dart +++ b/lib/src/io/vm.dart @@ -22,12 +22,6 @@ bool get isMacOS => io.Platform.isMacOS; bool get hasTerminal => io.stdout.hasTerminal; -const bool isJS = false; - -bool get isNode => false; - -bool get isBrowser => false; - bool get supportsAnsiEscapes { if (!hasTerminal) return false; diff --git a/lib/src/js/compile.dart b/lib/src/js/compile.dart index 0e734dc77..b0a192a9e 100644 --- a/lib/src/js/compile.dart +++ b/lib/src/js/compile.dart @@ -27,7 +27,7 @@ import 'utils.dart'; /// See https://github.com/sass/sass/spec/tree/main/js-api/compile.d.ts for /// details. NodeCompileResult compile(String path, [CompileOptions? options]) { - if (!isNode) { + if (!isNodeJs) { jsThrow(JsError("The compile() method is only available in Node.js.")); } var color = options?.alertColor ?? hasTerminal; @@ -88,7 +88,7 @@ NodeCompileResult compileString(String text, [CompileStringOptions? options]) { /// See https://github.com/sass/sass/spec/tree/main/js-api/compile.d.ts for /// details. Promise compileAsync(String path, [CompileOptions? options]) { - if (!isNode) { + if (!isNodeJs) { jsThrow(JsError("The compileAsync() method is only available in Node.js.")); } var color = options?.alertColor ?? hasTerminal; diff --git a/lib/src/js/legacy.dart b/lib/src/js/legacy.dart index 66875da51..5c5ad533a 100644 --- a/lib/src/js/legacy.dart +++ b/lib/src/js/legacy.dart @@ -39,7 +39,7 @@ import 'utils.dart'; /// [render]: https://github.com/sass/node-sass#options void render( RenderOptions options, void callback(Object? error, RenderResult? result)) { - if (!isNode) { + if (!isNodeJs) { jsThrow(JsError("The render() method is only available in Node.js.")); } if (options.fiber case var fiber?) { @@ -118,7 +118,7 @@ Future _renderAsync(RenderOptions options) async { /// /// [render]: https://github.com/sass/node-sass#options RenderResult renderSync(RenderOptions options) { - if (!isNode) { + if (!isNodeJs) { jsThrow(JsError("The renderSync() method is only available in Node.js.")); } try { diff --git a/lib/src/value/color.dart b/lib/src/value/color.dart index 794f236fd..8df328564 100644 --- a/lib/src/value/color.dart +++ b/lib/src/value/color.dart @@ -4,13 +4,13 @@ import 'dart:math' as math; +import 'package:cli_pkg/js.dart'; import 'package:meta/meta.dart'; import 'package:source_span/source_span.dart'; import '../deprecation.dart'; import '../evaluation_context.dart'; import '../exception.dart'; -import '../io.dart'; import '../util/number.dart'; import '../value.dart'; import '../visitor/interface/value.dart'; diff --git a/lib/src/visitor/async_evaluate.dart b/lib/src/visitor/async_evaluate.dart index 389c6991a..767d2393b 100644 --- a/lib/src/visitor/async_evaluate.dart +++ b/lib/src/visitor/async_evaluate.dart @@ -6,6 +6,7 @@ import 'dart:async'; import 'dart:collection'; import 'dart:math' as math; +import 'package:cli_pkg/js.dart'; import 'package:charcode/charcode.dart'; import 'package:collection/collection.dart'; import 'package:path/path.dart' as p; @@ -33,7 +34,6 @@ import '../functions/meta.dart' as meta; import '../importer.dart'; import '../importer/legacy_node.dart'; import '../interpolation_map.dart'; -import '../io.dart'; import '../logger.dart'; import '../module.dart'; import '../module/built_in.dart'; diff --git a/lib/src/visitor/evaluate.dart b/lib/src/visitor/evaluate.dart index 0aaecec85..50b2cce78 100644 --- a/lib/src/visitor/evaluate.dart +++ b/lib/src/visitor/evaluate.dart @@ -5,7 +5,7 @@ // DO NOT EDIT. This file was generated from async_evaluate.dart. // See tool/grind/synchronize.dart for details. // -// Checksum: 358960b72c6e4f48d3e2e9d52be3abbe9e8b5a9f +// Checksum: 58ef9912c6a9d9cfe9c3f5d991f625ab1a627e7a // // ignore_for_file: unused_import @@ -15,6 +15,7 @@ export 'async_evaluate.dart' show EvaluateResult; import 'dart:collection'; import 'dart:math' as math; +import 'package:cli_pkg/js.dart'; import 'package:charcode/charcode.dart'; import 'package:collection/collection.dart'; import 'package:path/path.dart' as p; @@ -42,7 +43,6 @@ import '../functions/meta.dart' as meta; import '../importer.dart'; import '../importer/legacy_node.dart'; import '../interpolation_map.dart'; -import '../io.dart'; import '../logger.dart'; import '../module.dart'; import '../module/built_in.dart'; diff --git a/pkg/sass_api/CHANGELOG.md b/pkg/sass_api/CHANGELOG.md index f685c4bb4..58fae4f02 100644 --- a/pkg/sass_api/CHANGELOG.md +++ b/pkg/sass_api/CHANGELOG.md @@ -1,3 +1,7 @@ +## 9.2.2 + +* No user-visible changes. + ## 9.2.1 * No user-visible changes. diff --git a/pkg/sass_api/pubspec.yaml b/pkg/sass_api/pubspec.yaml index 89ae60992..3b1dda241 100644 --- a/pkg/sass_api/pubspec.yaml +++ b/pkg/sass_api/pubspec.yaml @@ -2,7 +2,7 @@ name: sass_api # Note: Every time we add a new Sass AST node, we need to bump the *major* # version because it's a breaking change for anyone who's implementing the # visitor interface(s). -version: 9.2.1 +version: 9.2.2 description: Additional APIs for Dart Sass. homepage: https://github.com/sass/dart-sass @@ -10,7 +10,7 @@ environment: sdk: ">=3.0.0 <4.0.0" dependencies: - sass: 1.69.1 + sass: 1.69.2 dev_dependencies: dartdoc: ^6.0.0 diff --git a/pubspec.yaml b/pubspec.yaml index 5ff89e2a9..123585e64 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: sass -version: 1.69.1 +version: 1.69.2 description: A Sass implementation in Dart. homepage: https://github.com/sass/dart-sass @@ -14,7 +14,7 @@ dependencies: args: ^2.0.0 async: ^2.5.0 charcode: ^1.2.0 - cli_pkg: ^2.5.0 + cli_pkg: ^2.7.0 cli_repl: ^0.2.1 collection: ^1.16.0 http: "^1.1.0" diff --git a/test/ensure_npm_package.dart b/test/ensure_npm_package.dart index 5e73c3c54..d31041e1a 100644 --- a/test/ensure_npm_package.dart +++ b/test/ensure_npm_package.dart @@ -4,6 +4,7 @@ import 'package:test/test.dart'; +import 'package:cli_pkg/js.dart'; import 'package:sass/src/io.dart'; /// Ensures that the NPM package is compiled and up-to-date. @@ -12,7 +13,7 @@ import 'package:sass/src/io.dart'; Future ensureNpmPackage() async { // spawnHybridUri() doesn't currently work on Windows and Node due to busted // path handling in the SDK. - if (isNode && isWindows) return; + if (isNodeJs && isWindows) return; var channel = spawnHybridCode(""" import 'package:cli_pkg/testing.dart' as pkg;