content: // Copyright 2021 Google Inc. Use of this source code is governed by an
  // MIT-style license that can be found in the LICENSE file or at
  // https://opensource.org/licenses/MIT.
  
  import 'package:js/js.dart';
  import 'package:node_interop/node_interop.dart';
  import 'package:term_glyph/term_glyph.dart' as glyph;
  
  import '../exception.dart';
  import '../utils.dart';
  import 'reflection.dart';
  import 'utils.dart';
  
  @JS()
  @anonymous
  class _NodeException extends JsError {
    // Fake constructor to silence the no_generative_constructor_in_superclass
    // error.
    external factory _NodeException();
  
    external SassException get _dartException;
  }
  
  /// Sass's JS API exception class.
  final JSClass exceptionClass = () {
    // There's no way to define this in pure Dart, because the only way to create
    // a subclass of the JS `Error` type that sets its internal `[[ErrorData]]`
    // field is to call `super()` with the ES6 class syntax.
    var jsClass = jsEval(r'''
      return class Exception extends Error {
        constructor(dartException, message) {
          super(message);
  
          // Define this as non-enumerable so that it doesn't show up when the
          // exception hits the top level.
          Object.defineProperty(this, '_dartException', {
            value: dartException,
            enumerable: false
          });
        }
  
        toString() {
          return this.message;
        }
      }
    ''') as JSClass;
    jsClass.setName('sass.Exception');
  
    jsClass.defineGetters({
      'sassMessage': (_NodeException exception) =>
          exception._dartException.message,
      'sassStack': (_NodeException exception) =>
          exception._dartException.trace.toString(),
      'span': (_NodeException exception) => exception._dartException.span,
    });
  
    return jsClass;
  }();
  
  /// Wraps [exception] with a Node API exception and throws it.
  ///
  /// If [color] is `true`, the thrown exception uses colors in its
  /// stringification.
  ///
  /// If [ascii] is `false`, the thrown exception uses non-ASCII characters in its
  /// stringification.
  ///
  /// If [trace] is passed, it's used as the stack trace for the JS exception.
  Never throwNodeException(
    SassException exception, {
    required bool color,
    required bool ascii,
    StackTrace? trace,
  }) {
    var wasAscii = glyph.ascii;
    glyph.ascii = ascii;
    try {
      var jsException = exceptionClass.construct([
        exception,
        exception.toString(color: color).replaceFirst('Error: ', ''),
      ]) as _NodeException;
      trace = getTrace(exception) ?? trace;
      if (trace != null) attachJsStack(jsException, trace);
      jsThrow(jsException);
    } finally {
      glyph.ascii = wasAscii;
    }
  }
  