Skip to content

Instantly share code, notes, and snippets.

@rubys
Created December 20, 2021 03:48
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rubys/301ac5ac9b350e38633bc48ce66a75e5 to your computer and use it in GitHub Desktop.
Save rubys/301ac5ac9b350e38633bc48ce66a75e5 to your computer and use it in GitHub Desktop.

Rails 7 + import maps + Lit component + TypeScript

This is a follow-on to Rails 7 + import maps + JSX proof of concept.

Overall flow:

  • Create project
    • rails new lit-ts-demo
    • Add sprocket-esbuild gem to bundle
    • Add monkey patch for importmap-rails to support sprocket transpilation
  • Prep for development of Lit elements
    • make a directory for javascript elements
      • pin the contents of the directory to the import map
      • link the directory to the manifest, specifying ts as the file extension
    • pin the lit and lit/decorators.js modules
  • Create and use the simple-greeting element
    • create simple-greeting.ts in the app/javascript/elements directory
    • import the simple-greeting element in the application
    • generate a Rails controller with a single view
    • make use of the simple-greeting element in the view

Start the server with ./bin/rails server, and visit http://localhost:3000/lit/demo

Shell script to do the above appears below

# Create a project
rm -rf lit-ts-demo
rails new lit-ts-demo
cd lit-ts-demo
bundle add sprockets-esbuild
cat <<-'EOF' > config/initializers/importmap_monkeypatch.rb
class Importmap::Map
# determine what extensions to look for by parsing the manifest
def exts_for_path(path)
exts = Set.new ['.js']
# Read the manifest
config_dir = File.join(Rails.root, 'app/assets/config')
manifest_file = File.join(config_dir, 'manifest.js')
manifest = IO.read(manifest_file)
# Extract extensions from the manifest
manifest.scan(%r{//=\s+link_\w+\s+(\S+)\s+(\.\S+)}).map do |match|
link = File.expand_path(match[0], config_dir)
exts << match[1] if path.relative_path_from(link).to_s =~ /^(\w|\.$)/
end
exts
rescue
['.js']
end
# find all files matching the manifest in a given path
def find_javascript_files_in_tree(path)
files = []
exts_for_path(path).each do |ext|
files += Dir[path.join("**/*#{ext}")].collect do |file|
next if File.directory? file
Pathname.new(file.chomp(ext) + '.js')
end
end
files.compact
end
end
EOF
# Prep for development of Lit elements
mkdir app/javascript/elements
echo 'pin_all_from "app/javascript/elements", under: "elements"' >> config/importmap.rb
echo '//= link_tree ../../javascript/elements .ts' >> app/assets/config/manifest.js
./bin/importmap pin lit
./bin/importmap pin lit/decorators.js
# Simple greeting example from https://lit.dev/
cat <<-'EOF' > app/javascript/elements/simple-greeting.ts
import {html, css, LitElement} from 'lit';
import {customElement, property} from 'lit/decorators.js';
@customElement('simple-greeting')
export class SimpleGreeting extends LitElement {
static styles = css`p { color: blue }`;
@property()
name = 'Somebody';
render() {
return html`<p>Hello, ${this.name}!</p>`;
}
}
EOF
echo 'import "elements/simple-greeting"' >> app/javascript/application.js
./bin/rails generate controller lit demo
echo '<simple-greeting name="World"></simple-greeting>' > app/views/lit/demo.html.erb
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment