An Importmap extension for Middleman.
Add gem to Gemfile
gem 'middleman-importmap'
activate :importmap
$ cd middleman_project && touch importmap.yml
imports:
"@hotwired/stimulus": https://unpkg.com/@hotwired/stimulus/dist/stimulus.js
The importmap.yml file keep the same structure of importmap in HTML
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Use the title from a page's frontmatter if it has one -->
<title><%= current_page.data.title || "Middleman" %></title>
<%= stylesheet_link_tag "site" %>
<%= javascript_importmap_tags %>
</head>
<body>
<%= yield %>
</body>
</html>
activate :importmap do |option|
option.entrypoint = "site" # js entrypoint's filename without extension
option.importmap = "importmap.yml" # importmap's filename with extension (yaml or json)
option.use_shim = true # or false
option.shim_src = "path/to/shim" # defaults to hardcoded js cdn
end
This will override options in config.rb
<%= javascript_importmap_tags("main", importmap: "importmap.json", shim: false) %>
# or customize one by one:
<%= javascript_importmap_shim_tag(shim_src: "another/path") %>
<%= javascript_inline_importmap_tag("importmap.json", shim: true) %>
<%= javascript_inline_module_tag("main", shim: true) %>
# See source code for methods implementation
import { Application } from "@hotwired/stimulus"
import HelloController from "./controllers/hello_controller.js"
window.Stimulus = Application.start()
Stimulus.register("hello", HelloController)
$ mkdir -p source/javascripts/controllers
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
connect() {
console.log("Hello, Stimulus!", this.element)
}
greet() {
console.log("Clicked Greet Button")
}
}
---
title: Welcome to Middleman
---
<h1>
Middleman is Running
</h1>
<div data-controller="hello">
<input type="text">
<button data-action="click->hello#greet">Greet</button>
</div>
<%= link_to(
"Read Documentation",
"https://middlemanapp.com/basics/templating_language/",
target: "_blank"
) %>
If all things are OK, than start middleman server using command bundle exec middleman server
and open your browser devtools to see the messages.
This example is based on DHH's Youtube video presenting rails-importmap gem using React and htm.
---
imports:
"htm": "https://ga.jspm.io/npm:htm@3.1.1/dist/htm.module.js"
"react": "https://ga.jspm.io/npm:react@18.2.0/index.js"
"react-dom": "https://ga.jspm.io/npm:react-dom@18.2.0/index.js"
"react-router-dom": "https://ga.jspm.io/npm:react-router-dom@6.21.1/dist/main.js"
"htm_create_element": "/javascripts/htm_create_element.js"
scopes:
"https://ga.jspm.io/":
"@remix-run/router": "https://ga.jspm.io/npm:@remix-run/router@1.14.1/dist/router.js"
"react-router": "https://ga.jspm.io/npm:react-router@6.21.1/dist/main.js"
"scheduler": "https://ga.jspm.io/npm:scheduler@0.23.0/index.js"
This file is necessary to use htm with React in an environment that doesn't have build process of JSX files.
import { createElement } from 'react'
import htm from 'htm'
export const h = htm.bind(createElement)
mkdir -p source/javascripts/components && mkdir -p source/javascripts/pages
Creating this file to avoid code duplication of components and demonstrate how to use composition in this environment.
import { h } from "htm_create_element"
const Footer = () => h`
<footer class="footer mt-auto py-3 bg-body-tertiary">
<div class="container">
<span class="text-body-secondary">
Build by <a href="https://github.com/dvinciguerra">dvinciguerra<//> using <a href="https://github.com/dvinciguerra/middleman-importmap">middleman-importmap<//>.
</span>
</div>
</footer>
`
const Container = ({ children }) => h`
<main class="flex-shrink-0">
<div class="container">
${children}
</div>
</main>
<${Footer} />
`
const Title = ({ children }) => h`
<h1 class="mt-5">${children}</h1>
`
const Lead = ({ children }) => h`
<p class="lead">${children}</p>
`
export default {
Container,
Title,
Lead
}
Now, let's create the Home page using the components created above and react-router-dom Link
component.
import { h } from "htm_create_element"
import { Link } from "react-router-dom"
import Page from "../components/Page.js"
export default () => h`
<${Page.Container}>
<${Page.Title}>Middleman Importmap React<//>
<${Page.Lead}>
This is a simple page created using Middleman-importmap and React to demonstrate how it is possible to build
frontends in Middleman using importmap without any build.
<//>
<hr class="my-4" />
<p>
<${Link}
to="/getting-started"
class="btn btn-dark btn-lg"
role="button"
>
Getting Started
<//>
<a
href="https://github.com/dvinciguerra/middleman-importmap"
class="btn btn-secondary btn-lg ms-1"
role="button"
target="_new"
>
GitHub
<//>
</p>
<//>
`
Creating just another page to demonstrate how to use react-router-dom Link
behaviour.
import { h } from "htm_create_element"
import { Link } from "react-router-dom"
import Page from "../components/Page.js"
export default () => h`
<${Page.Container}>
<${Page.Title}>About<//>
<${Page.Lead}>
This is a simple About page
<//>
<hr class="my-4" />
<p>
<${Link}
to="/"
class="btn btn-dark btn-lg"
role="button"
>
Back
<//>
</p>
<//>
Creating a component to wrap all pages and use react-router-dom RouterProvider
component.
import { createBrowserRouter, RouterProvider } from 'react-router-dom'
import { h } from 'htm_create_element'
import Home from "../pages/Home.js"
import About from "../pages/About.js"
const router = createBrowserRouter([
{ path: '/', element: h`<${Home} />` },
{ path: '/about', element: h`<${About} />` }
])
export default () => h`<${RouterProvider} router=${router} />`
import { render } from 'react-dom'
import { h } from 'htm_create_element'
import App from "./components/App.js"
const root = document.getElementById('root')
render(h`<${App} />`, root)
---
title: Welcome to Middleman
---
<div id="root"></div>
If all things are OK, than start middleman server using command bundle exec middleman server
, open your browser and
access http://127.0.0.1:4567/.
- Importmap polyfill at guybedford/es-module-shims
- Can I Use about Import Maps browser support
- W3C Import Maps Spec
- The helper tags are inspired by rails/importmap-rails gem
See ./LICENSE
file for more details.
Daniel Vinciguerra