001
Use jQuery in typescript modules exclusively
Lukas Juhrich
2021-06-20
Proposed
There’s multiple ways to interact with our HTML output using Javascript:
Add inline Javascript in e.g. a {% page_script %}
block
Add an ECMAscript (ES) or typescript (TS) module, configure webpack to export it as a chunk, and reference it in the relevant HTML pages
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.
New JS code that requires jQuery or jQuery extension functions shall occur in TS modules.
No jQuery invocations shall exist in inline JS.
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.
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.