telerik / kendo-themes

Monorepo for SASS-based Kendo UI themes
148 stars 70 forks source link

Slow themes compilation with DartSass and webpack #4394

Open TeyaVes opened 1 year ago

TeyaVes commented 1 year ago

Describe the bug The compilation of the themes when importing separate components and using webpack + DartSass has increased significantly between version 5.9 and 6.2 of the themes (more than 5 times). The compilation is fast when compiling the flatten theme file from dist folder.

To reproduce Steps to reproduce the behavior:

  1. Import the grid: @import "@progress/kendo-theme-default/scss/grid/_index.scss";
  2. Use webpack and dartSass for compilation

The compilation with v. 6.2 takes about 37459 ms, with v. 5.9 it's 6076ms.

We should figure out what might be causing this slow down.

ChristineBoersen commented 1 year ago

I had the exact same problem (using dart-sass standalone cli directly, no webpack), I then realized my version was old, 1.41.1 to 1.6.2 (latest as of this post) and the horrible slowness went away :)

Took me from 1.5 min to 4 seconds on my kendo theme (bootstrap) with my project's changes/additions added to the compile.

Hope this helps your situation as well as it did mine! :)

Stephen-Hamilton-C commented 1 year ago

Also running into the same issue. 20 minute build times are causing our CI to timeout and fail. Had to downgrade kendo-theme-bootstrap to 5.12.0 and resolve the CSS conflicts manually. This reduced our build time to 8 minutes. This is happening on an Angular project, so we can't control the webpack or dart-sass versions directly.

joneff commented 1 year ago

I understand the frustration any developer may have with slow compilation times when using dart-sass (or just sass) and I will try to give as much as detailed answer as possible:

Kendo themes

A big contributor to the slow down is definitely the way we organize the themes: since day one, we support both loading the entire theme, or any number of components with their respective dependencies. The way we do that is that we load each component dependencies and then just trim out the excess output.

That leads to humongous recursive loading and processing of files!

We've mitigated this issue by providing a flattened version (single file) of our themes for the most common scenario. Yet, for developers who want to use, say the grid, things are still gloomy: the grid depends on more than 20 components, and of those components, many depend on the button, which depends on the icon. In effect, the icon component gets processed probably more than 20 times...

Then we provided a "plugin" (importer to be precise) for processing files once. That works well in node-sass, but not so well in dart-sass. In fact, dart-sass skips usage of importers when the file requested exists in the local file system, as opposed to node_modules. A somewhat bizarre consequence of that is the classic theme, which imports the default theme in its entirety, when using said importer compiles faster than the default theme using the same importer.

webpack

Webpack has a life of its own -- it's a pivotal tool that gets update somewhat unpredictably. For instance, at some point in time, right around Angular v8, webpack update sass-loader to a version which uses dart-sass by default.

node-sass

Node-sass is a js wrapper around libsass -- a C based sass implementation. When compared to the original ruby sass it was blazing fast and supported many more features, including previously mentioned loaders. It would crunch sass-projects like a hot knife goes trough butter and frankly, there weren't that many faults.

dart-sass dart-sass is a dart implementation of sass, compiled to javascript. It's optimized heavily in favour of the new syntax (@use) and removes some features we've come to love in both ruby sass and node-sass.

And it's slow. Like major league slow... True, it would work some what acceptably fast with @use syntax, but that's about it.

sass-embedded

That's a js wrapper around dart native binary. It's fast. Really fast. However, it's not the default implementation for sass-loader, which is used by webpack.


Where does this leave us? It's quite simple, actually:

Use sass-embedded in lieu of dart-sass

This should make things faster. Like a lot faster. Optionally use an importer function (see node-sass)

// in webpack.config.js, where you specify sass-loader

{
    loader: 'sass-loader',
    'options': {
        implementation: require('sass-embedded'),
    }
}

**Use node-sass with importer***

// importer function
function sassCacheImporter( options ) {
    const opts = options || { cache: true };
    const cache = opts.cache;
    const importedFiles = cache instanceof Set ? cache : new Set();

    function importer(url, prev) {
        if (prev === 'stdin') {
            return null;
        }

        const file = path.resolve(path.dirname(prev), url);

        // Only cache existing files
        if (fs.existsSync(file)) {
            if ( !!cache && importedFiles.has(file) ) {
                return {};
            }

            importedFiles.add(file);

            return { file };
        }

        return null;
    }

    return importer;
}

// webpack.config
{
    loader: 'sass-loader',
    options: {
        implementation: require('node-sass'),
        sassOptions: {
            importer: sassCacheImporter(),
        }
    }
}

Those should give you decent enough speed improvement in the magnitude of about 10-15x.

Finally, if the theme is not something you customize on every build, you can compile it once and include that css in the application. After all, noone is compiling the angular framework on every step, why should anyone do the same for styles ;)


I know the above requires extra work. We are working on ways of improving things, by providing a mechanism to always load a single file and then via variable configuration to load what's needed. Another option is not to load every dependency of a component. Both are, however, breaking changes and either improvement will not make it till 2024 R1 at the earliest.

In the meantime, you can use any of the approaches above to speed up compilation of your project.

austinw-fineart commented 2 months ago

I'd like to add that compilation times have regressed further with v9.0.0, by more than 2 times on top of the previously noted increase. It appears that dart-sass is greatly hindered by the splintering of the utilities package.