fable-compiler / Fable.Lit

Write Fable Elmish apps with Lit
https://fable.io/Fable.Lit/
MIT License
91 stars 13 forks source link

Q: wrapping requirejs amd module (durandaljs interop) #54

Open jkone27 opened 2 years ago

jkone27 commented 2 years ago

is there a way to wrap within a web-component a requirejs/amd module?

i am using a very old durandaljs (then aureliajs) app, and it uses requirejs and amd module loading. https://github.com/BlueSpire/Durandal https://www.infoq.com/articles/durandal-javascript-framework/

i was thinking i could turn that to interop with F# and Fable.Lit and progressively migrate away from it completely

index.html

<html>
    <head>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.min.js" data-main="app/main"></script>
    </head>
    <body>
        <div id="applicationHost"></div>
    </body>
</html>

Main.js

requirejs.config({
    paths: {
        'text': 'https://cdnjs.cloudflare.com/ajax/libs/require-text/2.0.12/text.min',
        'durandal':'https://cdn.jsdelivr.net/npm/durandal@2.2.0/js',
        'plugins' : 'https://cdn.jsdelivr.net/npm/durandal@2.2.0/js/plugins',
        'transitions' : 'https://cdn.jsdelivr.net/npm/durandal@2.2.0/js/transitions',
        'knockout': 'http://ajax.aspnetcdn.com/ajax/knockout/knockout-3.5.0',
        'jquery': 'https://ajax.googleapis.com/ajax/libs/jquery/2.2.1/jquery.min',
        } 
    });

define(function (require) {
    var system = require('durandal/system');
    var app = require('durandal/app');

    system.debug(true);

    app.title = 'Durandal Starter Kit';

    app.configurePlugins({
        router:true,
        dialog: true
    });

    app.start().then(function() {
        app.setRoot('shell');
    });
});

and the main view: Shell.js

define(function (require) {
    var app = require('durandal/app');
    var ko = require('knockout');
    return {
        name: ko.observable(),
        sayHello: function () {
            app.showMessage('Hello ' + this.name() + '! Nice to meet you.', 'Greetings');
        }
    };
});

And Shell.html.. they use Knockout.js for 2-ways bindings... https://knockoutjs.com/index.html

was just curious which approach would you go to use Fable.Lit on this one, if any idea

thank you so much, i am quite new to js for now i have managed to transpile to ts and back to js but took me a while, i was checking also fable alternatives as F# is my fav lang :)

jkone27 commented 2 years ago

also i never used vite (was doing all with babel for what i achieved for now), could this be useful? - https://github.com/linsk1998/vite-plugin-amd ?

AngelMunoz commented 2 years ago

is there a way to wrap within a web-component a requirejs/amd module?

There is no need to wrap web components at all, they are standard browser features so you can just import the file that registers the web component directly

you can see this in the codepen https://codepen.io/AngelMunoz/pen/XWEKxLO you can import them directly in your index.html or if you you truly want to make it part of requirejs you'd need to do something like this

requirejs.config({
    paths: {
        '@material/mwc-snackbar': 'https://cdn.skypack.dev/@material/mwc-snackbar',
        // other imports
        } 
    });
// Shell.js
define(function (require) {
    require('@material/mwc-snackbar'); // import to register the web component
    // other code
});

For Fable.Lit based Web components, depending on how you setup your project, you can include the imports in a similar fashion to this project, you can import the fable bundle to your index.html or compile the fable sources, put them in an internal cdn and consume them via requirejs or index.html

the key point here is that once you register a web component by importing on the browser somehow it is not globally available and it is not required to be in any kind of bundler, be it webpack, vite, require, etc.

module Components.MyElement

[<LitElement("my-element")>]
let private Element() =
  LitElement.init() |> ignore
  html $"<div>I'm Web Component</div>"

let register() = ()

to use it from F# you'd do something like this

module Main

open Components

MyElement.register() // this ensures the web component gets registered within the browser

to use it from Javascript you'd do something like this

// for bundlers and other things that don't apply dead code elimination
import 'fable-output/MyElement.fs.js' // this is enough

// for bundlers and tools that apply dead code elimination (trimming, tree-shaking)
// you can import it in the following ways
import { register } from '../fable-output/MyElement.fs.js'
// or 
import { register } from 'fable-output/MyElement.fs.js' 

register();