middleman-importmap
An Importmap extension for Middleman.
Install
Add gem to Gemfile
gem 'middleman-importmap'
Usage
Activate extension in config.rb
**
activate :importmap
Create the importmap.yml
file at middleman root path
$ cd middleman_project && touch importmap.yml
Add importmaps to file (example)
imports:
"@hotwired/stimulus": https://unpkg.com/@hotwired/stimulus/dist/stimulus.js
The importmap.yml file keep the same structure of importmap in HTML
Replace default javascript tag by importmap
<!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>
Customize options if necessary
in config.rb
:
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
or by specifying arguments in template helpers:
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
Examples
- Creating an app using Stimulus JS
- Creating an app using React and React Router
Creating an app using Stimulus JS
Add the following code to /source/javascripts/site.js
import { Application } from "@hotwired/stimulus"
import HelloController from "./controllers/hello_controller.js"
window.Stimulus = Application.start()
Stimulus.register("hello", HelloController)
Create controllers
directory
$ mkdir -p source/javascripts/controllers
Now add HelloController at controllers/hello_controller.js
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
connect() {
console.log("Hello, Stimulus!", this.element)
}
greet() {
console.log("Clicked Greet Button")
}
}
One last and important thing is add element binding at index.html.erb
---
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.
Creating an app using React and React Router
This example is based on DHH's Youtube video presenting rails-importmap gem using React and htm.
Change importmap.yml
file to be like this
---
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"
Create source/javascripts/htm_create_element.js
file
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)
Create components
and pages
directories
mkdir -p source/javascripts/components && mkdir -p source/javascripts/pages
Create components/Page.js
file
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
}
Create pages/Home.js
file
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>
<//>
`
Create pages/About.js
file
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>
<//>
Create components/App.js
file
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} />`
Add the following code to site.js
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)
Add the following code to source/index.html.erb
---
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/.
See more
- 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
License
See ./LICENSE
file for more details.
Author
Daniel Vinciguerra