Genfic / Ogma

Publish your stories, talk about them, categorize them to your heart's content
https://genfic.net
GNU General Public License v3.0
1 stars 1 forks source link

Include a JS bundler into the pipeline #58

Open Atulin opened 3 years ago

Atulin commented 3 years ago

Currently (https://github.com/Atulin/Ogma/commit/52d086ee2cf7a778a25806f43488fe3718374b59) the size of all minified JS files excluding ones for the admin panel is 57.74 KB (13.0 KB GZipped). Seeing how user avatars, club icons, and story covers can be hundreds of kilobytes in size, concerns about the JS bundle being large is unfounded.

Even with Axios (17.5/6.3 KB), Lit (16.3/5.8 KB) and DayJS (6.3/2.8 KB), avan with the entirety of Vue (91.5/32.9 KB), the bundle would still weigh less than a single image loaded on the site, less than the fonts (smallest used WOFF2 font is 102.43/73.94). It would most likely be comparable in size with the current stylesheet (59.95/11.12) and that is all assuming no tree-shaking or any other optimizations a bundler could implement.

Suffice to say, using a bundler would only bring benefits, like being able to use generated OpenAPI clients (https://github.com/Atulin/Ogma/commit/b0c0263de6aade465583631c22d566453b8d5e44) or being able to fully make use of Lit for creating components and perhaps even deprecate the use of Vue. And even if we decide to keep Vue, it will be tree-shakeable so the total download size might decrease.

That leaves the problem of picking a bundler.

Output of the benchmark ``` $ py size.py .\alerts.min.js 210 bytes .\blogpost.min.js 636 bytes .\chapter-reads.min.js 1158 bytes .\chapter.min.js 634 bytes .\clock.min.js 203 bytes .\club-bar.min.js 773 bytes .\comments.min.js 3979 bytes .\input-progressbars.min.js 1575 bytes .\local-settings.min.js 742 bytes .\login.min.js 498 bytes .\navbar.min.js 387 bytes .\notifications-button.min.js 340 bytes .\notifications.min.js 608 bytes .\passwords.min.js 444 bytes .\profile-bar.min.js 949 bytes .\shelves.min.js 1694 bytes .\site.min.js 1164 bytes .\story.min.js 251 bytes .\themeswap.min.js 461 bytes .\tts.min.js 665 bytes .\account\blacklists.min.js 103 bytes .\account\index.min.js 143 bytes .\account\invite-codes.min.js 749 bytes .\club\add-story.min.js 67 bytes .\components\club-folder-selector-component.min.js 2435 bytes .\components\clubs-with-story-component.min.js 1175 bytes .\components\comment-component.min.js 6140 bytes .\components\folder-tree-component.min.js 2854 bytes .\components\input-blog-tags-component.min.js 1389 bytes .\components\input-counter-component.min.js 1570 bytes .\components\input-file-component.min.js 1403 bytes .\components\rating-component.min.js 1484 bytes .\components\report-modal.min.js 1632 bytes .\components\status-select-component.min.js 968 bytes .\components\tag-search-select-component.min.js 4200 bytes .\components\textarea-counter-component.min.js 1938 bytes .\components\toggle-component.min.js 1157 bytes .\editors\chapter.min.js 246 bytes .\editors\club.min.js 223 bytes .\native-components\featured-in-clubs.min.js 2233 bytes .\native-components\follow-button.min.js 1105 bytes .\native-components\quote-box.min.js 1282 bytes .\native-components\shelves-button.min.js 2938 bytes .\native-components\subscribe-thread-button.min.js 1670 bytes .\native-components\vote-button.min.js 1340 bytes .\search\blog.min.js 264 bytes .\search\story.min.js 561 bytes .\vue-directives\closeable.min.js 483 bytes ─────────────────────────────────────────────────────────────── Total: 59123.00 bytes 57.74 KB 0.06 MB ─────────────────────────────────────────────────────────────── Gzipped: 13308.00 bytes 13.00 KB 0.01 MB 22.51 % ```
Atulin commented 3 years ago

First attempt ended with a plethora of issues.

1. Event listeners somehow break

The code responsible for attaching event listeners to read/unread buttons in the story view breaks. The event listeners simply do not fire. affects both Rollup and Webpack

2. NSwag needs more concrete types

If an action method has response type of IActionResult, NSwag will default to FileResponse as the TS response type for some godforsaken reason, and it contains only a Blob of data. Which is pretty useless, since we're not handling any files here. Tracked by #59.

3. Build times

Both Rollup and Webpack seem to suffer fairly long build times, longer than the current Gulp setup

Atulin commented 3 years ago

Just a loose thought, but... Pretty much every route from the API would map to only one component, like a bookshelves menu or comment box. Perhaps using Lit in full and "bundling" each component separately with the clients it needs would be the way to go, rather than opting for one huge bundle.

That would eliminate the need for Axios that would otherwise pollute the global space, and since Dayjs is used only to format the dates perhaps a more lightweight solution could be created, like a simple Date object extension method that would format it in the way it needs to be formatted.

Atulin commented 2 years ago

In case I can't find a way to make OpenAPI TS clients fully treeshakeable, there's a route of using Closure Compiler on TS via https://github.com/angular/tsickle

Atulin commented 2 years ago

Treeshaking the TS clients seems to be blocked by https://github.com/RicoSuter/NSwag/issues/3624

Atulin commented 10 months ago

Rollup attempt, produces empty bundles:

// rollup.config.mjs
import { defineConfig } from "rollup";
import multi from "@rollup/plugin-multi-entry";
import resolve from "@rollup/plugin-node-resolve";
import minifyHTML from "rollup-plugin-html-literals";
import esbuild from "rollup-plugin-esbuild";

const root = './wwwroot/js';

/**
 * @type {import("rollup").RollupOptions}
 */
const common = {
    plugins: [
        multi(),
        resolve(),
        minifyHTML(),
        esbuild({
            tsconfig: `${root}/tsconfig.json`,
            minify: true,
            legalComments: "eof"
        })
    ],
};

export default defineConfig([
    {
        input: `${root}/src/**/*.(js|ts)`,
        output: {
            file: `${root}/bundle/bundle.js`,
            format: "cjs",
            sourcemap: true,
        },
        ...common
    },
    {
        input: `${root}/src-admin/**/*.(js|ts)`,
        output: {
            file: `${root}/bundle/bundle.admin.js`,
            format: "cjs",
            sourcemap: true,
        },
        ...common
    }
]);