diff --git a/package-lock.json b/package-lock.json index 4a6d18423..19ac56617 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6478,6 +6478,16 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", "dev": true }, + "node_modules/eleventy-plugin-git-commit-date": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/eleventy-plugin-git-commit-date/-/eleventy-plugin-git-commit-date-0.1.3.tgz", + "integrity": "sha512-dmdkGpMuRj8apWptC1QGqAsLHCguddFlfPjbjflGCqXdJ8cdhEjrzzvI/rL0XUvzCC4ETgGl9i/wDU6ujZ94gA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3" + } + }, "node_modules/emoji-regex": { "version": "10.4.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", @@ -13985,7 +13995,8 @@ "qr-creator": "^1.0.0" }, "devDependencies": { - "@wc-toolkit/jsx-types": "^1.3.0" + "@wc-toolkit/jsx-types": "^1.3.0", + "eleventy-plugin-git-commit-date": "^0.1.3" }, "engines": { "node": ">=14.17.0" @@ -14006,7 +14017,8 @@ "qr-creator": "^1.0.0" }, "devDependencies": { - "@wc-toolkit/jsx-types": "^1.3.0" + "@wc-toolkit/jsx-types": "^1.3.0", + "eleventy-plugin-git-commit-date": "^0.1.3" }, "engines": { "node": ">=14.17.0" diff --git a/packages/webawesome/docs/.eleventy.js b/packages/webawesome/docs/.eleventy.js index dd4cd119a..1e18bebe9 100644 --- a/packages/webawesome/docs/.eleventy.js +++ b/packages/webawesome/docs/.eleventy.js @@ -1,5 +1,6 @@ import { nanoid } from 'nanoid'; import { parse as HTMLParse } from 'node-html-parser'; +import { execFileSync } from 'node:child_process'; import * as fs from 'node:fs'; import * as path from 'node:path'; import { anchorHeadingsTransformer } from './_transformers/anchor-headings.js'; @@ -20,6 +21,7 @@ import { replaceTextPlugin } from './_plugins/replace-text.js'; import { searchPlugin } from './_plugins/search.js'; const __dirname = url.fileURLToPath(new URL('.', import.meta.url)); const isDev = process.argv.includes('--develop'); +const ignoreGit = process.env.ELEVENTY_IGNORE_GIT === 'true'; const passThroughExtensions = ['js', 'css', 'png', 'svg', 'jpg', 'mp4']; async function getPackageData() { @@ -58,6 +60,15 @@ export default async function (eleventyConfig) { if (updateComponentData) { allComponents = getComponents(); } + + // Invalidate last-modified cache for changed content files during watch + if (Array.isArray(changedFiles)) { + for (const file of changedFiles) { + if (/\.(md|njk|html)$/i.test(file)) { + lastModCache.delete(file); + } + } + } }); /** @@ -89,6 +100,79 @@ export default async function (eleventyConfig) { eleventyConfig.addFilter('stripExtension', string => path.parse(string + '').name); eleventyConfig.addFilter('stripPrefix', content => content.replace(/^wa-/, '')); eleventyConfig.addFilter('uniqueId', (_value, length = 8) => nanoid(length)); + // Returns last modified date as ISO 8601 (UTC, Z-suffixed) + // Fallback order: front matter override -> Git last commit date -> filesystem mtime -> now + // Caching: in-memory per inputPath during one build/dev session + // Override: pass a Date or string: {{ page.inputPath | gitLastModifiedISO(lastUpdated) }} + const lastModCache = new Map(); + let repoRoot = null; // lazily resolved; null => not resolved, undefined => failed + + function getLastModifiedISO(inputPath, overrideDate) { + if (overrideDate instanceof Date) { + return overrideDate.toISOString(); + } + if (typeof overrideDate === 'string' && overrideDate) { + const parsed = new Date(overrideDate); + if (!isNaN(parsed.getTime())) return parsed.toISOString(); + } + if (!inputPath) return new Date().toISOString(); + if (lastModCache.has(inputPath)) return lastModCache.get(inputPath); + + // Try Git (ISO via %cI). Use a repo-root-relative path for portability. + if (!ignoreGit) { + try { + if (repoRoot === null) { + try { + repoRoot = execFileSync('git', ['rev-parse', '--show-toplevel'], { + stdio: ['ignore', 'pipe', 'ignore'], + cwd: __dirname, + }) + .toString() + .trim(); + } catch (_) { + repoRoot = undefined; + } + } + + const gitPath = repoRoot ? path.relative(repoRoot, inputPath) : inputPath; + const args = ['log', '-1', '--format=%cI', '--follow', '--', gitPath]; + const result = execFileSync('git', args, { + stdio: ['ignore', 'pipe', 'ignore'], + cwd: repoRoot || path.dirname(inputPath), + }) + .toString() + .trim(); + if (result) { + const iso = new Date(result).toISOString(); + lastModCache.set(inputPath, iso); + return iso; + } + } catch (_) { + // continue to fs fallback + } + } + + // Fallback to filesystem mtime + try { + const stats = fs.statSync(inputPath); + const iso = new Date(stats.mtime).toISOString(); + lastModCache.set(inputPath, iso); + return iso; + } catch (_) { + const now = new Date().toISOString(); + lastModCache.set(inputPath, now); + return now; + } + } + + eleventyConfig.addFilter('gitLastModifiedISO', function (inputPath, overrideDate) { + return getLastModifiedISO(inputPath, overrideDate); + }); + + // Attach lastUpdatedISO to page data so templates can use {{ lastUpdatedISO }} directly + eleventyConfig.addGlobalData('eleventyComputed', { + lastUpdatedISO: data => getLastModifiedISO(data.page?.inputPath, data.lastUpdated), + }); // Trims whitespace and pipes from the start and end of a string. Useful for CEM types, which can be pipe-delimited. // With Prettier 3, this means a leading pipe will exist be present when the line wraps. eleventyConfig.addFilter('trimPipes', content => { diff --git a/packages/webawesome/docs/_includes/sidebar.njk b/packages/webawesome/docs/_includes/sidebar.njk index 12c91cfc3..8f6a0dd04 100644 --- a/packages/webawesome/docs/_includes/sidebar.njk +++ b/packages/webawesome/docs/_includes/sidebar.njk @@ -22,6 +22,9 @@
  • Browser Support
  • Contributing
  • Changelog
  • +
  • Free License
  • +
  • Pro License
  • +
  • Terms of Service
  • Visual Tests
  • diff --git a/packages/webawesome/docs/assets/styles/utils.css b/packages/webawesome/docs/assets/styles/utils.css index 6364f9002..9e4e87951 100644 --- a/packages/webawesome/docs/assets/styles/utils.css +++ b/packages/webawesome/docs/assets/styles/utils.css @@ -128,6 +128,18 @@ } } + ul, + ol { + &.appearance-plain { + list-style: none; + padding-inline-start: 0; + + li { + padding-inline-start: 0; + } + } + } + /* increasing visual size of icons in certain contexts (such as in plain buttons) */ wa-icon.icon-embiggen { font-size: round(1.125em, 1px); diff --git a/packages/webawesome/docs/docs/resources/changelog.md b/packages/webawesome/docs/docs/resources/changelog.md index 0dd4ba572..6549a8d07 100644 --- a/packages/webawesome/docs/docs/resources/changelog.md +++ b/packages/webawesome/docs/docs/resources/changelog.md @@ -4,6 +4,8 @@ description: Changes to each version of the project are documented here. layout: page-outline --- +

    Last updated:

    + Web Awesome follows [Semantic Versioning](https://semver.org/). Breaking changes in components with the Stable badge will not be accepted until the next major version. As such, all contributions must consider the project's roadmap and take this into consideration. Features that are deemed no longer necessary will be deprecated but not removed. Components with the Experimental badge should not be used in production. They are made available as release candidates for development and testing purposes. As such, changes to experimental components will not be subject to semantic versioning. diff --git a/packages/webawesome/package.json b/packages/webawesome/package.json index d2d40108e..b8da85372 100644 --- a/packages/webawesome/package.json +++ b/packages/webawesome/package.json @@ -89,6 +89,7 @@ ] }, "devDependencies": { - "@wc-toolkit/jsx-types": "^1.3.0" + "@wc-toolkit/jsx-types": "^1.3.0", + "eleventy-plugin-git-commit-date": "^0.1.3" } }