Skip to content

JS Runtime Package #205

@tumidi

Description

@tumidi

Die Logik des Attempt-Iframe lebt aktuell in einem jinja2-Template, was historisch gewachsen, aber eigentlich suboptimal ist.

Diese Nachteile sehe ich im aktuellen Ansatz:

  • "Template-Abuse": Jinja wird verwendet, um JS-Code zu generieren.
  • Type safety: Inline JS im Template ist nicht statisch analysierbar.
  • Testability: Nur schwer oder gar nicht zu testen.
  • Global Pollution: attempt wird als globale Variable gesetzt.
  • Framework boundary: Der Inhalt des iframe's sollte als separate (Mini-)Applikation angesehen werden.
  • Unnötige "Cross-project" Code-Duplizierung zwischen SDK und Moodle Plugin.

Die Idee wäre, eine "Mini-Lib", geschrieben in TypeScript zu erstellen, die vom SDK (und Moodle-Plugin?) verwendet wird.

  • Verantwortlichkeiten klar trennen:
    • Host application (Vue SPA, Moodle, ...)
      • spricht via postMessage mit dem iframe
      • greift niemals direkt in den iframe DOM.
    • Question iframe
      • unabhängige "Mini-Applikation"
      • exposed nötige JS-API (Attempt-Objekt, Formdaten, resize, theme switch, etc.)
      • Führt zusätzlichen JS-Code vom QPy-Package im iframe aus.

Alles was Logik/Verhalten ist, wandert außerhalb des iframe in eine "Mini-Lib". Die wird einmal gebundled und als static asset serviert.

Das Jinja2-Template wird stark vereinfacht und enthält ausschließlich Daten, etwa

{% extends "page.html.jinja2" %}
{% block head %}
    {{ super() }}
    <base target="_blank">
    <link rel="stylesheet" href="{{ static_url('question-iframe.css') }}">
    {% for url in stylesheet_urls %}<link rel="stylesheet" href="{{ url|safe }}">{% endfor %}
{% endblock head %}

{% block content %}
    <div id="qpy-root">
        {# Question formulation, feedback, etc. (wie bisher) #}
    </div>

    {# Data-only #}
    <script type="application/json" id="qpy-runtime-data">
        {
            "displayOptions": {{ display_options|tojson }},
            "javascriptCalls": {{ javascript_calls|tojson }},
        }
    </script>

    <script type="importmap">
        { "imports": {{ import_map|tojson }} }
    </script>

    {# Runtime bundle #}
    <script type="module" src="/static/iframe-runtime.js"></script>
{% endblock content %}

@questionpy/iframe-runtime Package

Skizze:

# Python SDK / Moodle plugin
npm install @questionpy/iframe-runtime
/questionpy-iframe-runtime
├─ src
│   ├─ index.ts       # Public API (bootstraps the iframe)
│   ├─ Attempt.ts     # Attempt class
│   ├─ events.ts      # postMessage protocol
│   ├─ resize.ts      # ResizeObserver logic
│   ├─ theme.ts       # Color mode/custom theme logic
│   ├─ ...
│   └─ types.d.ts     # Shared type definitions
├─ package.json
├─ tsconfig.json
└─ rollup.config.ts

@questionpy/iframe-runtime lebt entweder innerhalb des SDKs, oder wenn das Moodle-Plugin auch diese Lib verwenden soll (m.M.n. die präferierte Variante), dann könnte @questionpy/iframe-runtime evtl. auch in ein eigenes Repository wandern.

Integration mit Host-Apps

  • Host-Apps können mit Hilfe des Templates/Render-Logik Einfluss auf die DOM-Struktur des iframe-Inhalts nehmen und bei Bedarf auch custom CSS injizieren.
  • Je nach Host-App beinhaltet das Plugin (oder SDK in diesem Fall) JavaScript-Adapter-Code, der mit dem iframe spricht.
  • Rollup baut verschiedene (ESM-, AMD-, iife-Module, ...), so dass die LMS und ihre Modulsystem-Constraints abgedeckt werden.

Integration mit Question-Package-Code

Die Lib bietet eine einheitliche API, um Paketen ein Interface zum Attempt-Objekt (und zukünftig evtl. noch anderen Dingen?) zu bieten. Sollte einen Promise zurückliefern, so dass sichergestellt ist, dass der Code komplett initialisiert und bereit ist, etwa

import { getAttempt } from '@questionpy/iframe-runtime'

const attempt = await getAttempt()

Vorteile

  • Type Safety: Volle Coverage für die iframe-Runtime-Logik
  • Entkoppelte Architektur: Klare Trennung zwischen
    • Host-App (SDK bzw. LMS)
    • Iframe-Runtime (generisches TypeScript)
    • Question Package (HTML/JS/CSS)
  • Code wiederverwendbar zwischen SDK und LMS-Plugins
  • Testability: Runtime-Logik kann komplett "ge-unit-tested" werden
  • Klar definierte API zwischen
    • Host und iframe-Runtime (Message passing)
    • Paket- und iframe-Runtime-Code
    • (beide APIs könnten bei Bedarf versioniert werden)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions