hebcal / hebcal-leyning

Javascript Torah Reading API for Parashat HaShavua and holidays
https://hebcal.github.io/api/leyning/
BSD 2-Clause "Simplified" License
9 stars 1 forks source link

Refactor to work with Tree Shaking #433

Open SLaks opened 2 weeks ago

SLaks commented 2 weeks ago

Motivation

I wrote some very simple code to run in a browser and print a full year of פרשה names:

import { HebrewCalendar } from '@hebcal/core'
const events = HebrewCalendar.calendar({
  year: new Date().getFullYear() - 1,
  numYears: 2,
  sedrot: true,
  noHolidays: true,
})

console.log(
  events.map((event) => ({
    datetime: event.date.greg(),
    label: event.render('he-x-NoNikud'),
  }))
)

When compiled for production using https://vitejs.dev/ with default settings, this produced 150 kB of optimized JS!

dist/index-Cca6IFY6.js                    150.13 kB │ gzip: 53.69 kB

Since all functions are contained in the HebrewCalendar class and the Event hierarchy, Tree Shaking cannot see which APIs are actually used.

Suggestion

It ought to be possible to build a new modular API in which application code only imports features that are actually used. This should allow tree shaking to entirely drop unused features (eg, emojis, zmanim calculations, unused locales, etc).

This would be a new API that requires explicit imports to enable more calculations and methods. The existing API would remain as a wrapper to avoid breaking existing code. Applications that want to benefit from tree shaking would need to migrate to the new API to gain the benefits.

Details

Making code tree-shakable involves a number of changes (these changes can be made independently for incremental improvements, but each incremental change would require clients to refactor further to get more benefit):

Note: All of these suggestions are rough ideas; I have not reviewed the code carefully enough to be sure that each of these make sense and would actually allow non-trivial amounts of code to be tree-shaken

Let me know if you need help understanding, designing, prototyping, or implementing this.

mjradwin commented 2 weeks ago

Great suggestion! @hebcal/core was definitely not designed with minimal code-size as a goal. For example, there are some parts of the library such as Hallel and Tachanun that are rarely used, and could easily be separate libraries. The zmanim functionality is used much more widely (but not by every application), and it pulls in a large dependency on an elevation-aware sunset calculator - it could be split out, but it would be significant work to do so.

Unfortunately, I don't have the time to undertake such a large project -- I just started a new job and it may be many months before I could try to tackle this.

I'm totally supportive of this kind of work if it can be done without breaking compatibility. Is this something you want to attempt?

There was an earlier attempt to reduce the size of generated code with some modest success. As I was learning TypeScript, I split out HDate into its own package @hebcal/hdate which does Hebrew-Gregorian date conversions but has no notion of holidays or parsha.

To preserve backwards compatibility and to avoid breaking clients of @hebcal/core, the entire @hebcal/hdate library is bundled into @hebcal/core.

SLaks commented 2 weeks ago

I'm also busy with other stuff right now; I will probably take a stab at this in a month or two.

I would probably do expose this as multiple files in a dedicated subfolder, so that application code would look like import {includeModernHolidays} from '@hebcal/core/modular/inclusions'