LibSassHost icon indicating copy to clipboard operation
LibSassHost copied to clipboard

request: non-static file manager

Open pablocar80 opened this issue 4 years ago • 5 comments

Hello! I'm building a web application that uses LibSassHost. The one issue I ran into is that to specify a file manager, I have to plug it into the static property SassCompiler.FileManager. However, when you have multiple users on a website, each user may have different values current directory, folders and files available.

I considered using locks, though that would be a nasty quick that makes each user wait for everybody else. Slim semaphores would be better than locks, because they release threads, but still would make each user wait in line.

The ideal scenario would be to be able to pass the file manager in the parameter options of the compiler.

pablocar80 avatar Oct 30 '19 16:10 pablocar80

I think I found a workaround using AsyncLocal.

pablocar80 avatar Oct 30 '19 17:10 pablocar80

Hello, Pablo!

The ideal scenario would be to be able to pass the file manager in the parameter options of the compiler.

I myself would be happy to implement this functionality, but I do not have such an opportunity due to architectural limitations of original library.

Taritsyn avatar Oct 30 '19 17:10 Taritsyn

Understood. In my case I am using ASP.NET Core and because of that I was able work around this using AsyncLocal. This lets you create static variables that have different values on each HTTP request. So, my static class has a different set of values populated for each user, and there is no locking involved.

pablocar80 avatar Oct 30 '19 17:10 pablocar80

Excellent!

Taritsyn avatar Oct 30 '19 17:10 Taritsyn

Below is a wrapper that instantiates a different IFileManager on each async flow. To use it, we simply call GlobalFileManager.LocalManager = myInstance

using LibSassHost;
using System.Threading;

namespace Integrative.ModelerEngine.Styles
{
    class GlobalFileManager : IFileManager
    {
        readonly static GlobalFileManager _instance = new GlobalFileManager();
        readonly static AsyncLocal<IFileManager> _local = new AsyncLocal<IFileManager>();
        readonly static object _lock = new object();

        private GlobalFileManager()
        {
        }

        public static IFileManager LocalManager
        {
            get => _local.Value;
            set
            {
                InitializeGlobal();
                _local.Value = value;
            }
        }

        private static void InitializeGlobal()
        {
            if (SassCompiler.FileManager == _instance)
            {
                return;
            }
            lock (_lock)
            {
                if (SassCompiler.FileManager != _instance) // check again under lock
                {
                    SassCompiler.FileManager = _instance;
                }
            }
        }

        public bool SupportsConversionToAbsolutePath
            => _local.Value.SupportsConversionToAbsolutePath;

        public string GetCurrentDirectory()
        {
            return _local.Value.GetCurrentDirectory();
        }

        public bool FileExists(string path)
        {
            return _local.Value.FileExists(path);
        }

        public bool IsAbsolutePath(string path)
        {
            return _local.Value.IsAbsolutePath(path);
        }

        public string ToAbsolutePath(string path)
        {
            return _local.Value.ToAbsolutePath(path);
        }

        public string ReadFile(string path)
        {
            return _local.Value.ReadFile(path);
        }
    }
}

pablocar80 avatar Oct 30 '19 21:10 pablocar80

Hello, Pablo!

The ideal scenario would be to be able to pass the file manager in the parameter options of the compiler.

At the moment, I recommend that you switch to a Dart Sass Host library that supports this feature.

Taritsyn avatar Dec 27 '23 12:12 Taritsyn