ADR001

Number

001

Title

Use jQuery in typescript modules exclusively

Author

Lukas Juhrich

Created

2021-06-20

Status

Proposed

Context

There’s multiple ways to interact with our HTML output using Javascript:

  1. Add inline Javascript in e.g. a {% page_script %} block

  2. Add an ECMAscript (ES) or typescript (TS) module, configure webpack to export it as a chunk, and reference it in the relevant HTML pages

  3. Add an ES/TS module, and import it in the main chunk.

The important differences between option one and the other two are

  • In inline JS, one cannot use an ES2015 import to use arbitrary libraries. However, this can indirectly be achieved by using the expose-loader or the ProvidePlugin in webpack’s config to expose certain symbols as window.foo.

  • In a separate module, we’re able to use typescript.

  • A separate module has to undergo the webpack toolchain, whereas inline JS is “cheap” in that respect.

Prior to this decision, we did a cleanup of the webpack rules, which replaced brute-force ProvidePlugin invocations

  • by specific import injections via imports-loader

  • or by window attribute expositions using expose-loader.

This is mainly motivated by a comment of Sebastian Schrader stating that blindly providing things via ProvidePlugin can have detrimental effects on tree shaking because webpack cannot make any strong assumptions anymore about who the dependents of an exposed symbol are.

Also, most uses of these plugins can be considered workarounds for a deficiency, since most inter-module dependencies should be realized by proper module import s and export s.

In light of this workaround, two instances of inline JS broke because jQuery’s $ symbol was not accessible anymore. In one instance, this was actually unavoidable, because bootstrapTable is only accessible as a jQuery extension function, a fact which will remain that way in the forseeable future.

Decision

  1. New JS code that requires jQuery or jQuery extension functions shall occur in TS modules.

  2. No jQuery invocations shall exist in inline JS.

  3. Current jQuery invocations that exist in ES modules shall be replaced by pure-ES alternatives wherever possible, or turned into TS code. This does not need to happen retrospectively, but at the latest whenever these invocations are next modified.

Consequences

  • The providePlugin section in the webpack config does not have to be reinstantiated.

  • The aforementioned, broken inline-JS took significantly more effort to fix: We had to create a new typescript module, and to make the $().bootstrap invocations compile, and we had to add declaration files (.d.ts), declaring the signature of bootstrapTable('refresh', params).

  • As a consequence, code completion and documentation lookups in JS files are now aware of this extension function, and can provide documentation and type hints.

  • Every new usage of bootstrapTable functionality now requires adding type declarations, which is a slight increase in effort as opposed to just reading the API docs.

  • From a contribbutor who is not too acquainted with TS syntax, this demands a few minutes more in time investment. This cost however does not scale linearly, as with more functions declared, more examples to imitate exist directly in the codebase.

  • Frontend developers are forced to read up on modern solutions to the problems jQuery once tried to solve.