Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,30 @@ Sometimes we want to include something that is not on lit.dev in the search inde
}
```

### How to associate keywords to a search item

Sometimes the text of a search item does not include certain keywords you'd like to
associate with that URL. e.g. If someone searches for a react term like `componentDidMount`, or `onMounted` in Vue,
the text for `connectedCallback` did not include `componentDidMount` or `onMounted` in the text. To associate that term with `connectedCallback` you would modify the `packages/lit-dev-content/site/_data/keywords.json` file to include associate keywords with a set of URLs like so:

```json
{
"keywords": [
{
"keywords": ["componentDidMount", "onMounted"],
"urls": [
"/docs/components/lifecycle/#connectedcallback",
"/articles/lit-cheat-sheet/#connectedcallback"
]
}
]
}
```

This will add some more context for algolia to search on when a user searches
for `componentDidMount` or `onMounted`. The keywords will not be rendered in the
UI, but the results should be.

### How to administer the Algolia search index

To administer the search index to add, remove, delete, enable Algolia features, etc., you must be a part of the Algolia team which has limited space and is currently limited to the Lit team. Contact Elliott on the Lit team if you need access. We do not use Algolia Analytics as we have not had the time to go through Google's privacy review + privacy policy / cookie process for storing user data in Algolia.
Expand Down
22 changes: 22 additions & 0 deletions packages/lit-dev-content/site/_data/externalSearchData.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,27 @@
"tag": "other"
},
"isExternal": true
},
{
"relativeUrl": "https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/exportparts",
"title": "exportparts",
"heading": "",
"text": "The exportparts global attribute allows you to select and style elements existing in nested shadow trees, by exporting their part names.",
"docType": {
"type": "MDN",
"tag": "other"
},
"isExternal": true
},
{
"relativeUrl": "https://www.npmjs.com/package/postcss-lit",
"title": "postcss-lit",
"heading": "",
"text": "A PostCSS and stylelint custom syntax for parsing CSS inside lit templates. You must specify all tailwind directives you intend to use in your CSS, otherwise their replacement CSS will be incorrectly appended to the end of the document. For example, in the code above, @tailwind base and @tailwind utilities were specified to make text-xs available. Without them, the code would not build.",
"docType": {
"type": "npm",
"tag": "other"
},
"isExternal": true
}
]
96 changes: 96 additions & 0 deletions packages/lit-dev-content/site/_data/searchKeywords.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/**
* @typedef {{
* urls: !Array<string>,
* keywords: !Array<string>
* }} KeywordRecord
*
* @typedef {{
* keywords: !Array<!KeywordRecord>
* }} KeywordModifiers
*/

/** @type {KeywordModifiers} */
const keywords = {
keywords: [
{
keywords: ['componentDidMount', 'onMounted', 'ngOnInit'],
urls: [
'/docs/components/lifecycle/#connectedcallback',
'/articles/lit-cheat-sheet/#connectedcallback'
]
},
{
keywords: [
'ngAfterViewInit',
'afterFirstRender'
],
urls: [
'/docs/components/lifecycle/#firstupdated',
'/articles/lit-cheat-sheet/#firstupdated'
]
},
{
keywords: [
'componentDidUpdate',
'afterUpdate'
],
urls: [
'/docs/components/lifecycle/#updated',
'/articles/lit-cheat-sheet/#updated'
]
},
{
keywords: [
'shouldComponentUpdate',
'updateCheck'
],
urls: [
'/docs/components/lifecycle/#shouldupdate'
]
},
{
keywords: [
'componentWillUnmount',
'onUnmounted',
'ngOnDestroy'
],
urls: [
'/docs/components/lifecycle/#disconnectedcallback',
'/articles/lit-cheat-sheet/#disconnectedcallback'
]
},
{
keywords: [
'renderToPipeableStream',
'renderToReadableStream',
'renderToStaticMarkup',
'renderToString'
],
urls: [
'/docs/ssr/server-usage/#handling-renderresults'
]
},
{
keywords: ['tsx'],
urls: [
'/docs/frameworks/react/#createcomponent',
'https://www.youtube.com/watch?v=agBn1LW6dbM',
]
},
{
keywords: ['exportparts'],
urls: [
'https://www.youtube.com/watch?v=Xt7blcyuw5s',
]
}
]
};

/**
* 11ty data JS loader.
*
* @returns {!KeywordModifiers} The keywords data for 11ty search index rendered in search-modifiers/keywords.
*/
module.exports = async () => {
return keywords;
};
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
permalink: external-search-data/data.json
permalink: search-modifiers/external-data.json
---

{% if not env.DEV %}{{ externalSearchData | dump | safe }}{% endif %}
7 changes: 7 additions & 0 deletions packages/lit-dev-content/site/search-modifiers/keywords.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
permalink: search-modifiers/keywords.json
---

{% if not env.DEV %}
{{ searchKeywords | dump | safe }}
{% endif %}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
permalink: external-search-data/videos.json
permalink: search-modifiers/videos.json
---

{% if not env.DEV %}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export async function indexExternalData(
// Path of the external data index.
const EXTERNAL_DATA_INDEX_PATH = path.resolve(
__dirname,
`../../../../lit-dev-content/${outputDir}/external-search-data/data.json`
`../../../../lit-dev-content/${outputDir}/search-modifiers/external-data.json`
);

const fileContents = await fs.readFile(EXTERNAL_DATA_INDEX_PATH, 'utf-8');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export async function indexVideos(outputDir: '_dev' | '_site', idOffset = 0) {
// Path of the video index.
const VIDEO_INDEX_PATH = path.resolve(
__dirname,
`../../../../lit-dev-content/${outputDir}/external-search-data/videos.json`
`../../../../lit-dev-content/${outputDir}/search-modifiers/videos.json`
);

const fileContents = await fs.readFile(VIDEO_INDEX_PATH, 'utf-8');
Expand Down
73 changes: 73 additions & 0 deletions packages/lit-dev-tools-cjs/src/search/indexers/keywords.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: BSD-3-Clause
*/

import * as fs from 'fs/promises';
import * as path from 'path';
import type {UserFacingPageData} from '../plugin';

interface KeywordRecord {
urls: string[];
keywords: string[];
}

interface KeywordModifiers {
keywords: KeywordRecord[];
}

/**
* Adds keyword metadata to pages in the search index based on keyword modifiers defined in a JSON file.
* Only processes keywords for production builds (when outputDir is '_site').
*
* @param outputDir - The output directory for the build ('_dev' or '_site'). Keywords are only added for '_site' builds.
* @param index - Array of page data objects to be enhanced with keywords
* @returns The modified index array with keywords added to relevant pages. Returns empty array for dev builds.
*/
export async function addKeywords(
outputDir: '_dev' | '_site',
index: UserFacingPageData[]
) {
if (outputDir === '_dev') {
return index;
}

// Path to the keyword modifiers JSON file.
const KEYWORD_MODIFIERS_PATH = path.resolve(
__dirname,
`../../../../lit-dev-content/${outputDir}/search-modifiers/keywords.json`
);

const fileContents = await fs.readFile(KEYWORD_MODIFIERS_PATH, 'utf-8');
const data = JSON.parse(fileContents) as KeywordModifiers;

const keywordMap = new Map<string, Set<string>>();

// Create a map of urls to keywords associated with that url.
for (const keywordRecord of data.keywords) {
const keywords = new Set(keywordRecord.keywords);

for (const url of keywordRecord.urls) {
let keywordsForURL = keywordMap.get(url);
if (!keywordsForURL) {
keywordsForURL = new Set<string>();
keywordMap.set(url, keywordsForURL);
}

for (const keyword of keywords) {
keywordsForURL.add(keyword);
}
}
}

// Add keywords to the index to each url that has keywords associated with it.
for (const page of index) {
const keywords = keywordMap.get(page.relativeUrl);
if (keywords) {
page.keywords = Array.from(keywords);
}
}

return index;
}
4 changes: 4 additions & 0 deletions packages/lit-dev-tools-cjs/src/search/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
indexVideos,
indexExternalData,
} from './indexers/index.js';
import {addKeywords} from './indexers/keywords.js';

/**
* Generic that describes the type of document.
Expand Down Expand Up @@ -46,6 +47,7 @@ export interface UserFacingPageData {
parentID?: string;
isExternal?: boolean;
docType: DocTypes;
keywords?: string[];
}

/**
Expand Down Expand Up @@ -92,5 +94,7 @@ export async function createSearchIndex(outputDir: '_dev' | '_site') {
...externalSearchData,
];

await addKeywords(outputDir, searchIndex);

fs.writeFileSync(OUT_PATH, JSON.stringify(searchIndex));
}
Loading