WIP: Добавляет поддержку PWA #10

Draft
Maks1mS wants to merge 6 commits from 7-pwa into master
15 changed files with 3166 additions and 38 deletions

View File

@ -22,15 +22,14 @@ module.exports = (eleventyConfig) => {
mkdirSync('dist/css', { recursive: true }) mkdirSync('dist/css', { recursive: true })
} }
}) })
eleventyConfig.addPassthroughCopy('./src/fonts/')
eleventyConfig.addPlugin(svgContents) eleventyConfig.addPlugin(svgContents)
eleventyConfig.addPlugin(faviconPlugin, { eleventyConfig.addPlugin(faviconPlugin, {
destination: './dist' destination: './dist'
}) })
eleventyConfig.addPlugin(syntaxHighlight) eleventyConfig.addPlugin(syntaxHighlight)
eleventyConfig.addPlugin(require('./plugins/manifest-icons'))
eleventyConfig.addPassthroughCopy('./src/fonts/')
eleventyConfig.addCollection('notes', function (collectionApi) { eleventyConfig.addCollection('notes', function (collectionApi) {
return collectionApi.getFilteredByGlob('src/notes/**/*.md') return collectionApi.getFilteredByGlob('src/notes/**/*.md')

2995
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -5,6 +5,7 @@
"start": "npx @11t/eleventy --serve", "start": "npx @11t/eleventy --serve",
"prebuild": "node ./scripts/symlink.js", "prebuild": "node ./scripts/symlink.js",
"build": "cross-env NODE_ENV=production npx @11t/eleventy", "build": "cross-env NODE_ENV=production npx @11t/eleventy",
"build:sw": "workbox generateSW workbox-config.js",
"stylelint": "stylelint 'src/**/*.css'", "stylelint": "stylelint 'src/**/*.css'",
"eslint": "eslint src" "eslint": "eslint src"
}, },
@ -30,10 +31,13 @@
"prismjs": "^1.28.0", "prismjs": "^1.28.0",
"rollup": "^2.72.1", "rollup": "^2.72.1",
"rollup-plugin-terser": "^7.0.2", "rollup-plugin-terser": "^7.0.2",
"rollup-plugin-workbox": "^6.2.0",
"simple-git-hooks": "^2.7.0", "simple-git-hooks": "^2.7.0",
"stylelint": "^14.8.2", "stylelint": "^14.8.2",
"stylelint-config-standard": "^25.0.0", "stylelint-config-standard": "^25.0.0",
"tailwindcss": "^3.0.24" "tailwindcss": "^3.0.24",
"workbox-build": "^6.5.3",
"workbox-cli": "^6.5.3"
}, },
"dependencies": { "dependencies": {
"alpinejs": "^3.10.2", "alpinejs": "^3.10.2",

View File

@ -0,0 +1,38 @@
const sharp = require('sharp')
const fs = require('fs').promises
const getDirName = require('path').dirname
async function writeFile (path, contents, cb) {
await fs.mkdir(getDirName(path), { recursive: true })
fs.writeFile(path, contents, cb)
}
function generatePngFavicon ({ density, width, height }, sourcePath, dimension) {
return sharp(sourcePath, {
density: (dimension / Math.max(width, height)) * density
})
.resize(dimension, dimension)
.png()
.toBuffer()
}
module.exports = (eleventyConfig) => {
eleventyConfig.addAsyncShortcode('manifest_icons', async function (faviconFile, ...dimensions) {
const metadata = await sharp(faviconFile).metadata()
const res = await Promise.all(dimensions.map(async dimension => {
const favicon = await generatePngFavicon(metadata, faviconFile, dimension)
writeFile(`dist/icons/${dimension}/favicon.png`, favicon)
return {
src: `/icons/${dimension}/favicon.png`,
sizes: `${dimension}x${dimension}`,
type: 'image/png'
}
}))
res[res.length - 1].purpose = 'maskable'
return JSON.stringify(res)
})
}

View File

@ -1,5 +1,8 @@
import resolve from '@rollup/plugin-node-resolve' import resolve from '@rollup/plugin-node-resolve'
import { terser } from 'rollup-plugin-terser' import { terser } from 'rollup-plugin-terser'
import { injectManifest } from 'rollup-plugin-workbox'
const workboxConfig = require('./workbox-config')
export default { export default {
input: 'src/js/index.js', input: 'src/js/index.js',
@ -7,5 +10,9 @@ export default {
file: 'dist/js/bundle.js', file: 'dist/js/bundle.js',
format: 'esm' format: 'esm'
}, },
plugins: [resolve(), terser()] plugins: [
injectManifest(workboxConfig),
resolve(),
terser()
]
} }

View File

@ -2,43 +2,33 @@
<!-- Created with Inkscape (http://www.inkscape.org/) --> <!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg <svg
width="10mm" width="12.560524mm"
height="10mm" height="12.560524mm"
viewBox="0 0 10 10" viewBox="0 0 12.560524 12.560524"
version="1.1" version="1.1"
id="svg1163" id="svg6948"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"> xmlns:svg="http://www.w3.org/2000/svg">
<defs <defs
id="defs1160" /> id="defs6945" />
<g <g
id="layer1" id="layer1"
transform="translate(-84.492197,-76.648259)"> transform="translate(-24.152918,-28.577486)">
<g
id="g121725"
transform="translate(8.947168,-21.108691)">
<rect <rect
style="fill:#333333;stroke-width:0.262137" style="fill:#333333;stroke-width:0.329258"
id="rect94180-7" id="rect94180-7"
width="10" width="12.560524"
height="10" height="12.560524"
x="75.545029" x="24.152918"
y="97.75695" y="28.577486"
ry="1.1785811" /> ry="0" />
<path <path
d="m 78.668175,104.34225 q -0.603323,0 -1.092977,-0.13411 -0.480909,-0.14251 -0.856893,-0.40235 -0.375984,-0.26822 -0.673273,-0.62866 l 0.900613,-0.9807 q 0.43719,0.5616 0.874381,0.75439 0.445934,0.19279 0.918099,0.19279 0.236084,0 0.428447,-0.0587 0.192363,-0.0587 0.29729,-0.16766 0.104928,-0.11737 0.104928,-0.28498 0,-0.12574 -0.06995,-0.22632 -0.06119,-0.10068 -0.174876,-0.17602 -0.113673,-0.0754 -0.271058,-0.13411 -0.157387,-0.0672 -0.332265,-0.10893 -0.174875,-0.0502 -0.358496,-0.0837 -0.507141,-0.10892 -0.883125,-0.26824 -0.375983,-0.15926 -0.629555,-0.38556 -0.244826,-0.23471 -0.367239,-0.52808 -0.12241,-0.30177 -0.12241,-0.67059 0,-0.39394 0.183619,-0.720857 0.192364,-0.335277 0.507141,-0.569983 0.323521,-0.243081 0.743224,-0.368817 0.419703,-0.13411 0.87438,-0.13411 0.603324,0 1.03177,0.125735 0.437191,0.117384 0.743225,0.343665 0.314776,0.226317 0.533373,0.544843 l -0.918102,0.846594 q -0.183619,-0.21794 -0.402213,-0.36044 -0.209853,-0.14249 -0.45468,-0.217932 -0.236081,-0.07544 -0.489653,-0.07544 -0.262315,0 -0.454678,0.06713 -0.18362,0.05869 -0.288546,0.176022 -0.104928,0.10895 -0.104928,0.26825 0,0.14249 0.08743,0.24307 0.08743,0.10068 0.236082,0.18441 0.148645,0.0754 0.341009,0.13411 0.192363,0.0502 0.402215,0.0838 0.48091,0.0922 0.865638,0.24307 0.39347,0.15088 0.673273,0.36881 0.288545,0.21793 0.43719,0.51969 0.157389,0.29338 0.157389,0.68733 0,0.6119 -0.314778,1.03938 -0.306032,0.41911 -0.848148,0.64543 -0.533373,0.21793 -1.232879,0.21793 z" d="m 28.811206,36.251169 q -0.525475,0 -0.951947,-0.116806 -0.418856,-0.124121 -0.746326,-0.350433 -0.32747,-0.233611 -0.586399,-0.547543 l 0.784405,-0.854158 q 0.380778,0.489136 0.761557,0.65705 0.388394,0.167913 0.799634,0.167913 0.205622,0 0.373164,-0.05112 0.167542,-0.05113 0.25893,-0.146027 0.09139,-0.102225 0.09139,-0.248208 0,-0.109516 -0.06092,-0.197117 -0.05329,-0.08769 -0.152311,-0.153308 -0.09901,-0.06567 -0.236083,-0.116806 -0.137079,-0.05853 -0.289392,-0.09487 -0.15231,-0.04372 -0.312238,-0.0729 -0.441703,-0.09487 -0.769173,-0.233628 -0.327469,-0.138711 -0.548322,-0.335811 -0.213236,-0.204424 -0.319853,-0.45994 -0.106615,-0.262832 -0.106615,-0.584062 0,-0.343109 0.159926,-0.627843 0.167543,-0.292015 0.441703,-0.496437 0.281776,-0.211715 0.647324,-0.321227 0.365548,-0.116806 0.761556,-0.116806 0.525476,0 0.898639,0.109511 0.380779,0.102238 0.647324,0.299321 0.27416,0.197115 0.464551,0.474541 l -0.799637,0.737356 q -0.159926,-0.189819 -0.350315,-0.313932 -0.182775,-0.124104 -0.396011,-0.189811 -0.205619,-0.06571 -0.426472,-0.06571 -0.228468,0 -0.39601,0.05847 -0.159927,0.05112 -0.251314,0.153309 -0.09139,0.09489 -0.09139,0.233637 0,0.124104 0.07615,0.211706 0.07615,0.08769 0.20562,0.160615 0.129465,0.06567 0.297008,0.116806 0.167541,0.04372 0.350316,0.07299 0.418857,0.0803 0.753942,0.211706 0.3427,0.131412 0.586399,0.321221 0.251313,0.18981 0.380778,0.452634 0.137081,0.255524 0.137081,0.598642 0,0.532945 -0.274161,0.905266 -0.266544,0.365031 -0.73871,0.562148 -0.46455,0.18981 -1.073797,0.18981 z"
style="font-weight:bold;font-size:10.5833px;line-height:1.25;font-family:'Lexend Deca';-inkscape-font-specification:'Lexend Deca Bold';fill:#999999;stroke-width:0.214026" style="font-weight:bold;font-size:10.5833px;line-height:1.25;font-family:'Lexend Deca';-inkscape-font-specification:'Lexend Deca Bold';fill:#999999;stroke-width:0.18641"
id="path96539-9" /> id="path96539-9" />
<text <path
xml:space="preserve" d="M 29.407328,38.764371 28.069052,34.30345 h 1.102485 l 0.669139,2.345176 q 0.04461,0.159317 0.07647,0.32501 0.03824,0.159318 0.05735,0.305884 0.02549,0.146575 0.03824,0.274033 0.01274,0.121081 0.02549,0.203928 h -0.1402 q 0.03824,-0.235788 0.06373,-0.420599 0.03186,-0.184811 0.0701,-0.344128 0.03824,-0.165693 0.09559,-0.344128 l 0.662766,-1.765251 h 0.866694 l 0.65002,1.765251 q 0.07647,0.210295 0.127455,0.395106 0.05099,0.18481 0.07647,0.356879 0.03186,0.165684 0.05098,0.331377 l -0.127455,0.01916 q 0.01274,-0.114707 0.01912,-0.210295 0.0064,-0.101964 0.01274,-0.191186 0.01274,-0.09559 0.02549,-0.197553 0.01274,-0.101964 0.03824,-0.223046 0.02549,-0.127458 0.0701,-0.286775 l 0.662766,-2.3388 h 1.076994 l -1.338277,4.460921 h -0.828456 l -1.057876,-2.568213 0.121082,0.01272 -0.97503,2.555471 z"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:7.31686px;line-height:1.25;font-family:'Lexend Deca';-inkscape-font-specification:'Lexend Deca Bold';fill:#f9f9f9;fill-opacity:1;stroke:none;stroke-width:0.182922" id="path6768"
x="77.786804" style="font-weight:bold;font-size:7.31686px;line-height:1.25;font-family:'Lexend Deca';-inkscape-font-specification:'Lexend Deca Bold';fill:#f9f9f9;stroke-width:0.159319" />
y="107.22778"
id="text107851"><tspan
id="tspan107849"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:'Lexend Deca';-inkscape-font-specification:'Lexend Deca Bold';fill:#f9f9f9;stroke-width:0.182922"
x="77.786804"
y="107.22778">W</tspan></text>
</g>
</g> </g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -4,4 +4,6 @@ import Fuse from 'fuse.js'
window.Fuse = Fuse window.Fuse = Fuse
window.Alpine = Alpine window.Alpine = Alpine
if ('serviceWorker' in navigator) { navigator.serviceWorker.register('/sw.js') }
Alpine.start() Alpine.start()

58
src/js/sw.js Normal file
View File

@ -0,0 +1,58 @@
/* global self, caches */
import { precacheAndRoute } from 'workbox-precaching'
precacheAndRoute(self.__WB_MANIFEST)
const CACHE_NAME = 'static-v2'
const OFFLINE_URL = 'offline.html'
self.addEventListener('install', event => {
console.log('Install!')
event.waitUntil(
caches.open(CACHE_NAME).then(cache => {
return cache.addAll([
'/',
'/js/bundle.js',
'/css/index.css',
'/fonts/OpenSans-Bold.ttf',
'/fonts/OpenSans-Bold.woff',
'/fonts/OpenSans-Bold.woff2',
'/fonts/OpenSans-Regular.ttf',
'/fonts/OpenSans-Regular.woff',
'/fonts/OpenSans-Regular.woff2',
'/favicon.ico',
'/favicon.svg',
OFFLINE_URL
])
})
)
self.skipWaiting()
})
self.addEventListener('fetch', (event) => {
// Нам нужно вызвать функцию event.respondWith(), только если это запрос на переход между
// HTML-страницами.
if (event.request.mode === 'navigate') {
event.respondWith(
(async () => {
try {
const preloadResponse = await event.preloadResponse
if (preloadResponse) {
return preloadResponse
}
const networkResponse = await fetch(event.request)
return networkResponse
} catch (error) {
console.log('Не удалось получить данные; вместо этого возвращаем страницу для автономного режима.', error)
const cache = await caches.open(CACHE_NAME)
const cachedResponse = await cache.match(OFFLINE_URL)
return cachedResponse
}
})()
)
}
})

View File

@ -6,6 +6,8 @@
<title>{{ title or metadata.title }}</title> <title>{{ title or metadata.title }}</title>
<meta name="description" content="{{ description or metadata.description }}"> <meta name="description" content="{{ description or metadata.description }}">
<meta name="generator" content="{{ eleventy.generator }}"> <meta name="generator" content="{{ eleventy.generator }}">
<meta name="theme-color" content="#fff"/>
<link rel="manifest" href="/manifest.json">
<link rel="stylesheet" href="/css/index.css"> <link rel="stylesheet" href="/css/index.css">
{% favicon 'src/images/favicon.svg' %} {% favicon 'src/images/favicon.svg' %}
</head> </head>

View File

@ -0,0 +1,3 @@
module.exports = {
permalink: '/manifest.json'
}

10
src/pages/manifset.njk Normal file
View File

@ -0,0 +1,10 @@
{
"name": "{{ metadata.title }}",
"lang": "ru",
"start_url": "/",
"display": "standalone",
"background_color": "#fff",
"theme_color": "#fff",
"description": "Вики с различными полезными статьями",
"icons": {% manifest_icons 'src/images/favicon.svg', 48, 72, 96, 144, 148, 512 %}
}

View File

@ -1,4 +1,7 @@
<main> <main>
<a href="/" class="block p-1 rounded-xl">
{{ '/src/images/logo.svg' | svgContents("max-h-12 block w-[50vw]") | safe }}
</a>
<article class="article-content"> <article class="article-content">
<h1>{{ note.data.title | safe }}</h1> <h1>{{ note.data.title | safe }}</h1>
{{ note.templateContent | safe }} {{ note.templateContent | safe }}

View File

@ -0,0 +1,5 @@
module.exports = {
layout: 'base.njk',
permalink: '/offline.html'
}

2
src/pages/offline.njk Normal file
View File

@ -0,0 +1,2 @@
<h1>Отсутствует подключение к сети</h1>
<a href="/">Вернуться на главную</a>

10
workbox-config.js Normal file
View File

@ -0,0 +1,10 @@
module.exports = {
globDirectory: 'dist/',
globPatterns: [
'**/*.{png,css,ico,svg,ttf,woff,woff2,js,json}',
'index.html'
],
swSrc: 'src/js/sw.js',
swDest: 'dist/sw.js',
mode: 'production'
}