perf: lean bootstrap javascript (#1734)

This commit is contained in:
Cotes Chung 2024-05-11 10:29:14 +08:00 committed by GitHub
parent c17fba44f5
commit ddb48eda52
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 89 additions and 55 deletions

View file

@ -6,7 +6,6 @@ webfonts: /assets/lib/fonts/main.css
bootstrap: bootstrap:
css: /assets/lib/bootstrap/bootstrap.min.css css: /assets/lib/bootstrap/bootstrap.min.css
js: /assets/lib/bootstrap/bootstrap.bundle.min.js
toc: toc:
css: /assets/lib/tocbot/tocbot.min.css css: /assets/lib/tocbot/tocbot.min.css

View file

@ -21,7 +21,6 @@ webfonts: https://fonts.googleapis.com/css2?family=Lato:wght@300;400&family=Sour
bootstrap: bootstrap:
css: https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css css: https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css
js: https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js
toc: toc:
css: https://cdn.jsdelivr.net/npm/tocbot@4.25.0/dist/tocbot.min.css css: https://cdn.jsdelivr.net/npm/tocbot@4.25.0/dist/tocbot.min.css

View file

@ -2,9 +2,7 @@
<!-- commons --> <!-- commons -->
{%- capture urls -%} {% assign urls = site.data.origin[type].search.js %}
{{ site.data.origin[type].bootstrap.js }},{{ site.data.origin[type].search.js }}
{%- endcapture -%}
<!-- layout specified --> <!-- layout specified -->

View file

@ -1,6 +1,9 @@
/** /**
* Tab 'Categories' expand/close effect. * Tab 'Categories' expand/close effect.
*/ */
import 'bootstrap/js/src/collapse.js';
const childPrefix = 'l_'; const childPrefix = 'l_';
const parentPrefix = 'h_'; const parentPrefix = 'h_';
const children = document.getElementsByClassName('collapse'); const children = document.getElementsByClassName('collapse');

View file

@ -2,10 +2,11 @@
* Clipboard functions * Clipboard functions
* *
* Dependencies: * Dependencies:
* - popper.js (https://github.com/popperjs/popper-core) * clipboard.js (https://github.com/zenorocha/clipboard.js)
* - clipboard.js (https://github.com/zenorocha/clipboard.js)
*/ */
import Tooltip from 'bootstrap/js/src/tooltip';
const clipboardSelector = '.code-header>button'; const clipboardSelector = '.code-header>button';
const ICON_DEFAULT = 'far fa-clipboard'; const ICON_DEFAULT = 'far fa-clipboard';
@ -38,11 +39,11 @@ function unlock(node) {
function showTooltip(btn) { function showTooltip(btn) {
const succeedTitle = btn.getAttribute(ATTR_TITLE_SUCCEED); const succeedTitle = btn.getAttribute(ATTR_TITLE_SUCCEED);
btn.setAttribute(ATTR_TITLE_ORIGIN, succeedTitle); btn.setAttribute(ATTR_TITLE_ORIGIN, succeedTitle);
bootstrap.Tooltip.getInstance(btn).show(); Tooltip.getInstance(btn).show();
} }
function hideTooltip(btn) { function hideTooltip(btn) {
bootstrap.Tooltip.getInstance(btn).hide(); Tooltip.getInstance(btn).hide();
btn.removeAttribute(ATTR_TITLE_ORIGIN); btn.removeAttribute(ATTR_TITLE_ORIGIN);
} }
@ -56,7 +57,7 @@ function resumeIcon(btn) {
icon.setAttribute('class', ICON_DEFAULT); icon.setAttribute('class', ICON_DEFAULT);
} }
export function initClipboard() { function setCodeClipboard() {
const clipboardList = document.querySelectorAll(clipboardSelector); const clipboardList = document.querySelectorAll(clipboardSelector);
if (clipboardList.length === 0) { if (clipboardList.length === 0) {
@ -73,7 +74,7 @@ export function initClipboard() {
[...clipboardList].map( [...clipboardList].map(
(elem) => (elem) =>
new bootstrap.Tooltip(elem, { new Tooltip(elem, {
placement: 'left' placement: 'left'
}) })
); );
@ -97,11 +98,15 @@ export function initClipboard() {
unlock(trigger); unlock(trigger);
}, TIMEOUT); }, TIMEOUT);
}); });
}
/* --- Post link sharing --- */ function setLinkClipboard() {
const btnCopyLink = document.getElementById('copy-link'); const btnCopyLink = document.getElementById('copy-link');
if (btnCopyLink === null) {
return;
}
btnCopyLink.addEventListener('click', (e) => { btnCopyLink.addEventListener('click', (e) => {
const target = e.target; const target = e.target;
@ -116,7 +121,7 @@ export function initClipboard() {
// Switch tooltip title // Switch tooltip title
target.setAttribute(ATTR_TITLE_ORIGIN, succeedTitle); target.setAttribute(ATTR_TITLE_ORIGIN, succeedTitle);
bootstrap.Tooltip.getInstance(target).show(); Tooltip.getInstance(target).show();
lock(target); lock(target);
@ -128,6 +133,11 @@ export function initClipboard() {
}); });
btnCopyLink.addEventListener('mouseleave', (e) => { btnCopyLink.addEventListener('mouseleave', (e) => {
bootstrap.Tooltip.getInstance(e.target).hide(); Tooltip.getInstance(e.target).hide();
}); });
} }
export function initClipboard() {
setCodeClipboard();
setLinkClipboard();
}

View file

@ -1,12 +1,11 @@
/** import Tooltip from 'bootstrap/js/src/tooltip';
* Initial Bootstrap Tooltip.
*/
export function loadTooptip() { export function loadTooptip() {
const tooltipTriggerList = document.querySelectorAll( const tooltipTriggerList = document.querySelectorAll(
'[data-bs-toggle="tooltip"]' '[data-bs-toggle="tooltip"]'
); );
[...tooltipTriggerList].map( [...tooltipTriggerList].map(
(tooltipTriggerEl) => new bootstrap.Tooltip(tooltipTriggerEl) (tooltipTriggerEl) => new Tooltip(tooltipTriggerEl)
); );
} }

View file

@ -0,0 +1,3 @@
---
permalink: /:basename
---

View file

@ -1,19 +1,15 @@
--- import { pwa, baseurl } from '../../_config.yml';
layout: compress import Toast from 'bootstrap/js/src/toast';
permalink: /assets/js/dist/:basename.min.js
---
if ('serviceWorker' in navigator) { if ('serviceWorker' in navigator) {
const isEnabled = '{{ site.pwa.enabled }}' === 'true'; if (pwa.enabled) {
const swUrl = `${baseurl}/sw.min.js`;
if (isEnabled) {
const swUrl = '{{ '/sw.min.js' | relative_url }}';
const notification = document.getElementById('notification'); const notification = document.getElementById('notification');
const btnRefresh = notification.querySelector('.toast-body>button'); const btnRefresh = notification.querySelector('.toast-body>button');
const popupWindow = bootstrap.Toast.getOrCreateInstance(notification); const popupWindow = Toast.getOrCreateInstance(notification);
navigator.serviceWorker.register(swUrl).then((registration) => { navigator.serviceWorker.register(swUrl).then((registration) => {
{% comment %}In case the user ignores the notification{% endcomment %} // In case the user ignores the notification
if (registration.waiting) { if (registration.waiting) {
popupWindow.show(); popupWindow.show();
} }
@ -32,14 +28,13 @@ if ('serviceWorker' in navigator) {
if (registration.waiting) { if (registration.waiting) {
registration.waiting.postMessage('SKIP_WAITING'); registration.waiting.postMessage('SKIP_WAITING');
} }
popupWindow.hide(); popupWindow.hide();
}); });
}); });
let refreshing = false; let refreshing = false;
{% comment %}Detect controller change and refresh all the opened tabs{% endcomment %} // Detect controller change and refresh all the opened tabs
navigator.serviceWorker.addEventListener('controllerchange', () => { navigator.serviceWorker.addEventListener('controllerchange', () => {
if (!refreshing) { if (!refreshing) {
window.location.reload(); window.location.reload();

View file

@ -1,12 +1,7 @@
--- import { baseurl } from '../../_config.yml';
layout: compress
permalink: /:basename.min.js
# PWA service worker
---
const swconfUrl = '{{ '/assets/js/data/swconf.js' | relative_url }}'; importScripts(`${baseurl}/assets/js/data/swconf.js`);
importScripts(swconfUrl);
const purge = swconf.purge; const purge = swconf.purge;
function verifyUrl(url) { function verifyUrl(url) {
@ -74,7 +69,7 @@ self.addEventListener('fetch', (event) => {
return response; return response;
} }
{% comment %}See: <https://developers.google.com/web/fundamentals/primers/service-workers#cache_and_return_requests>{% endcomment %} // See: <https://developers.google.com/web/fundamentals/primers/service-workers#cache_and_return_requests>
let responseToCache = response.clone(); let responseToCache = response.clone();
caches.open(swconf.cacheName).then((cache) => { caches.open(swconf.cacheName).then((cache) => {

View file

@ -13,12 +13,16 @@
}, },
"homepage": "https://github.com/cotes2020/jekyll-theme-chirpy/", "homepage": "https://github.com/cotes2020/jekyll-theme-chirpy/",
"scripts": { "scripts": {
"prebuild": "npx rimraf assets/js/dist", "build": "npm run build:js",
"build": "NODE_ENV=production npx rollup -c --bundleConfigAsCjs", "build:js": "rollup -c --bundleConfigAsCjs --environment BUILD:production",
"prewatch": "npx rimraf assets/js/dist", "watch:js": "rollup -c --bundleConfigAsCjs -w",
"watch": "npx rollup -c --bundleConfigAsCjs -w", "lint:style": "stylelint _sass/**/*.scss",
"test": "npx stylelint _sass/**/*.scss", "lint:fix:style": "npm run lint:style -- --fix",
"fixlint": "npm run test -- --fix" "test": "npm run lint:scss"
},
"dependencies": {
"@popperjs/core": "^2.11.8",
"bootstrap": "^5.3.3"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.24.4", "@babel/core": "^7.24.4",
@ -27,13 +31,14 @@
"@commitlint/cli": "^19.2.2", "@commitlint/cli": "^19.2.2",
"@commitlint/config-conventional": "^19.2.2", "@commitlint/config-conventional": "^19.2.2",
"@rollup/plugin-babel": "^6.0.4", "@rollup/plugin-babel": "^6.0.4",
"@rollup/plugin-node-resolve": "^15.2.3",
"@rollup/plugin-terser": "^0.4.4", "@rollup/plugin-terser": "^0.4.4",
"@rollup/plugin-yaml": "^4.1.2",
"@semantic-release/changelog": "^6.0.3", "@semantic-release/changelog": "^6.0.3",
"@semantic-release/exec": "^6.0.3", "@semantic-release/exec": "^6.0.3",
"@semantic-release/git": "^10.0.1", "@semantic-release/git": "^10.0.1",
"conventional-changelog-conventionalcommits": "^7.0.2", "conventional-changelog-conventionalcommits": "^7.0.2",
"husky": "^9.0.11", "husky": "^9.0.11",
"rimraf": "^5.0.5",
"rollup": "^4.15.0", "rollup": "^4.15.0",
"rollup-plugin-license": "^3.3.1", "rollup-plugin-license": "^3.3.1",
"semantic-release": "^23.0.8", "semantic-release": "^23.0.8",

View file

@ -1,23 +1,42 @@
import babel from '@rollup/plugin-babel'; import babel from '@rollup/plugin-babel';
import terser from '@rollup/plugin-terser'; import terser from '@rollup/plugin-terser';
import license from 'rollup-plugin-license'; import license from 'rollup-plugin-license';
import { nodeResolve } from '@rollup/plugin-node-resolve';
import fs from 'fs';
import path from 'path'; import path from 'path';
import yaml from '@rollup/plugin-yaml';
const SRC_DEFAULT = '_javascript'; const SRC_DEFAULT = '_javascript';
const DIST_DEFAULT = 'assets/js/dist'; const DIST_DEFAULT = 'assets/js/dist';
const isProd = process.env.NODE_ENV === 'production'; const SRC_PWA = `${SRC_DEFAULT}/pwa`;
const isProd = process.env.BUILD === 'production';
if (fs.existsSync(DIST_DEFAULT)) {
fs.rm(DIST_DEFAULT, { recursive: true, force: true }, (err) => {
if (err) {
throw err;
}
});
}
function build(filename, opts = {}) {
const src = opts.src || SRC_DEFAULT;
const dist = opts.dist || DIST_DEFAULT;
const bannerUrl =
opts.bannerUrl || path.join(__dirname, SRC_DEFAULT, '_copyright');
const commentStyle = opts.commentStyle || 'ignored';
function build(filename) {
return { return {
input: [`${SRC_DEFAULT}/${filename}.js`], input: [`${src}/${filename}.js`],
output: { output: {
file: `${DIST_DEFAULT}/${filename}.min.js`, file: `${dist}/${filename}.min.js`,
format: 'iife', format: 'iife',
name: 'Chirpy', name: 'Chirpy',
sourcemap: !isProd sourcemap: !isProd
}, },
watch: { watch: {
include: `${SRC_DEFAULT}/**` include: `${src}/**`
}, },
plugins: [ plugins: [
babel({ babel({
@ -25,13 +44,16 @@ function build(filename) {
presets: ['@babel/env'], presets: ['@babel/env'],
plugins: ['@babel/plugin-transform-class-properties'] plugins: ['@babel/plugin-transform-class-properties']
}), }),
nodeResolve(),
yaml(),
isProd && commentStyle === 'none' && terser(),
license({ license({
banner: { banner: {
commentStyle: 'ignored', commentStyle,
content: { file: path.join(__dirname, SRC_DEFAULT, '_copyright') } content: { file: bannerUrl }
} }
}), }),
isProd && terser() isProd && commentStyle !== 'none' && terser()
] ]
}; };
} }
@ -42,5 +64,11 @@ export default [
build('categories'), build('categories'),
build('page'), build('page'),
build('post'), build('post'),
build('misc') build('misc'),
build('app', { src: SRC_PWA }),
build('sw', {
src: SRC_PWA,
bannerUrl: path.join(__dirname, SRC_PWA, '_frontmatter'),
commentStyle: 'none'
})
]; ];