diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8a3388310..219f1dce5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -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. diff --git a/packages/lit-dev-content/site/_data/externalSearchData.json b/packages/lit-dev-content/site/_data/externalSearchData.json index 95d378c8c..fd7361caa 100644 --- a/packages/lit-dev-content/site/_data/externalSearchData.json +++ b/packages/lit-dev-content/site/_data/externalSearchData.json @@ -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 } ] diff --git a/packages/lit-dev-content/site/_data/searchKeywords.js b/packages/lit-dev-content/site/_data/searchKeywords.js new file mode 100644 index 000000000..91e8ddbf0 --- /dev/null +++ b/packages/lit-dev-content/site/_data/searchKeywords.js @@ -0,0 +1,96 @@ +/** + * @typedef {{ + * urls: !Array, + * keywords: !Array + * }} KeywordRecord + * + * @typedef {{ + * keywords: !Array + * }} 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; +}; \ No newline at end of file diff --git a/packages/lit-dev-content/site/external-search-data/data.html b/packages/lit-dev-content/site/search-modifiers/external-data.html similarity index 62% rename from packages/lit-dev-content/site/external-search-data/data.html rename to packages/lit-dev-content/site/search-modifiers/external-data.html index 5f3045f65..455e5f04c 100644 --- a/packages/lit-dev-content/site/external-search-data/data.html +++ b/packages/lit-dev-content/site/search-modifiers/external-data.html @@ -1,5 +1,5 @@ --- -permalink: external-search-data/data.json +permalink: search-modifiers/external-data.json --- {% if not env.DEV %}{{ externalSearchData | dump | safe }}{% endif %} diff --git a/packages/lit-dev-content/site/search-modifiers/keywords.html b/packages/lit-dev-content/site/search-modifiers/keywords.html new file mode 100644 index 000000000..be52b7aa2 --- /dev/null +++ b/packages/lit-dev-content/site/search-modifiers/keywords.html @@ -0,0 +1,7 @@ +--- +permalink: search-modifiers/keywords.json +--- + +{% if not env.DEV %} +{{ searchKeywords | dump | safe }} +{% endif %} diff --git a/packages/lit-dev-content/site/external-search-data/videos.html b/packages/lit-dev-content/site/search-modifiers/videos.html similarity index 68% rename from packages/lit-dev-content/site/external-search-data/videos.html rename to packages/lit-dev-content/site/search-modifiers/videos.html index 9d103d65b..c6404420e 100644 --- a/packages/lit-dev-content/site/external-search-data/videos.html +++ b/packages/lit-dev-content/site/search-modifiers/videos.html @@ -1,5 +1,5 @@ --- -permalink: external-search-data/videos.json +permalink: search-modifiers/videos.json --- {% if not env.DEV %} diff --git a/packages/lit-dev-tools-cjs/src/search/indexers/index-external-data.ts b/packages/lit-dev-tools-cjs/src/search/indexers/index-external-data.ts index 432835300..887fb9840 100644 --- a/packages/lit-dev-tools-cjs/src/search/indexers/index-external-data.ts +++ b/packages/lit-dev-tools-cjs/src/search/indexers/index-external-data.ts @@ -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'); diff --git a/packages/lit-dev-tools-cjs/src/search/indexers/index-videos.ts b/packages/lit-dev-tools-cjs/src/search/indexers/index-videos.ts index 750c0fbf9..9e0d89eca 100644 --- a/packages/lit-dev-tools-cjs/src/search/indexers/index-videos.ts +++ b/packages/lit-dev-tools-cjs/src/search/indexers/index-videos.ts @@ -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'); diff --git a/packages/lit-dev-tools-cjs/src/search/indexers/keywords.ts b/packages/lit-dev-tools-cjs/src/search/indexers/keywords.ts new file mode 100644 index 000000000..da9cb6c5c --- /dev/null +++ b/packages/lit-dev-tools-cjs/src/search/indexers/keywords.ts @@ -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>(); + + // 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(); + 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; +} diff --git a/packages/lit-dev-tools-cjs/src/search/plugin.ts b/packages/lit-dev-tools-cjs/src/search/plugin.ts index bf5825d14..a98e1a226 100644 --- a/packages/lit-dev-tools-cjs/src/search/plugin.ts +++ b/packages/lit-dev-tools-cjs/src/search/plugin.ts @@ -13,6 +13,7 @@ import { indexVideos, indexExternalData, } from './indexers/index.js'; +import {addKeywords} from './indexers/keywords.js'; /** * Generic that describes the type of document. @@ -46,6 +47,7 @@ export interface UserFacingPageData { parentID?: string; isExternal?: boolean; docType: DocTypes; + keywords?: string[]; } /** @@ -92,5 +94,7 @@ export async function createSearchIndex(outputDir: '_dev' | '_site') { ...externalSearchData, ]; + await addKeywords(outputDir, searchIndex); + fs.writeFileSync(OUT_PATH, JSON.stringify(searchIndex)); }