AnswerDotAI / AnswerDotAI/fasthtml

[BUG] ScriptableJS() requires presence of Script(type='module')

Open
#510 0 comments 0 reactions 0 assignees View on GitHub
bug
Dominant language
Jupyter Notebook
Stars
7k
Forks
319
Avg merge
28m
Merged PRs (30d)
3

Description

**Describe the bug**
SortableJS() requires presence of Script(type='module')

**Minimal Reproducible Example**
External scripts can be included through the `fast_html()` wrapper, like so:

```python
app, rt = fast_app(
hdrs=(SortableJS())
)
```

...but this results in:

```html



Botifython / Another / Home


:root { --pico-font-size: 100%; }

import {Sortable} from 'https://cdn.jsdelivr.net/npm/sortablejs/+esm';
proc_htmx('.sortable', Sortable.create);

(function() {
var socket = new WebSocket(`ws://${window.location.host}/live-reload`);
var maxReloadAttempts = 1;
var reloadInterval = 1000; // time between reload attempts in ms
socket.onclose = function() {
let reloadAttempts = 0;
const intervalFn = setInterval(function(){
window.location.reload();
reloadAttempts++;
if (reloadAttempts === maxReloadAttempts) clearInterval(intervalFn);
}, reloadInterval);
}
})();

```

...which if you look close you will see that the JavaScript import is not
wrapped in a `` tag resulting in `<head>`-leakage. In
experimenting, I find that this will fix it:

```python
app, rt = fast_app(
hdrs=(SortableJS(), Script(type="module"))
)
```

...because even though it ends up with an empty script module tag, it also wraps
the SortableJS one properly:

```html
<!doctype html>
<html>
<head>
<title>Botifython / Another / Home </title> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<script src="https://unpkg.com/htmx.org@next/dist/htmx.min.js">
:root { --pico-font-size: 100%; }

import {Sortable} from 'https://cdn.jsdelivr.net/npm/sortablejs/+esm';
proc_htmx('.sortable', Sortable.create);

(function() {
var socket = new WebSocket(`ws://${window.location.host}/live-reload`);
var maxReloadAttempts = 1;
var reloadInterval = 1000; // time between reload attempts in ms
socket.onclose = function() {
let reloadAttempts = 0;
const intervalFn = setInterval(function(){
window.location.reload();
reloadAttempts++;
if (reloadAttempts === maxReloadAttempts) clearInterval(intervalFn);
}, reloadInterval);
}
})();

```

It's a hack.

**Expected behavior**

```python
app, rt = fast_app(
hdrs=(SortableJS())
)
```

...should result in:

```html

import {Sortable} from 'https://cdn.jsdelivr.net/npm/sortablejs/+esm';
proc_htmx('.sortable', Sortable.create);

```

**Environment Information**
- fasthtml version: 0.6.9

**Confirmation**
Please confirm the following:
- [✅] I have read the FAQ (https://docs.fastht.ml/explains/faq.html)
- [👨‍🎨] I have provided a minimal reproducible example
- [💯] I have included the versions of fastlite, fastcore, and fasthtml
- [🔥] I understand that this is a volunteer open source project with no commercial support.

Contributor guide

Open the contributing guide

Research direction

Start by reproducing fast_app(hdrs=(SortableJS(),)) with fasthtml 0.6.9 and compare the generated head with the documented workaround using Script(type="module"). Trace how SortableJS() content is inserted into the head; done means the expected usage emits the import inside a single script type="module" tag without requiring an empty Script tag.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, python
Domain
frontend, web-dev
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.