DEV: Preserve ordering of inline script tags #33607
Draft
+26
−33
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Previously, inline
<script>
would be collected together into the JS bundle for the theme field, which is then loaded with adefer
attribute to match all other script tags in Discourse. While this works in most situations, it can be surprising when inline script tags are used in conjunction with external scripts:In this example, the inline script would be collected into the JS bundle for the theme, and would be executed after the analytics script. The result would be something like
With our modern strict-dynamic CSP, ideally we would go back to rendering these as simple inline script tags. Unfortunately though, inline scripts do not support the
defer
attribute.type=module
has the same ordering asdefer
, but the change in scope would cause problems with existing scripts.Converting each inline script to its own
.js
file could work, but would introduce many new HTTP requests for tiny files, and would complicate the backend implementation.Therefore, this commit goes for a hybrid approach. For each script tag in a theme, a
type="text/discourse-deferred-inline-js"
attribute is added. This prevents the browser from executing it. Then, a small<script type="module"
is injected after the original script tag. When that runs, it removes the customtype
attribute, which causes the inline script to be executed immediately.That gives us the functionality of inline scripts, but with the timing of
defer
scripts.Note:
<script type="module"
andtype="text/discourse-plugin"
are unaffected by this change. For modern code,type="module"
is probably the cleanest option, and does not require us to apply this workaround.