content: // Copyright 2024 Google LLC. 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 'dart:js_util';
  
  import 'package:async/async.dart';
  import 'package:node_interop/js.dart';
  
  import 'compile.dart';
  import 'compile_options.dart';
  import 'reflection.dart';
  import 'utils.dart';
  
  /// The Dart Compiler class.
  class Compiler {
    /// A flag signifying whether the instance has been disposed.
    bool _disposed = false;
  
    /// Checks if `dispose()` has been called on this instance, and throws an
    /// error if it has. Used to verify that compilation methods are not called
    /// after disposal.
    void _throwIfDisposed() {
      if (_disposed) {
        jsThrow(JsError('Compiler has already been disposed.'));
      }
    }
  }
  
  /// The Dart Async Compiler class.
  class AsyncCompiler extends Compiler {
    /// A set of all compilations, tracked to ensure all compilations complete
    /// before async disposal resolves.
    final FutureGroup<void> compilations = FutureGroup();
  
    /// Adds a compilation to the FutureGroup.
    void addCompilation(Promise compilation) {
      Future<dynamic> comp = promiseToFuture(compilation);
      var wrappedComp = comp.catchError((err) {
        /// Ignore errors so FutureGroup doesn't close when a compilation fails.
      });
      compilations.add(wrappedComp);
    }
  }
  
  /// The JavaScript `Compiler` class.
  final JSClass compilerClass = () {
    var jsClass = createJSClass(
      'sass.Compiler',
      (Object self) => {
        jsThrow(
          JsError(
            ("Compiler can not be directly constructed. "
                "Please use `sass.initCompiler()` instead."),
          ),
        ),
      },
    );
  
    jsClass.defineMethods({
      'compile': (Compiler self, String path, [CompileOptions? options]) {
        self._throwIfDisposed();
        return compile(path, options);
      },
      'compileString': (
        Compiler self,
        String source, [
        CompileStringOptions? options,
      ]) {
        self._throwIfDisposed();
        return compileString(source, options);
      },
      'dispose': (Compiler self) {
        self._disposed = true;
      },
    });
  
    getJSClass(Compiler()).injectSuperclass(jsClass);
    return jsClass;
  }();
  
  Compiler initCompiler() => Compiler();
  
  /// The JavaScript `AsyncCompiler` class.
  final JSClass asyncCompilerClass = () {
    var jsClass = createJSClass(
      'sass.AsyncCompiler',
      (Object self) => {
        jsThrow(
          JsError(
            ("AsyncCompiler can not be directly constructed. "
                "Please use `sass.initAsyncCompiler()` instead."),
          ),
        ),
      },
    );
  
    jsClass.defineMethods({
      'compileAsync': (
        AsyncCompiler self,
        String path, [
        CompileOptions? options,
      ]) {
        self._throwIfDisposed();
        var compilation = compileAsync(path, options);
        self.addCompilation(compilation);
        return compilation;
      },
      'compileStringAsync': (
        AsyncCompiler self,
        String source, [
        CompileStringOptions? options,
      ]) {
        self._throwIfDisposed();
        var compilation = compileStringAsync(source, options);
        self.addCompilation(compilation);
        return compilation;
      },
      'dispose': (AsyncCompiler self) {
        self._disposed = true;
        return futureToPromise(
          (() async {
            self.compilations.close();
            await self.compilations.future;
          })(),
        );
      },
    });
  
    getJSClass(AsyncCompiler()).injectSuperclass(jsClass);
    return jsClass;
  }();
  
  Promise initAsyncCompiler() => futureToPromise((() async => AsyncCompiler())());
  