Skip to content

Commit 8d27cd6

Browse files
committed
New docs: Lit
As requested in: * https://trello.com/c/PVnfdeaN#comment-665fffaa2798a59513617915 * https://trello.com/c/PVnfdeaN#comment-60a7c84181482c8dce7d0f55 In a period of one year, it went from 17k to 20k stars on GitHub. This is my first time contributing to devdocs, it took me an entire day to learn how to write a new scraper and new filters for this documentation, including the time spent fine-tuning the result.
1 parent 4a23f5e commit 8d27cd6

File tree

8 files changed

+254
-0
lines changed

8 files changed

+254
-0
lines changed

assets/stylesheets/application.css.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@
8282
'pages/kubernetes',
8383
'pages/laravel',
8484
'pages/liquid',
85+
'pages/lit',
8586
'pages/love',
8687
'pages/lua',
8788
'pages/gnu_make',

assets/stylesheets/pages/_lit.scss

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
._lit {
2+
@extend %simple;
3+
4+
h4 { @extend %block-label, %label-blue; }
5+
6+
.propertyDetails {
7+
padding-left:1.5em
8+
}
9+
.heading.property {
10+
margin-top:2em
11+
}
12+
.heading.property > h4 {
13+
font-weight:400
14+
}
15+
.newKeyword,
16+
.readonlyKeyword,
17+
.staticKeyword {
18+
font-style:italic
19+
}
20+
.functionName,
21+
.propertyName {
22+
font-weight:700
23+
}
24+
aside.litdev-aside {
25+
display: flex;
26+
border-style: solid;
27+
border-width: 1px;
28+
padding: 1em 1em 1em 0em;
29+
margin: 1em 0;
30+
svg {
31+
width: 1.5em;
32+
margin-inline: 1em;
33+
}
34+
}
35+
litdev-switchable-sample {
36+
pre[data-language] {
37+
position: relative;
38+
}
39+
pre[data-language]::before {
40+
position: absolute;
41+
top: 0;
42+
right: 16px;
43+
opacity: 0.5;
44+
}
45+
pre[data-language="js"]::before {
46+
content: "JavaScript";
47+
}
48+
pre[data-language="ts"]::before {
49+
content: "TypeScript";
50+
}
51+
}
52+
}

lib/docs/filters/lit/clean_html.rb

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
module Docs
2+
class Lit
3+
class CleanHtmlFilter < Filter
4+
def call
5+
6+
css('.offscreen, #inlineToc, a.anchor, [aria-hidden="true"], #prevAndNextLinks').remove
7+
8+
css('[tabindex]').remove_attribute('tabindex')
9+
10+
# Removing the side navigation.
11+
css('#docsNavWrapper, #rhsTocWrapper').remove
12+
13+
# Removing this extra div.
14+
div = at_css('#articleWrapper')
15+
article = div.at_css('article')
16+
article.remove_attribute('id')
17+
div.replace(article)
18+
19+
# Expanding and replacing the <template>, statically.
20+
# This code is a hacky incomplete implementation of
21+
# https://github.com/lit/lit.dev/blob/main/packages/lit-dev-content/src/components/litdev-aside.ts
22+
css('litdev-aside').each do |node|
23+
frag = Nokogiri::HTML::DocumentFragment.new(node.document)
24+
template = node.at_css('template')
25+
aside = template.children.first
26+
aside['class'] = 'litdev-aside'
27+
frag.add_child(aside)
28+
template.remove
29+
div = Nokogiri::XML::Node.new('div', @doc)
30+
div.add_child(node.children)
31+
aside.add_child(div)
32+
node.replace(aside)
33+
end
34+
35+
# Removing the live playground examples.
36+
# https://github.com/lit/lit.dev/blob/main/packages/lit-dev-content/src/components/litdev-example.ts
37+
# Someday we can try enabling the live examples by adding appropriate code to assets/javascripts/views/pages/.
38+
css('litdev-example').each do |node|
39+
node.remove
40+
end
41+
42+
# Cleaning up the preformatted example code.
43+
css('pre:has(code[class])').each do |node|
44+
lang = node.at_css('code')['class']
45+
lang.sub! /^language-/, ''
46+
node.content = node.css('.cm-line').map(&:content).join("\n")
47+
node['data-language'] = lang
48+
end
49+
50+
# Cleaning up example import.
51+
css('div.import').each do |node|
52+
pre = Nokogiri::XML::Node.new('pre', @doc)
53+
pre.content = node.css('.cm-line').map(&:content).join("\n")
54+
pre['data-language'] = 'javascript'
55+
node.replace(pre)
56+
end
57+
58+
# Moving the "kind" to inside the header.
59+
# Because it looks better this way.
60+
css('.kindTag').each do |kindtag|
61+
heading = kindtag.parent
62+
next unless heading['class'].include? 'heading'
63+
h = heading.at_css('h2, h3, h4')
64+
h.prepend_child(kindtag)
65+
end
66+
67+
doc
68+
end
69+
end
70+
end
71+
end

lib/docs/filters/lit/entries.rb

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
module Docs
2+
class Lit
3+
class EntriesFilter < Docs::EntriesFilter
4+
5+
def get_name
6+
name = at_css('h1').content.strip
7+
end
8+
9+
def get_type
10+
# The type/category is section name from the sidebar.
11+
active = at_css('#docsNav details li.active')
12+
return nil unless active
13+
summary = active.ancestors('details').first.at_css('summary')
14+
return nil unless summary
15+
summary.css('[aria-hidden="true"]').remove
16+
summary.content.strip
17+
end
18+
19+
def additional_entries
20+
entries = []
21+
22+
# Code for the API reference pages (and other similar pages).
23+
scope_name = ''
24+
css('.heading > h2[id], .heading > h3[id], .heading > h4[id]').each do |node|
25+
name = node.content.strip
26+
id = node['id']
27+
# The kindTag has these values:
28+
# class, decorator, directive, function, namespace, type, value
29+
kind = node.parent.at_css('.kindTag')&.content&.strip
30+
31+
if kind
32+
# Saving the current "scope", i.e. the current class name.
33+
# This is useful to prefix the method/property names, which are defined after this element.
34+
scope_name = name
35+
name = kind + " " + name
36+
else
37+
# If this is a method/property, it has a different markup.
38+
# Let's extract them and add a prefix for disambiguation.
39+
function = node.at_css('.functionName')
40+
property = node.at_css('.propertyName')
41+
if function
42+
# Note how "functions" are actually "methods" of some class.
43+
# Bare (top-level) functions are extracted when `.kindTag` is "function".
44+
name = scope_name + '.' + function.content.strip
45+
kind = 'method'
46+
elsif property
47+
name = scope_name + '.' + property.content.strip
48+
kind = 'property'
49+
end
50+
end
51+
52+
# If we couldn't figure out the kind, this is a header tag that we can ignore.
53+
entries << [name, id, kind] if kind
54+
end
55+
56+
# Code for the Built-in Directives page.
57+
# This page has a TOC of the built-in directives, with a clear documentation of each one.
58+
# Note that the directives are also indexed in the API reference pages.
59+
# Yes, each directive is indexed twice, because each one is documented twice.
60+
css('.directory a[href^="#"]').each do |node|
61+
name = node.content.strip
62+
id = node['href'].sub /^#/, ''
63+
# type will be "Built-in directives"
64+
type = node.ancestors('article').at_css('h1').content.strip
65+
entries << [name, id, type]
66+
end
67+
68+
entries
69+
end
70+
end
71+
end
72+
end

lib/docs/scrapers/lit.rb

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
module Docs
2+
class Lit < UrlScraper
3+
self.name = 'Lit'
4+
self.slug = 'lit'
5+
self.type = 'lit'
6+
7+
self.links = {
8+
home: 'https://lit.dev/',
9+
code: 'https://github.com/lit/lit/'
10+
}
11+
12+
options[:container] = 'main'
13+
14+
options[:max_image_size] = 250_000
15+
16+
# Note: the copyright will change soon due to https://lit.dev/blog/2025-10-14-openjs/
17+
options[:attribution] = <<-HTML
18+
&copy; Google LLC<br>
19+
Licensed under the Creative Commons Attribution 3.0 Unported License.
20+
HTML
21+
22+
options[:fix_urls] = ->(url) do
23+
# A name without any extension is assumed to be a directory.
24+
# example.com/foobar -> example.com/foobar/
25+
url.sub! /(\/[-a-z0-9]+)([#?]|$)/i, '\1/\2'
26+
27+
url
28+
end
29+
30+
# The order of the filters is important.
31+
# The entries filter is applied to the raw (mostly) unmodified HTML.
32+
# The clean_html filter reformats the HTML to the a more appropriate markup for devdocs.
33+
html_filters.push 'lit/entries', 'lit/clean_html'
34+
35+
version '3' do
36+
self.release = '3.3.1'
37+
self.base_url = 'https://lit.dev/docs/'
38+
options[:skip_patterns] = [/v\d+\//]
39+
end
40+
41+
version '2' do
42+
self.release = '2.8.0'
43+
self.base_url = 'https://lit.dev/docs/v2/'
44+
end
45+
46+
version '1' do
47+
self.release = '1.0.1'
48+
self.base_url = 'https://lit.dev/docs/v1/'
49+
end
50+
51+
def get_latest_version(opts)
52+
get_npm_version('lit', opts)
53+
end
54+
end
55+
end

public/icons/docs/lit/16.png

399 Bytes
Loading
538 Bytes
Loading

public/icons/docs/lit/SOURCE

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
https://github.com/lit/lit.dev/blob/main/packages/lit-dev-content/site/images/flame.svg
2+
https://github.com/lit/lit.dev/blob/main/packages/lit-dev-content/site/images/icon.svg
3+
https://github.com/lit/lit/blob/main/packages/lit/logo.svg

0 commit comments

Comments
 (0)