vitejs / vite

Next generation frontend tooling. It's fast!
http://vitejs.dev
MIT License
67.07k stars 6.02k forks source link

Use the Sass Compiler API #14689

Closed nex3 closed 1 month ago

nex3 commented 10 months ago

Description

We (the Sass team) have just released a proposal for a Compiler API in Sass that shares resources between different compilations (see also https://github.com/sass/sass/issues/3296). This is particularly useful when using the sass-embedded package, which runs a very fast subprocess to compile Sass: within the lifetime of a single Compiler, the subprocess will remain open, eliminating all the overhead of starting it and shutting it down that currently causes sass-embedded to be slower than it could be.

For now, we're just seeking your feedback on the proposal. Once it lands, we hope you'll integrate it into Vite for substantially improved performance.

Suggested solution

Start a Compiler when Vite's build initializes and uses that compiler to compile all Sass files, so that Sass users using the embedded host experience better performance.

Alternative

Runs a separate sass.compile() call for each compilation, which has a large amount of overhead when using sass-embedded.

Additional context

No response

Validations

sapphi-red commented 10 months ago

Thank you!

As the proposed API only supports the modern API, Vite needs to migrate to modern API first (#7116). Other than that, I guess the proposed API work for us.

john-easci commented 4 months ago

Proposal -- do what sass-loader did (https://github.com/webpack-contrib/sass-loader/issues/774). Create an 'api' flag on the options that lets uses opt-in to use the modern API, while still maintaining 100% backwards compatibility with others.

Could be as simple as this in the config:

    export default defineConfig({
      plugins: [react()],
      css :{
        preprocessorOptions : {
            scss: {
                api: "modern",
                // Use the new Node loaders.  Added as an example so that it performed functionality other than what is
                // available today.
                importers: [
                    new sass.NodePackageImporter() 

                ]
            }        
        } 
      }
    })

Cutdown code will look something like this:

    // this would be the new input type... either one.
    type SassWorkerOptions = SassStylePreprocessorOptions & {additionalData: undefined} | 
                  Sass.StringOptions<"sync"> & {api:"modern"};

    if (options.api === "modern") {
        // eslint-disable-next-line no-restricted-globals
        const url = require('node:url');

        // reshape this.
        options.filename = url.pathToFileURL(options.filename);

        return new Promise((resolve, reject) => {
            try {
                let out = {
                    stats: {
                        includedFiles: [] as string[]
                    }
                };
                const res = sass.compileString(data, options as Sass.StringOptions<"sync">);
                out = Object.assign(out, res);

                // rebuild stats.includedFiles, since they're wanted
                // in some downstream process.
                out.stats = {
                    includedFiles: res.loadedUrls.map( (item) => {
                        return item.pathname
                    })
                }
                resolve(out);
            } catch (e) {
                reject(e);
            }
        });
    }