new-docs
Cory LaViska 2023-06-06 17:02:15 -04:00
rodzic aa48566aef
commit 8c44eb75d5
8 zmienionych plików z 109 dodań i 281 usunięć

8
.gitignore vendored
Wyświetl plik

@ -1,9 +1,7 @@
.DS_Store
.cache
_site
docs/dist
docs/search.json
docs/assets/images/sprite.svg
.cache
.DS_Store
dist
docs/assets/images/sprite.svg
node_modules
src/react

Wyświetl plik

@ -26,7 +26,6 @@ module.exports = function (doc, options) {
const adjacentPre = pre.nextElementSibling?.tagName.toLowerCase() === 'pre' ? pre.nextElementSibling : null;
const reactCode = adjacentPre?.querySelector('code[class$="react"]');
const sourceGroupId = `code-preview-source-group-${count}`;
const toggleId = `code-preview-toggle-${count}`;
const isExpanded = code.getAttribute('class').includes(':expanded');
const noCodePen = code.getAttribute('class').includes(':no-codepen');
@ -137,17 +136,3 @@ module.exports = function (doc, options) {
return doc;
};
function getAdjacentExample(name, pre) {
let currentPre = pre.nextElementSibling;
while (currentPre?.tagName.toLowerCase() === 'pre') {
if (currentPre?.getAttribute('class').indexOf(name) > -1) {
return currentPre;
}
currentPre = currentPre.nextElementSibling;
}
return null;
}

Wyświetl plik

@ -9,7 +9,7 @@ module.exports = function (doc, options) {
className: 'external-link', // the class name to add to links
noopener: true, // sets rel="noopener"
noreferrer: true, // sets rel="noreferrer"
ignore: link => false, // callback function to filter links that should be ignored
ignore: () => false, // callback function to filter links that should be ignored
within: 'body', // element that contains the target links
target: '', // sets the target attribute
...options

Wyświetl plik

@ -1,14 +1,14 @@
const smartquotes = require('smartquotes');
smartquotes.replacements.push([/\-\-\-/g, '\u2014']); // em dash
smartquotes.replacements.push([/\-\-/g, '\u2013']); // en dash
smartquotes.replacements.push([/---/g, '\u2014']); // em dash
smartquotes.replacements.push([/--/g, '\u2013']); // en dash
smartquotes.replacements.push([/\.\.\./g, '\u2026']); // ellipsis
smartquotes.replacements.push([/\(c\)/gi, '\u00A9']); // copyright
smartquotes.replacements.push([/\(r\)/gi, '\u00AE']); // registered trademark
smartquotes.replacements.push([/\?!/g, '\u2048']); // ?!
smartquotes.replacements.push([/!!/g, '\u203C']); // !!
smartquotes.replacements.push([/\?\?/g, '\u2047']); // ??
smartquotes.replacements.push([/([0-9]\s?)\-(\s?[0-9])/g, '$1\u2013$2']); // number ranges use en dash
smartquotes.replacements.push([/([0-9]\s?)-(\s?[0-9])/g, '$1\u2013$2']); // number ranges use en dash
/**
* Improves typography by adding smart quotes and similar corrections within the specified element(s).

Wyświetl plik

@ -21,12 +21,6 @@ const assetsDir = 'assets';
const allComponents = getAllComponents();
let hasBuiltSearchIndex = false;
function benchmark (callback) {
const time = performance.now()
callback()
return performance.now() - time
}
module.exports = function (eleventyConfig) {
//
// Global data
@ -110,19 +104,6 @@ module.exports = function (eleventyConfig) {
//
// Transforms
//
let transformTimers = {
activeLinks: 0,
anchorHeadings: 0,
tableOfContents: 0,
codePreviews: 0,
externalLinks: 0,
highlightCodeBlock: 0,
scrollingTables: 0,
copyCodeButtons: 0,
typography: 0,
prettier: 0,
}
eleventyConfig.addTransform('html-transform', function (content) {
// Parse the template and get a Document object
const doc = new JSDOM(content, {
@ -132,57 +113,28 @@ module.exports = function (eleventyConfig) {
}).window.document;
// DOM transforms
transformTimers.activeLinks += benchmark(() => {
activeLinks(doc, { pathname: this.page.url });
})
transformTimers.anchorHeadings += benchmark(() => {
anchorHeadings(doc, {
within: '#content .content__body',
levels: ['h2', 'h3', 'h4', 'h5']
});
})
transformTimers.tableOfContents += benchmark(() => {
tableOfContents(doc, {
levels: ['h2', 'h3'],
container: '#content .content__toc > ul',
within: '#content .content__body'
});
})
transformTimers.codePreviews += benchmark(() => {
codePreviews(doc);
})
transformTimers.externalLinks += benchmark(() => {
externalLinks(doc, { target: '_blank' });
})
transformTimers.highlightCodeBlock += benchmark(() => {
highlightCodeBlocks(doc);
})
transformTimers.scrollingTables += benchmark(() => {
scrollingTables(doc);
})
transformTimers.copyCodeButtons += benchmark(() => {
copyCodeButtons(doc); // must be after codePreviews + highlightCodeBlocks
})
transformTimers.typography += benchmark(() => {
typography(doc, '#content');
})
activeLinks(doc, { pathname: this.page.url });
anchorHeadings(doc, {
within: '#content .content__body',
levels: ['h2', 'h3', 'h4', 'h5']
});
tableOfContents(doc, {
levels: ['h2', 'h3'],
container: '#content .content__toc > ul',
within: '#content .content__body'
});
codePreviews(doc);
externalLinks(doc, { target: '_blank' });
highlightCodeBlocks(doc);
scrollingTables(doc);
copyCodeButtons(doc); // must be after codePreviews + highlightCodeBlocks
typography(doc, '#content');
// Serialize the Document object to an HTML string and prepend the doctype
content = `<!DOCTYPE html>\n${doc.documentElement.outerHTML}`;
// String transforms
transformTimers.prettier += benchmark(() => {
content = prettier(content);
})
content = prettier(content);
return content;
});
@ -190,7 +142,7 @@ module.exports = function (eleventyConfig) {
//
// Build a search index
//
eleventyConfig.on('eleventy.after', async ({ results }) => {
eleventyConfig.on('eleventy.after', ({ results }) => {
// We only want to build the search index on the first run so all pages get indexed.
if (hasBuiltSearchIndex) {
return;
@ -239,13 +191,6 @@ module.exports = function (eleventyConfig) {
fs.writeFileSync(searchIndexFilename, JSON.stringify({ searchIndex, map }), 'utf-8');
hasBuiltSearchIndex = true;
let totalTime = 0
Object.entries(transformTimers).forEach(([k,v]) => {
const rounded = Math.ceil(v)
console.log(k + ": " + rounded + "ms")
totalTime += rounded
})
console.log("Total transform time: " + totalTime + "ms")
});
//
@ -254,9 +199,7 @@ module.exports = function (eleventyConfig) {
eleventyConfig.setServerOptions({
domDiff: false, // disable dom diffing so custom elements don't break on reload,
port: 4000, // if port 4000 is taken, 11ty will use the next one available
watch: [
"dist/**/*.*"
] // additional files to watch that will trigger server updates (array of paths or globs)
watch: ['dist/**/*'] // additional files to watch that will trigger server updates (array of paths or globs)
});
//

Wyświetl plik

@ -43,7 +43,7 @@
},
"scripts": {
"start": "node scripts/build.js --bundle --serve",
"build": "node scripts/build.js --bundle --types --copydir \"docs/dist\"",
"build": "node scripts/build.js --bundle --types --copydir \"_site/dist\"",
"verify": "npm run prettier:check && npm run lint && npm run build && npm run test",
"prepublishOnly": "npm run verify",
"prettier": "prettier --write --loglevel warn .",

Wyświetl plik

@ -1,23 +1,30 @@
import { spawn, execSync } from 'child_process';
import getPort, { portNumbers } from 'get-port';
import { globby } from 'globby';
import { deleteSync } from 'del';
import { globby } from 'globby';
import { execSync, spawn } from 'child_process';
import browserSync from 'browser-sync';
import chalk from 'chalk';
import chokidar from 'chokidar';
import commandLineArgs from 'command-line-args';
import copy from 'recursive-copy';
import esbuild from 'esbuild';
import fs from 'fs';
import copy from 'recursive-copy';
import getPort, { portNumbers } from 'get-port';
const abortController = new AbortController();
const abortSignal = abortController.signal;
function buildTheDocs({ watch = false }) {
function buildTheDocs(watch = false) {
deleteSync('./_site');
if (!watch) {
return execSync('npx @11ty/eleventy', { stdio: 'inherit', cwd: 'docs' });
return execSync('npx @11ty/eleventy --quiet', { stdio: 'inherit', cwd: 'docs' });
}
return spawn('npx', ['@11ty/eleventy', '--watch', '--incremental'], { stdio: 'inherit', cwd: 'docs', signal: abortSignal });
return spawn('npx', ['@11ty/eleventy', '--watch', '--incremental', '--quiet'], {
stdio: 'inherit',
cwd: 'docs',
signal: abortSignal
});
}
const { bundle, copydir, dir, serve, types } = commandLineArgs([
@ -33,7 +40,7 @@ const outdir = dir;
deleteSync(outdir);
fs.mkdirSync(outdir, { recursive: true });
;(async () => {
(async () => {
try {
execSync(`node scripts/make-metadata.js --outdir "${outdir}"`, { stdio: 'inherit' });
execSync(`node scripts/make-react.js --outdir "${outdir}"`, { stdio: 'inherit' });
@ -104,80 +111,84 @@ fs.mkdirSync(outdir, { recursive: true });
copy(outdir, copydir);
}
console.log(chalk.green(`The build has been generated at ${outdir} 📦\n`));
if (serve) {
// Dev
buildTheDocs({ watch: true });
// Build it with --watch and --incremental
buildTheDocs(true);
const bs = browserSync.create();
const port = await getPort({
port: portNumbers(4000, 4999)
});
// Wait for the search index to appear before launching the browser. This file is generated during eleventy.after,
// so it's usually the last one to appear.
const watcher = chokidar.watch('./_site', { persistent: true });
watcher.on('add', async filename => {
if (filename.endsWith('search.json')) {
watcher.close();
const browserSyncConfig = {
startPath: '/',
port,
logLevel: 'silent',
logPrefix: '[shoelace]',
logFileChanges: true,
notify: false,
single: true,
ghostMode: false,
server: {
baseDir: '_site',
routes: {
'/dist': './dist'
}
const bs = browserSync.create();
const port = await getPort({
port: portNumbers(4000, 4999)
});
const browserSyncConfig = {
startPath: '/',
port,
logLevel: 'silent',
logPrefix: '[shoelace]',
logFileChanges: true,
notify: false,
single: true,
ghostMode: false,
server: {
baseDir: '_site',
routes: {
'/dist': './dist'
}
}
};
// Launch browser sync
bs.init(browserSyncConfig, () => {
const url = `http://localhost:${port}`;
console.log(chalk.cyan(`Launched the Shoelace dev server at ${url} 🥾\n`));
});
// Rebuild and reload when source files change
bs.watch(['src/**/!(*.test).*']).on('change', async filename => {
buildResult
// Rebuild and reload
.rebuild()
.then(() => {
// Rebuild stylesheets when a theme file changes
if (/^src\/themes/.test(filename)) {
execSync(`node scripts/make-themes.js --outdir "${outdir}"`, { stdio: 'inherit' });
}
})
.then(() => {
// Skip metadata when styles are changed
if (/(\.css|\.styles\.ts)$/.test(filename)) {
return;
}
execSync(`node scripts/make-metadata.js --outdir "${outdir}"`, { stdio: 'inherit' });
})
.then(() => bs.reload())
.catch(err => console.error(chalk.red(err)));
});
// Reload without rebuilding when the docs change
bs.watch(['_site/**/*.*']).on('change', () => {
bs.reload();
});
}
};
// Launch browser sync
bs.init(browserSyncConfig, () => {
const url = `http://localhost:${port}`;
console.log(chalk.cyan(`Launched the Shoelace dev server at ${url} 🥾\n`));
});
}
// Rebuild and reload when source files change
bs.watch(['src/**/!(*.test).*']).on('change', async filename => {
console.log(`Source file changed - ${filename}`);
buildResult
// Rebuild and reload
.rebuild()
.then(() => {
// Rebuild stylesheets when a theme file changes
if (/^src\/themes/.test(filename)) {
execSync(`node scripts/make-themes.js --outdir "${outdir}"`, { stdio: 'inherit' });
}
})
.then(() => {
// Skip metadata when styles are changed
if (/(\.css|\.styles\.ts)$/.test(filename)) {
return;
}
execSync(`node scripts/make-metadata.js --outdir "${outdir}"`, { stdio: 'inherit' });
})
.then(() => bs.reload())
.catch(err => console.error(chalk.red(err)));
});
// Reload without rebuilding when the docs change
bs.watch(['_site/**/*.*']).on('change', async (filename) => {
console.log(`File changed - ${filename}`);
// TODO: I tried writing a debounce here, but it wasnt working -.-
bs.reload()
});
} else {
// Prod build
// Prod build
if (!serve) {
buildTheDocs();
}
// Cleanup on exit
process.on('SIGTERM', () => {
buildResult.rebuild.dispose()
buildResult.rebuild.dispose();
abortController.abort(); // Stops the child process
});
})();

Wyświetl plik

@ -1,109 +0,0 @@
import commandLineArgs from 'command-line-args';
import fs from 'fs';
import path from 'path';
import { globby } from 'globby';
import lunr from 'lunr';
import { getAllComponents } from './shared.js';
const { outdir } = commandLineArgs({ name: 'outdir', type: String });
const metadata = JSON.parse(fs.readFileSync(path.join(outdir, 'custom-elements.json'), 'utf8'));
console.log('Generating search index for documentation');
;(async () => {
function getHeadings(markdown, maxLevel = 6) {
const headings = [];
const lines = markdown.split('\n');
lines.forEach(line => {
if (line.startsWith('#')) {
const level = line.match(/^(#+)/)[0].length;
const content = line.replace(/^#+/, '');
if (level <= maxLevel) {
headings.push({ level, content });
}
}
});
return headings;
}
function getMembers(markdown) {
const members = [];
const headers = markdown.match(/\[component-header:([a-z-]+)\]/g);
if (!headers) {
return '';
}
headers.forEach(header => {
const tagName = header.match(/\[component-header:([a-z-]+)\]/)[1];
const component = getAllComponents(metadata).find(component => component.tagName === tagName);
if (component) {
const fields = ['members', 'cssProperties', 'cssParts', 'slots', 'events'];
fields.forEach(field => {
if (component[field]) {
component[field].forEach(entry => {
if (entry.name) members.push(entry.name);
if (entry.description) members.push(entry.description);
if (entry.attribute) members.push(entry.attribute);
});
}
});
}
});
return members.join(' ');
}
const files = await globby('./docs/**/*.md');
const map = {};
const searchIndex = lunr(function () {
// The search index uses these field names extensively, so shortening them can save some serious bytes. The initial
// index file went from 468 KB => 401 KB by using single-character names!
this.ref('id'); // id
this.field('t', { boost: 10 }); // title
this.field('h', { boost: 5 }); // headings
this.field('m', { boost: 2 }); // members (props, methods, events, etc.)
this.field('c'); // content
files.forEach((file, index) => {
const relativePath = path.relative('./docs', file).replace(/\\/g, '/');
const relativePathNoExtension = relativePath.split('.').slice(0, -1).join('.');
const url = relativePath.replace(/\.md$/, '');
const filename = path.basename(file);
// Ignore certain directories and files
if (
relativePath.startsWith('assets/') ||
relativePath.startsWith('dist/') ||
filename === '_sidebar.md' ||
filename === '404.md'
) {
return false;
}
const content = fs.readFileSync(file, 'utf8');
const allHeadings = getHeadings(content, 4);
const title = allHeadings.find(heading => heading.level === 1)?.content || '';
const headings = allHeadings
.filter(heading => heading.level > 1)
.map(heading => heading.content)
.concat([relativePathNoExtension])
.join(' ');
const members = getMembers(content);
// Remove markdown code fields from search results. This seems to make search results a bit more accurate and
// reduces search.json from ~679 KB to ~455 KB.
const prunedContent = content.replace(/```(.*?)```/gs, '');
this.add({ id: index, t: title, h: headings, m: members, c: prunedContent });
map[index] = { title, url };
});
});
fs.writeFileSync('./docs/search.json', JSON.stringify({ searchIndex, map }), 'utf8');
})();