@morphemecms/cms 0.1.0 → 0.2.0-next-20261104150211
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/LICENSE +21 -21
- package/README.md +51 -51
- package/astro.mjs +162 -162
- package/bin/morpheme.mjs +38 -38
- package/client.d.ts +10 -10
- package/defaults/blockTexts.ts +33 -33
- package/defaults/collections.ts +774 -774
- package/defaults/colors.ts +125 -125
- package/defaults/company.ts +20 -20
- package/defaults/editor.ts +178 -178
- package/defaults/internalLinks.ts +30 -30
- package/defaults/opens.ts +18 -18
- package/defaults/popup.tsx +93 -93
- package/defaults/privacy.ts +79 -79
- package/defaults/rules.ts +450 -450
- package/defaults/schema.ts +16 -16
- package/defaults/sitedata.ts +129 -129
- package/defaults/standard.ts +106 -106
- package/defaults/standardPages.ts +87 -87
- package/defaults/texts.ts +481 -481
- package/demo/README.md +26 -26
- package/demo/src/content/blog/build-a-page-from-blocks.md +19 -19
- package/demo/src/content/blog/colors-everyone-can-read.md +19 -19
- package/demo/src/content/blog/edit-your-website-from-your-phone.md +21 -21
- package/demo/src/content/blog/every-save-is-a-version.md +21 -21
- package/demo/src/content/blog/schedule-a-post.md +17 -17
- package/demo/src/content/blog/secure-from-day-one.md +17 -17
- package/demo/src/content/blog/seo-without-the-guesswork.md +23 -23
- package/demo/src/content/blog/switch-themes-without-losing-anything.md +23 -23
- package/demo/src/content/blog/the-app-store.md +21 -21
- package/demo/src/content/pages/about.md +22 -22
- package/demo/src/content/pages/app-store.md +30 -30
- package/demo/src/content/pages/blocks.md +98 -98
- package/demo/src/content/pages/blog-page.md +11 -11
- package/demo/src/content/pages/contact.md +16 -16
- package/demo/src/content/pages/get-started.md +20 -20
- package/demo/src/content/pages/home.md +26 -26
- package/demo/src/content/pages/page-not-found.md +11 -11
- package/demo/src/content/pages/privacy-policy.md +24 -24
- package/demo/src/data/blocks.json +25 -25
- package/demo/src/data/booking.json +6 -6
- package/demo/src/data/categories/apps.json +6 -6
- package/demo/src/data/categories/design.json +6 -6
- package/demo/src/data/categories/editing.json +6 -6
- package/demo/src/data/categories/security.json +6 -6
- package/demo/src/data/categories/seo.json +6 -6
- package/demo/src/data/colors.json +75 -75
- package/demo/src/data/contact.json +8 -8
- package/demo/src/data/cookies.json +21 -21
- package/demo/src/data/footer.json +27 -27
- package/demo/src/data/menu.json +35 -35
- package/demo/src/data/mobile-menu.json +25 -25
- package/demo/src/data/popups/booking-bar.json +13 -13
- package/demo/src/data/popups/try-the-editor.json +16 -16
- package/demo/src/data/standard-pages.json +8 -8
- package/demo/src/data/testimonials/sample-bakery.json +10 -10
- package/demo/src/data/testimonials/sample-clinic.json +10 -10
- package/demo/src/data/testimonials/sample-studio.json +10 -10
- package/demo/src/site/colors.ts +11 -11
- package/demo/src/site/config.ts +19 -19
- package/icons/lucide/LICENSE +43 -43
- package/icons/lucide/address-book.svg +1 -1
- package/icons/lucide/app-window.svg +1 -1
- package/icons/lucide/arrow-counter-clockwise.svg +1 -1
- package/icons/lucide/arrow-down.svg +1 -1
- package/icons/lucide/arrow-left.svg +1 -1
- package/icons/lucide/arrow-right.svg +1 -1
- package/icons/lucide/arrow-square-out.svg +1 -1
- package/icons/lucide/arrow-u-up-left.svg +1 -1
- package/icons/lucide/arrow-u-up-right.svg +1 -1
- package/icons/lucide/arrow-up-right.svg +1 -1
- package/icons/lucide/arrow-up.svg +1 -1
- package/icons/lucide/arrows-down-up.svg +1 -1
- package/icons/lucide/arrows-in-line-horizontal.svg +1 -1
- package/icons/lucide/arrows-out-line-horizontal.svg +1 -1
- package/icons/lucide/article.svg +1 -1
- package/icons/lucide/bank.svg +1 -1
- package/icons/lucide/bell.svg +1 -1
- package/icons/lucide/bicycle.svg +1 -1
- package/icons/lucide/binoculars.svg +1 -1
- package/icons/lucide/book-open-text.svg +1 -1
- package/icons/lucide/book-open.svg +1 -1
- package/icons/lucide/browser.svg +1 -1
- package/icons/lucide/buildings.svg +1 -1
- package/icons/lucide/bus.svg +1 -1
- package/icons/lucide/calendar-blank.svg +1 -1
- package/icons/lucide/calendar-check.svg +1 -1
- package/icons/lucide/car.svg +1 -1
- package/icons/lucide/cards.svg +1 -1
- package/icons/lucide/caret-down.svg +1 -1
- package/icons/lucide/caret-left.svg +1 -1
- package/icons/lucide/caret-right.svg +1 -1
- package/icons/lucide/caret-up.svg +1 -1
- package/icons/lucide/certificate.svg +1 -1
- package/icons/lucide/chart-line-up.svg +1 -1
- package/icons/lucide/chats.svg +1 -1
- package/icons/lucide/check-circle.svg +1 -1
- package/icons/lucide/check.svg +1 -1
- package/icons/lucide/circle-half.svg +1 -1
- package/icons/lucide/circle.svg +1 -1
- package/icons/lucide/clock-counter-clockwise.svg +1 -1
- package/icons/lucide/clock.svg +1 -1
- package/icons/lucide/code.svg +1 -1
- package/icons/lucide/coffee.svg +1 -1
- package/icons/lucide/columns.svg +1 -1
- package/icons/lucide/cookie.svg +1 -1
- package/icons/lucide/copy.svg +1 -1
- package/icons/lucide/credit-card.svg +1 -1
- package/icons/lucide/currency-circle-dollar.svg +1 -1
- package/icons/lucide/cursor-click.svg +1 -1
- package/icons/lucide/database.svg +1 -1
- package/icons/lucide/desktop.svg +1 -1
- package/icons/lucide/device-mobile.svg +1 -1
- package/icons/lucide/door-open.svg +1 -1
- package/icons/lucide/download-simple.svg +1 -1
- package/icons/lucide/envelope-simple.svg +1 -1
- package/icons/lucide/eye-slash.svg +1 -1
- package/icons/lucide/eye.svg +1 -1
- package/icons/lucide/file-pdf.svg +1 -1
- package/icons/lucide/file-text.svg +1 -1
- package/icons/lucide/file-video.svg +1 -1
- package/icons/lucide/file-zip.svg +1 -1
- package/icons/lucide/flower-lotus.svg +1 -1
- package/icons/lucide/folder-simple.svg +1 -1
- package/icons/lucide/funnel.svg +1 -1
- package/icons/lucide/gauge.svg +1 -1
- package/icons/lucide/gear.svg +1 -1
- package/icons/lucide/github-logo.svg +1 -1
- package/icons/lucide/globe.svg +1 -1
- package/icons/lucide/google-logo.svg +1 -1
- package/icons/lucide/graduation-cap.svg +1 -1
- package/icons/lucide/hammer.svg +1 -1
- package/icons/lucide/hand-heart.svg +1 -1
- package/icons/lucide/handshake.svg +1 -1
- package/icons/lucide/hard-drives.svg +1 -1
- package/icons/lucide/heart.svg +1 -1
- package/icons/lucide/house.svg +1 -1
- package/icons/lucide/image-square.svg +1 -1
- package/icons/lucide/image.svg +1 -1
- package/icons/lucide/info.svg +1 -1
- package/icons/lucide/keyboard.svg +1 -1
- package/icons/lucide/leaf.svg +1 -1
- package/icons/lucide/lifebuoy.svg +1 -1
- package/icons/lucide/lightbulb.svg +1 -1
- package/icons/lucide/link-break.svg +1 -1
- package/icons/lucide/link-simple.svg +1 -1
- package/icons/lucide/list-bullets.svg +1 -1
- package/icons/lucide/list-checks.svg +1 -1
- package/icons/lucide/list-numbers.svg +1 -1
- package/icons/lucide/list.svg +1 -1
- package/icons/lucide/lock-simple-open.svg +1 -1
- package/icons/lucide/lock-simple.svg +1 -1
- package/icons/lucide/magnifying-glass.svg +1 -1
- package/icons/lucide/map-pin.svg +1 -1
- package/icons/lucide/map-trifold.svg +1 -1
- package/icons/lucide/medal.svg +1 -1
- package/icons/lucide/megaphone.svg +1 -1
- package/icons/lucide/minus.svg +1 -1
- package/icons/lucide/moon.svg +1 -1
- package/icons/lucide/newspaper.svg +1 -1
- package/icons/lucide/pack.json +23 -23
- package/icons/lucide/paint-brush-broad.svg +1 -1
- package/icons/lucide/palette.svg +1 -1
- package/icons/lucide/paper-plane-tilt.svg +1 -1
- package/icons/lucide/pencil-simple.svg +1 -1
- package/icons/lucide/phone.svg +1 -1
- package/icons/lucide/play-fill.svg +1 -1
- package/icons/lucide/plus.svg +1 -1
- package/icons/lucide/prohibit.svg +1 -1
- package/icons/lucide/puzzle-piece.svg +1 -1
- package/icons/lucide/question.svg +1 -1
- package/icons/lucide/quotes.svg +1 -1
- package/icons/lucide/rows.svg +1 -1
- package/icons/lucide/ruler.svg +1 -1
- package/icons/lucide/scan.svg +1 -1
- package/icons/lucide/shapes.svg +1 -1
- package/icons/lucide/share-network.svg +1 -1
- package/icons/lucide/shield-check.svg +1 -1
- package/icons/lucide/sidebar-simple.svg +1 -1
- package/icons/lucide/sign-out.svg +1 -1
- package/icons/lucide/signpost.svg +1 -1
- package/icons/lucide/sliders-horizontal.svg +1 -1
- package/icons/lucide/smiley.svg +1 -1
- package/icons/lucide/sparkle.svg +1 -1
- package/icons/lucide/square-half-bottom.svg +1 -1
- package/icons/lucide/square.svg +1 -1
- package/icons/lucide/squares-four.svg +1 -1
- package/icons/lucide/sticker.svg +1 -1
- package/icons/lucide/storefront.svg +1 -1
- package/icons/lucide/sun.svg +1 -1
- package/icons/lucide/swatches.svg +1 -1
- package/icons/lucide/tag.svg +1 -1
- package/icons/lucide/text-aa.svg +1 -1
- package/icons/lucide/text-align-left.svg +1 -1
- package/icons/lucide/text-b.svg +1 -1
- package/icons/lucide/text-h-one.svg +1 -1
- package/icons/lucide/text-h-three.svg +1 -1
- package/icons/lucide/text-h-two.svg +1 -1
- package/icons/lucide/text-italic.svg +1 -1
- package/icons/lucide/text-t.svg +1 -1
- package/icons/lucide/ticket.svg +1 -1
- package/icons/lucide/toggle-right.svg +1 -1
- package/icons/lucide/train.svg +1 -1
- package/icons/lucide/trash.svg +1 -1
- package/icons/lucide/upload-simple.svg +1 -1
- package/icons/lucide/user-circle.svg +1 -1
- package/icons/lucide/user.svg +1 -1
- package/icons/lucide/users-three.svg +1 -1
- package/icons/lucide/users.svg +1 -1
- package/icons/lucide/video-camera.svg +1 -1
- package/icons/lucide/warning-circle.svg +1 -1
- package/icons/lucide/warning.svg +1 -1
- package/icons/lucide/wave-sine.svg +1 -1
- package/icons/lucide/wheelchair.svg +1 -1
- package/icons/lucide/wrench.svg +1 -1
- package/icons/lucide/x.svg +1 -1
- package/icons/material-symbols/LICENSE +201 -201
- package/icons/material-symbols/address-book.svg +1 -1
- package/icons/material-symbols/app-window.svg +1 -1
- package/icons/material-symbols/arrow-counter-clockwise.svg +1 -1
- package/icons/material-symbols/arrow-down.svg +1 -1
- package/icons/material-symbols/arrow-left.svg +1 -1
- package/icons/material-symbols/arrow-right.svg +1 -1
- package/icons/material-symbols/arrow-square-out.svg +1 -1
- package/icons/material-symbols/arrow-u-up-left.svg +1 -1
- package/icons/material-symbols/arrow-u-up-right.svg +1 -1
- package/icons/material-symbols/arrow-up-right.svg +1 -1
- package/icons/material-symbols/arrow-up.svg +1 -1
- package/icons/material-symbols/arrows-down-up.svg +1 -1
- package/icons/material-symbols/arrows-in-line-horizontal.svg +1 -1
- package/icons/material-symbols/arrows-out-line-horizontal.svg +1 -1
- package/icons/material-symbols/article.svg +1 -1
- package/icons/material-symbols/bank.svg +1 -1
- package/icons/material-symbols/bell.svg +1 -1
- package/icons/material-symbols/bicycle.svg +1 -1
- package/icons/material-symbols/binoculars.svg +1 -1
- package/icons/material-symbols/book-open-text.svg +1 -1
- package/icons/material-symbols/book-open.svg +1 -1
- package/icons/material-symbols/browser.svg +1 -1
- package/icons/material-symbols/buildings.svg +1 -1
- package/icons/material-symbols/bus.svg +1 -1
- package/icons/material-symbols/calendar-blank.svg +1 -1
- package/icons/material-symbols/calendar-check.svg +1 -1
- package/icons/material-symbols/car.svg +1 -1
- package/icons/material-symbols/cards.svg +1 -1
- package/icons/material-symbols/caret-down.svg +1 -1
- package/icons/material-symbols/caret-left.svg +1 -1
- package/icons/material-symbols/caret-right.svg +1 -1
- package/icons/material-symbols/caret-up.svg +1 -1
- package/icons/material-symbols/certificate.svg +1 -1
- package/icons/material-symbols/chart-line-up.svg +1 -1
- package/icons/material-symbols/chats.svg +1 -1
- package/icons/material-symbols/check-circle.svg +1 -1
- package/icons/material-symbols/check.svg +1 -1
- package/icons/material-symbols/circle-half.svg +1 -1
- package/icons/material-symbols/circle.svg +1 -1
- package/icons/material-symbols/clock-counter-clockwise.svg +1 -1
- package/icons/material-symbols/clock.svg +1 -1
- package/icons/material-symbols/code.svg +1 -1
- package/icons/material-symbols/coffee.svg +1 -1
- package/icons/material-symbols/columns.svg +1 -1
- package/icons/material-symbols/cookie.svg +1 -1
- package/icons/material-symbols/copy.svg +1 -1
- package/icons/material-symbols/credit-card.svg +1 -1
- package/icons/material-symbols/currency-circle-dollar.svg +1 -1
- package/icons/material-symbols/cursor-click.svg +1 -1
- package/icons/material-symbols/database.svg +1 -1
- package/icons/material-symbols/desktop.svg +1 -1
- package/icons/material-symbols/device-mobile.svg +1 -1
- package/icons/material-symbols/door-open.svg +1 -1
- package/icons/material-symbols/download-simple.svg +1 -1
- package/icons/material-symbols/envelope-simple.svg +1 -1
- package/icons/material-symbols/eye-slash.svg +1 -1
- package/icons/material-symbols/eye.svg +1 -1
- package/icons/material-symbols/file-pdf.svg +1 -1
- package/icons/material-symbols/file-text.svg +1 -1
- package/icons/material-symbols/file-video.svg +1 -1
- package/icons/material-symbols/file-zip.svg +1 -1
- package/icons/material-symbols/flower-lotus.svg +1 -1
- package/icons/material-symbols/folder-simple.svg +1 -1
- package/icons/material-symbols/funnel.svg +1 -1
- package/icons/material-symbols/gauge.svg +1 -1
- package/icons/material-symbols/gear.svg +1 -1
- package/icons/material-symbols/github-logo.svg +1 -1
- package/icons/material-symbols/globe.svg +1 -1
- package/icons/material-symbols/google-logo.svg +1 -1
- package/icons/material-symbols/graduation-cap.svg +1 -1
- package/icons/material-symbols/hammer.svg +1 -1
- package/icons/material-symbols/hand-heart.svg +1 -1
- package/icons/material-symbols/handshake.svg +1 -1
- package/icons/material-symbols/hard-drives.svg +1 -1
- package/icons/material-symbols/heart.svg +1 -1
- package/icons/material-symbols/house.svg +1 -1
- package/icons/material-symbols/image-square.svg +1 -1
- package/icons/material-symbols/image.svg +1 -1
- package/icons/material-symbols/info.svg +1 -1
- package/icons/material-symbols/keyboard.svg +1 -1
- package/icons/material-symbols/leaf.svg +1 -1
- package/icons/material-symbols/lifebuoy.svg +1 -1
- package/icons/material-symbols/lightbulb.svg +1 -1
- package/icons/material-symbols/link-break.svg +1 -1
- package/icons/material-symbols/link-simple.svg +1 -1
- package/icons/material-symbols/list-bullets.svg +1 -1
- package/icons/material-symbols/list-checks.svg +1 -1
- package/icons/material-symbols/list-numbers.svg +1 -1
- package/icons/material-symbols/list.svg +1 -1
- package/icons/material-symbols/lock-simple-open.svg +1 -1
- package/icons/material-symbols/lock-simple.svg +1 -1
- package/icons/material-symbols/magnifying-glass.svg +1 -1
- package/icons/material-symbols/map-pin.svg +1 -1
- package/icons/material-symbols/map-trifold.svg +1 -1
- package/icons/material-symbols/medal.svg +1 -1
- package/icons/material-symbols/megaphone.svg +1 -1
- package/icons/material-symbols/minus.svg +1 -1
- package/icons/material-symbols/moon.svg +1 -1
- package/icons/material-symbols/newspaper.svg +1 -1
- package/icons/material-symbols/pack.json +29 -29
- package/icons/material-symbols/paint-brush-broad.svg +1 -1
- package/icons/material-symbols/palette.svg +1 -1
- package/icons/material-symbols/paper-plane-tilt.svg +1 -1
- package/icons/material-symbols/pencil-simple.svg +1 -1
- package/icons/material-symbols/phone.svg +1 -1
- package/icons/material-symbols/play-fill.svg +1 -1
- package/icons/material-symbols/plus.svg +1 -1
- package/icons/material-symbols/prohibit.svg +1 -1
- package/icons/material-symbols/puzzle-piece.svg +1 -1
- package/icons/material-symbols/question.svg +1 -1
- package/icons/material-symbols/quotes.svg +1 -1
- package/icons/material-symbols/rows.svg +1 -1
- package/icons/material-symbols/ruler.svg +1 -1
- package/icons/material-symbols/scan.svg +1 -1
- package/icons/material-symbols/shapes.svg +1 -1
- package/icons/material-symbols/share-network.svg +1 -1
- package/icons/material-symbols/shield-check.svg +1 -1
- package/icons/material-symbols/sidebar-simple.svg +1 -1
- package/icons/material-symbols/sign-out.svg +1 -1
- package/icons/material-symbols/signpost.svg +1 -1
- package/icons/material-symbols/sliders-horizontal.svg +1 -1
- package/icons/material-symbols/smiley.svg +1 -1
- package/icons/material-symbols/sparkle.svg +1 -1
- package/icons/material-symbols/square-half-bottom.svg +1 -1
- package/icons/material-symbols/square.svg +1 -1
- package/icons/material-symbols/squares-four.svg +1 -1
- package/icons/material-symbols/sticker.svg +1 -1
- package/icons/material-symbols/storefront.svg +1 -1
- package/icons/material-symbols/sun.svg +1 -1
- package/icons/material-symbols/swatches.svg +1 -1
- package/icons/material-symbols/tag.svg +1 -1
- package/icons/material-symbols/text-aa.svg +1 -1
- package/icons/material-symbols/text-align-left.svg +1 -1
- package/icons/material-symbols/text-b.svg +1 -1
- package/icons/material-symbols/text-h-one.svg +1 -1
- package/icons/material-symbols/text-h-three.svg +1 -1
- package/icons/material-symbols/text-h-two.svg +1 -1
- package/icons/material-symbols/text-italic.svg +1 -1
- package/icons/material-symbols/text-t.svg +1 -1
- package/icons/material-symbols/ticket.svg +1 -1
- package/icons/material-symbols/toggle-right.svg +1 -1
- package/icons/material-symbols/train.svg +1 -1
- package/icons/material-symbols/trash.svg +1 -1
- package/icons/material-symbols/upload-simple.svg +1 -1
- package/icons/material-symbols/user-circle.svg +1 -1
- package/icons/material-symbols/user.svg +1 -1
- package/icons/material-symbols/users-three.svg +1 -1
- package/icons/material-symbols/users.svg +1 -1
- package/icons/material-symbols/video-camera.svg +1 -1
- package/icons/material-symbols/warning-circle.svg +1 -1
- package/icons/material-symbols/warning.svg +1 -1
- package/icons/material-symbols/wave-sine.svg +1 -1
- package/icons/material-symbols/wheelchair.svg +1 -1
- package/icons/material-symbols/wrench.svg +1 -1
- package/icons/material-symbols/x.svg +1 -1
- package/icons/phosphor/pack.json +8 -8
- package/lib/apps.mjs +142 -139
- package/lib/apps.test.d.mts +1 -0
- package/lib/apps.test.mjs +50 -0
- package/lib/blocks.mjs +90 -90
- package/lib/caseCollisions.d.mts +4 -0
- package/lib/caseCollisions.mjs +62 -0
- package/lib/caseCollisions.test.d.mts +1 -0
- package/lib/caseCollisions.test.mjs +74 -0
- package/lib/colors.ts +86 -86
- package/lib/contactBar.ts +18 -18
- package/lib/contracts.d.mts +15 -0
- package/lib/contracts.mjs +17 -0
- package/lib/cookieCategories.mjs +28 -28
- package/lib/crop.mjs +83 -83
- package/lib/csp.mjs +130 -130
- package/lib/cspSources.mjs +60 -60
- package/lib/dynamic-colors.ts +426 -426
- package/lib/git.mjs +64 -64
- package/lib/hooks.ts +51 -51
- package/lib/inline.ts +20 -20
- package/lib/license-texts/Apache-2.0.txt +190 -190
- package/lib/license-texts/BSD-2-Clause.txt +24 -24
- package/lib/license-texts/BSD-3-Clause.txt +28 -28
- package/lib/license-texts/ISC.txt +15 -15
- package/lib/license-texts/MIT.txt +21 -21
- package/lib/licenses.d.mts +1 -1
- package/lib/licenses.mjs +683 -683
- package/lib/markdown.ts +42 -42
- package/lib/motion-schemes.mjs +5 -5
- package/lib/motion.ts +131 -131
- package/lib/notice.mjs +34 -34
- package/lib/scheduled.mjs +37 -37
- package/lib/snackbar.ts +149 -149
- package/lib/status.mjs +18 -18
- package/lib/storeEntries.d.mts +18 -0
- package/lib/storeEntries.mjs +64 -0
- package/lib/storeEntries.test.d.mts +1 -0
- package/lib/storeEntries.test.mjs +71 -0
- package/lib/svg.mjs +231 -231
- package/lib/text-colors.mjs +43 -43
- package/lib/video-id.mjs +72 -72
- package/lib/video.mjs +91 -91
- package/package.json +136 -131
- package/pages/404.astro +10 -10
- package/pages/[...slug].astro +51 -51
- package/pages/_headers.txt +31 -31
- package/pages/blog/index.astro +10 -10
- package/pages/blog/kategori/[category].astro +18 -18
- package/pages/editor/code-images.json.js +25 -25
- package/pages/editor/index.astro +75 -75
- package/pages/editor/manifest.webmanifest.js +23 -23
- package/pages/editor/sw.js +33 -33
- package/pages/editor/sw.js.js +4 -4
- package/pages/editor/thumbnails.json.js +21 -21
- package/pages/editor/version.json.js +22 -22
- package/pages/headers.js +4 -4
- package/pages/index.astro +10 -10
- package/pages/ny.astro +6 -6
- package/pages/robots.txt.js +3 -3
- package/pages/rss.xml.js +20 -20
- package/scripts/app-screenshots.mjs +164 -164
- package/scripts/app-workers.mjs +6 -6
- package/scripts/bin.mjs +12 -12
- package/scripts/built-site.mjs +134 -99
- package/scripts/check-consent.mjs +113 -113
- package/scripts/check-content.mjs +151 -149
- package/scripts/check-demo.mjs +24 -24
- package/scripts/check-store.mjs +160 -0
- package/scripts/check-themes.mjs +45 -45
- package/scripts/check-worker.mjs +569 -569
- package/scripts/compare-html.mjs +81 -81
- package/scripts/declarations.mjs +10 -0
- package/scripts/declarations.test.mjs +32 -0
- package/scripts/demo-site.mjs +42 -42
- package/scripts/editor-icons.mjs +56 -56
- package/scripts/editor-roundtrip.mjs +134 -134
- package/scripts/editor-screens.mjs +101 -101
- package/scripts/git-env.mjs +9 -9
- package/scripts/icon-packs.mjs +151 -151
- package/scripts/link-counts.mjs +22 -22
- package/scripts/page-colors.mjs +42 -42
- package/scripts/releases.mjs +80 -80
- package/scripts/responsive-images.mjs +94 -94
- package/scripts/scheduled-list.mjs +15 -15
- package/scripts/theme-screenshots.mjs +88 -88
- package/scripts/types.mjs +17 -21
- package/scripts/video-thumbnails.mjs +89 -89
- package/scripts/worker-modules.mjs +34 -34
- package/site/appCookies.mjs +74 -74
- package/site/apps.ts +21 -21
- package/site/autolinks.mjs +200 -200
- package/site/blockScripts.ts +31 -31
- package/site/categories.ts +16 -16
- package/site/consent.ts +26 -26
- package/site/content.config.ts +84 -84
- package/site/content.ts +104 -104
- package/site/cookieList.ts +17 -17
- package/site/defineSite.d.mts +2 -14
- package/site/defineSite.mjs +131 -136
- package/site/directives/interact.mjs +31 -31
- package/site/editor-version.mjs +26 -26
- package/site/fill.ts +13 -13
- package/site/format.ts +11 -11
- package/site/home.ts +66 -66
- package/site/iconPacks.mjs +43 -43
- package/site/icons.mjs +605 -605
- package/site/images.mjs +157 -157
- package/site/motion.ts +13 -13
- package/site/noindex.mjs +21 -21
- package/site/pageColors.ts +110 -110
- package/site/popups.ts +98 -98
- package/site/schema.ts +189 -189
- package/site/sections.ts +3 -3
- package/site/seo.ts +28 -28
- package/site/site.ts +76 -76
- package/site/siteConfig.mjs +8 -8
- package/site/siteCsp.mjs +75 -75
- package/site/siteModules.mjs +27 -27
- package/site/standardPages.ts +59 -59
- package/site/storeTheme.d.mts +6 -0
- package/site/storeTheme.mjs +29 -0
- package/site/storeTheme.test.d.mts +1 -0
- package/site/storeTheme.test.mjs +53 -0
- package/site/testimonials.ts +48 -48
- package/site/text.ts +43 -43
- package/site/theme.mjs +483 -472
- package/site/tracking.client.ts +225 -225
- package/site/tracking.ts +9 -9
- package/src/ActionParts.tsx +205 -205
- package/src/Advanced.tsx +152 -152
- package/src/App.tsx +901 -904
- package/src/AppList.tsx +1362 -1345
- package/src/BlockDialog.tsx +360 -360
- package/src/BlockParts.tsx +652 -652
- package/src/BlockSettings.tsx +533 -533
- package/src/BrandForm.tsx +164 -164
- package/src/Colors.tsx +476 -476
- package/src/CommunicationParts.tsx +199 -199
- package/src/CompanyForm.tsx +209 -209
- package/src/Consents.tsx +175 -175
- package/src/ContainmentParts.tsx +185 -185
- package/src/Crop.tsx +152 -152
- package/src/DataForm.tsx +434 -434
- package/src/Deleted.tsx +177 -177
- package/src/EntryForm.tsx +740 -740
- package/src/FileView.tsx +30 -30
- package/src/FormFrame.tsx +237 -237
- package/src/Keywords.tsx +225 -225
- package/src/Library.tsx +1563 -1563
- package/src/Licenses.tsx +336 -336
- package/src/LinkSheet.tsx +138 -138
- package/src/MarkdownEditor.tsx +1075 -1075
- package/src/Media.tsx +677 -677
- package/src/NavigationParts.tsx +350 -350
- package/src/NavigationRestParts.tsx +247 -247
- package/src/Notifications.tsx +59 -59
- package/src/PagePicker.tsx +136 -136
- package/src/Parts.tsx +991 -991
- package/src/PartsShown.tsx +168 -168
- package/src/PickerParts.tsx +183 -183
- package/src/Redirects.tsx +450 -450
- package/src/Screen.tsx +184 -184
- package/src/SectionsField.tsx +65 -65
- package/src/SelectionParts.tsx +134 -134
- package/src/SeoScores.tsx +184 -184
- package/src/Sheet.tsx +35 -35
- package/src/ThemeStore.tsx +376 -376
- package/src/Tokens.tsx +346 -346
- package/src/Versions.tsx +214 -214
- package/src/addresses.ts +75 -75
- package/src/allIcons.ts +7 -7
- package/src/appFiles.ts +29 -29
- package/src/apps.ts +281 -281
- package/src/ask.tsx +123 -123
- package/src/blocks.ts +251 -251
- package/src/brand.ts +153 -153
- package/src/cache.ts +42 -42
- package/src/check.ts +285 -285
- package/src/checkSite.ts +256 -256
- package/src/{consents.ts → consentData.ts} +26 -26
- package/src/cookieList.ts +70 -70
- package/src/countries.ts +59 -59
- package/src/deploy.ts +155 -155
- package/src/editor.css +602 -602
- package/src/examples.ts +85 -85
- package/src/fields.tsx +942 -942
- package/src/frontmatter.mjs +42 -42
- package/src/github.ts +376 -376
- package/src/i18n/da-DK/advanced.ts +28 -28
- package/src/i18n/da-DK/app.ts +71 -71
- package/src/i18n/da-DK/apps.ts +90 -90
- package/src/i18n/da-DK/blockSettings.ts +38 -38
- package/src/i18n/da-DK/blocks.ts +408 -408
- package/src/i18n/da-DK/check.ts +25 -25
- package/src/i18n/da-DK/colors.ts +64 -64
- package/src/i18n/da-DK/common.ts +118 -118
- package/src/i18n/da-DK/company.ts +24 -24
- package/src/i18n/da-DK/consents.ts +23 -23
- package/src/i18n/da-DK/forms.ts +116 -116
- package/src/i18n/da-DK/history.ts +35 -35
- package/src/i18n/da-DK/index.ts +49 -49
- package/src/i18n/da-DK/keywords.ts +19 -19
- package/src/i18n/da-DK/library.ts +135 -135
- package/src/i18n/da-DK/licenses.ts +25 -25
- package/src/i18n/da-DK/media.ts +79 -79
- package/src/i18n/da-DK/parts.ts +696 -696
- package/src/i18n/da-DK/preview.ts +13 -13
- package/src/i18n/da-DK/redirects.ts +51 -51
- package/src/i18n/da-DK/seo.ts +20 -20
- package/src/i18n/da-DK/text.ts +35 -35
- package/src/i18n/da-DK/theme.ts +41 -41
- package/src/i18n/en-US/advanced.ts +32 -32
- package/src/i18n/en-US/app.ts +84 -84
- package/src/i18n/en-US/apps.ts +134 -134
- package/src/i18n/en-US/blockSettings.ts +48 -48
- package/src/i18n/en-US/blocks.ts +441 -441
- package/src/i18n/en-US/check.ts +31 -31
- package/src/i18n/en-US/colors.ts +82 -82
- package/src/i18n/en-US/common.ts +144 -144
- package/src/i18n/en-US/company.ts +25 -25
- package/src/i18n/en-US/consents.ts +25 -25
- package/src/i18n/en-US/forms.ts +145 -145
- package/src/i18n/en-US/history.ts +43 -43
- package/src/i18n/en-US/index.ts +51 -51
- package/src/i18n/en-US/keywords.ts +24 -24
- package/src/i18n/en-US/library.ts +176 -176
- package/src/i18n/en-US/licenses.ts +36 -36
- package/src/i18n/en-US/media.ts +80 -80
- package/src/i18n/en-US/parts.ts +718 -718
- package/src/i18n/en-US/preview.ts +15 -15
- package/src/i18n/en-US/redirects.ts +66 -66
- package/src/i18n/en-US/seo.ts +26 -26
- package/src/i18n/en-US/text.ts +41 -41
- package/src/i18n/en-US/theme.ts +41 -41
- package/src/i18n/index.ts +124 -124
- package/src/icon.tsx +160 -160
- package/src/iconPacks.ts +89 -89
- package/src/images.ts +163 -163
- package/src/{licenses.ts → licenseData.ts} +79 -79
- package/src/markdown.mjs +204 -204
- package/src/nav.ts +68 -68
- package/src/notFound.ts +28 -28
- package/src/{notifications.ts → notificationData.ts} +223 -223
- package/src/pagePreview.tsx +204 -204
- package/src/r2Caching.ts +55 -55
- package/src/{redirects.ts → redirectData.ts} +149 -149
- package/src/registry.tsx +188 -188
- package/src/schema.ts +417 -417
- package/src/sections.mjs +303 -303
- package/src/seo.ts +333 -333
- package/src/shared.ts +44 -44
- package/src/sidebar.ts +31 -31
- package/src/site.ts +317 -317
- package/src/standard/Block.astro +19 -19
- package/src/standard/BlockSite.astro +18 -18
- package/src/standard/accordion/index.tsx +47 -47
- package/src/standard/accordion/load.ts +23 -23
- package/src/standard/accordion/shared.ts +15 -15
- package/src/standard/accordion/view.ts +32 -32
- package/src/standard/buttons/fields.ts +98 -98
- package/src/standard/buttons/index.tsx +23 -23
- package/src/standard/buttons/load.ts +34 -34
- package/src/standard/buttons/shared.ts +12 -12
- package/src/standard/buttons/view.ts +30 -30
- package/src/standard/cards/index.tsx +71 -71
- package/src/standard/cards/load.ts +17 -17
- package/src/standard/cards/shared.ts +27 -27
- package/src/standard/cards/view.ts +29 -29
- package/src/standard/checklist/index.tsx +27 -27
- package/src/standard/checklist/load.ts +5 -5
- package/src/standard/checklist/view.ts +13 -13
- package/src/standard/cookies/index.tsx +17 -17
- package/src/standard/cookies/list.ts +56 -56
- package/src/standard/cookies/load.ts +11 -11
- package/src/standard/cookies/shared.ts +20 -20
- package/src/standard/cookies/view.ts +28 -28
- package/src/standard/faq/index.tsx +29 -29
- package/src/standard/faq/load.ts +14 -14
- package/src/standard/faq/view.ts +21 -21
- package/src/standard/head.ts +23 -23
- package/src/standard/hero/fields.ts +48 -48
- package/src/standard/hero/index.tsx +57 -57
- package/src/standard/hero/load.ts +23 -23
- package/src/standard/hero/view.ts +38 -38
- package/src/standard/links/index.tsx +41 -41
- package/src/standard/links/load.ts +17 -17
- package/src/standard/links/view.ts +25 -25
- package/src/standard/list/index.tsx +50 -50
- package/src/standard/list/load.ts +5 -5
- package/src/standard/list/view.ts +18 -18
- package/src/standard/load.ts +7 -7
- package/src/standard/notice/index.tsx +30 -30
- package/src/standard/notice/load.ts +16 -16
- package/src/standard/notice/view.ts +10 -10
- package/src/standard/points/index.tsx +42 -42
- package/src/standard/points/load.ts +5 -5
- package/src/standard/points/view.ts +12 -12
- package/src/standard/posts/index.tsx +132 -132
- package/src/standard/posts/load.ts +36 -36
- package/src/standard/posts/shared.ts +21 -21
- package/src/standard/posts/view.ts +63 -63
- package/src/standard/pricecards/index.tsx +57 -57
- package/src/standard/pricecards/load.ts +11 -11
- package/src/standard/pricecards/view.ts +50 -50
- package/src/standard/sharedTexts.ts +6 -6
- package/src/standard/site.ts +124 -124
- package/src/standard/siteLooks.ts +19 -19
- package/src/standard/sites.ts +13 -13
- package/src/standard/steps/index.tsx +28 -28
- package/src/standard/steps/load.ts +5 -5
- package/src/standard/steps/view.ts +11 -11
- package/src/standard/testimonials/index.tsx +87 -87
- package/src/standard/testimonials/list.ts +43 -43
- package/src/standard/testimonials/load.ts +58 -58
- package/src/standard/testimonials/shared.ts +13 -13
- package/src/standard/testimonials/view.ts +76 -76
- package/src/standard/themeBlocks.ts +13 -13
- package/src/standard/video/index.tsx +23 -23
- package/src/standard/video/load.ts +10 -10
- package/src/standard/video/view.ts +42 -42
- package/src/standard/view.tsx +105 -105
- package/src/standard/views.ts +37 -37
- package/src/store.ts +479 -499
- package/src/theme.ts +90 -90
- package/src/themeUi.ts +97 -97
- package/src/themes.ts +91 -91
- package/src/ui.tsx +3402 -3402
- package/src/virtual.d.ts +5 -5
- package/src/when.ts +22 -22
- package/starter/.githooks/pre-push +4 -4
- package/starter/.nvmrc +1 -1
- package/starter/.prettierignore +11 -11
- package/starter/.prettierrc.json +7 -7
- package/starter/README.md +27 -26
- package/starter/astro.config.mjs +5 -5
- package/starter/package.json +67 -67
- package/starter/public/_redirects +1 -1
- package/starter/public/favicon.svg +1 -1
- package/starter/public/logo.svg +1 -1
- package/starter/src/content/pages/blog.md +8 -8
- package/starter/src/content/pages/forside.md +9 -9
- package/starter/src/content/pages/kontakt.md +7 -7
- package/starter/src/content/pages/privatlivspolitik.md +9 -9
- package/starter/src/content/pages/siden-findes-ikke.md +7 -7
- package/starter/src/content.config.ts +2 -2
- package/starter/src/data/apps.json +1 -1
- package/starter/src/data/block-settings.json +3 -3
- package/starter/src/data/blocks.json +1 -1
- package/starter/src/data/booking.json +6 -6
- package/starter/src/data/colors.json +70 -70
- package/starter/src/data/contact.json +8 -8
- package/starter/src/data/cookies.json +21 -21
- package/starter/src/data/csp.json +1 -1
- package/starter/src/data/footer.json +15 -15
- package/starter/src/data/internal-links.json +4 -4
- package/starter/src/data/menu.json +4 -4
- package/starter/src/data/mobile-menu.json +4 -4
- package/starter/src/data/old-addresses.json +1 -1
- package/starter/src/data/redirects.json +1 -1
- package/starter/src/data/standard-pages.json +8 -8
- package/starter/src/data/theme.json +1 -1
- package/starter/src/env.d.ts +2 -2
- package/starter/src/site/config.ts +13 -13
- package/starter/src/site/worker.js +14 -14
- package/starter/tsconfig.json +17 -17
- package/starter/worker/apps.js +6 -6
- package/starter/wrangler.jsonc +39 -39
- package/theme/LICENSE +21 -21
- package/theme/blocks/ArrowLink.tsx +14 -14
- package/theme/blocks/BlockHead.tsx +26 -26
- package/theme/blocks/Panel.tsx +32 -32
- package/theme/blocks/Person.tsx +26 -26
- package/theme/blocks/accordion/PageRow.tsx +25 -25
- package/theme/blocks/accordion/View.tsx +17 -17
- package/theme/blocks/buttons/Buttons.tsx +28 -28
- package/theme/blocks/buttons/View.tsx +12 -12
- package/theme/blocks/cards/LinkCards.tsx +72 -72
- package/theme/blocks/cards/View.tsx +20 -20
- package/theme/blocks/checklist/Checks.tsx +15 -15
- package/theme/blocks/checklist/View.tsx +14 -14
- package/theme/blocks/cookies/View.tsx +36 -36
- package/theme/blocks/faq/Accordion.tsx +34 -34
- package/theme/blocks/faq/View.tsx +16 -16
- package/theme/blocks/faq/script.ts +56 -56
- package/theme/blocks/hero/View.tsx +44 -44
- package/theme/blocks/links/LinkGroups.tsx +24 -24
- package/theme/blocks/links/View.tsx +17 -17
- package/theme/blocks/list/Rows.tsx +24 -24
- package/theme/blocks/list/View.tsx +14 -14
- package/theme/blocks/notice/View.tsx +13 -13
- package/theme/blocks/points/PointList.tsx +34 -34
- package/theme/blocks/points/View.tsx +14 -14
- package/theme/blocks/posts/View.tsx +26 -26
- package/theme/blocks/pricecards/View.tsx +40 -40
- package/theme/blocks/steps/StepList.tsx +23 -23
- package/theme/blocks/steps/View.tsx +14 -14
- package/theme/blocks/testimonials/List.tsx +46 -46
- package/theme/blocks/testimonials/Slider.tsx +44 -44
- package/theme/blocks/testimonials/Testimonial.tsx +124 -124
- package/theme/blocks/testimonials/View.tsx +21 -21
- package/theme/blocks/testimonials/script.ts +52 -52
- package/theme/blocks/video/View.tsx +41 -41
- package/theme/components/ActionBar.astro +63 -63
- package/theme/components/AppSlot.astro +21 -21
- package/theme/components/ArrowLink.tsx +21 -21
- package/theme/components/BlogIndex.astro +48 -48
- package/theme/components/BlogPosts.astro +18 -18
- package/theme/components/BlogPosts.tsx +45 -45
- package/theme/components/BookButton.tsx +22 -22
- package/theme/components/Container.astro +11 -11
- package/theme/components/CookieConsent.astro +121 -121
- package/theme/components/EndBlock.astro +21 -21
- package/theme/components/FontPreload.astro +6 -6
- package/theme/components/Footer.astro +85 -85
- package/theme/components/Header.astro +186 -186
- package/theme/components/Icon.astro +16 -16
- package/theme/components/Img.astro +29 -29
- package/theme/components/Img.tsx +33 -33
- package/theme/components/Inline.astro +11 -11
- package/theme/components/Logo.astro +23 -23
- package/theme/components/LogoMark.astro +10 -10
- package/theme/components/Markdown.astro +17 -17
- package/theme/components/MobileMenu.tsx +224 -224
- package/theme/components/MobileNav.astro +33 -33
- package/theme/components/PageHeader.astro +22 -22
- package/theme/components/PageSection.astro +24 -24
- package/theme/components/PhoneButton.tsx +11 -11
- package/theme/components/Popup.astro +145 -145
- package/theme/components/PopupButton.tsx +24 -24
- package/theme/components/PopupView.tsx +48 -48
- package/theme/components/PostList.tsx +58 -58
- package/theme/components/PostMeta.tsx +17 -17
- package/theme/components/RichText.astro +28 -28
- package/theme/components/Section.astro +29 -29
- package/theme/components/SectionHeading.tsx +8 -8
- package/theme/components/TextSection.astro +16 -16
- package/theme/components/home/PageBlock.astro +19 -19
- package/theme/components/home/Text.astro +13 -13
- package/theme/components/loadPopup.ts +22 -22
- package/theme/components/loadPosts.ts +16 -16
- package/theme/components/posts.ts +15 -15
- package/theme/editor/looks.mjs +7 -7
- package/theme/editor/previews.tsx +56 -56
- package/theme/layouts/Base.astro +273 -273
- package/theme/layouts/PageLayout.astro +126 -126
- package/theme/layouts/PostLayout.astro +137 -137
- package/theme/scripts/motion.ts +29 -29
- package/theme/styles/base.css +320 -320
- package/theme/styles/blocks.css +1808 -1808
- package/theme/styles/components.css +206 -206
- package/theme/styles/global.css +1302 -1302
- package/theme/styles/ui.css +6544 -6544
- package/theme/templates/Blog.astro +38 -38
- package/theme/templates/Category.astro +19 -19
- package/theme/templates/Home.astro +36 -36
- package/theme/templates/New.astro +68 -68
- package/theme/templates/NotFound.astro +117 -117
- package/theme/theme.json +28 -28
- package/theme/ui/Badge.tsx +45 -45
- package/theme/ui/BottomSheet.tsx +148 -148
- package/theme/ui/Button.tsx +79 -79
- package/theme/ui/ButtonGroup.tsx +31 -31
- package/theme/ui/Card.tsx +45 -45
- package/theme/ui/Carousel.tsx +124 -124
- package/theme/ui/Checkbox.tsx +59 -59
- package/theme/ui/Chip.tsx +101 -101
- package/theme/ui/ChipSet.tsx +27 -27
- package/theme/ui/DatePicker.tsx +149 -149
- package/theme/ui/DatePickerContainer.tsx +316 -316
- package/theme/ui/Dialog.tsx +100 -100
- package/theme/ui/Divider.tsx +27 -27
- package/theme/ui/ExposedDropdownMenu.tsx +77 -77
- package/theme/ui/Fab.tsx +88 -88
- package/theme/ui/FabMenu.tsx +67 -67
- package/theme/ui/IconButton.tsx +78 -78
- package/theme/ui/List.tsx +287 -287
- package/theme/ui/LoadingIndicator.tsx +29 -29
- package/theme/ui/Menu.tsx +278 -278
- package/theme/ui/NavigationBar.tsx +62 -62
- package/theme/ui/NavigationDrawer.tsx +80 -80
- package/theme/ui/NavigationRail.tsx +154 -154
- package/theme/ui/ProgressIndicator.tsx +162 -162
- package/theme/ui/RadioButton.tsx +50 -50
- package/theme/ui/Search.tsx +129 -129
- package/theme/ui/SegmentedButton.tsx +49 -49
- package/theme/ui/SideSheet.tsx +54 -54
- package/theme/ui/Slider.tsx +155 -155
- package/theme/ui/Snackbar.tsx +67 -67
- package/theme/ui/SplitButton.tsx +67 -67
- package/theme/ui/Switch.tsx +60 -60
- package/theme/ui/Tabs.tsx +114 -114
- package/theme/ui/TextField.tsx +278 -278
- package/theme/ui/TimePicker.tsx +128 -128
- package/theme/ui/TimePickerContainer.tsx +234 -234
- package/theme/ui/Toolbar.tsx +67 -67
- package/theme/ui/Tooltip.tsx +58 -58
- package/theme/ui/TopAppBar.tsx +77 -77
- package/theme/ui/bottomSheet.script.ts +8 -8
- package/theme/ui/{bottomSheet.ts → bottomSheetControl.ts} +211 -211
- package/theme/ui/carousel.script.ts +28 -28
- package/theme/ui/carouselLayout.ts +229 -229
- package/theme/ui/checkbox.script.ts +8 -8
- package/theme/ui/datePicker.script.ts +302 -302
- package/theme/ui/dates.ts +185 -185
- package/theme/ui/{dialog.ts → dialogControl.ts} +48 -48
- package/theme/ui/dropdownMenu.script.ts +166 -166
- package/theme/ui/fabMenu.script.ts +57 -57
- package/theme/ui/navigationRail.script.ts +47 -47
- package/theme/ui/ripple.script.ts +159 -159
- package/theme/ui/search.script.ts +92 -92
- package/theme/ui/{sideSheet.ts → sideSheetControl.ts} +48 -48
- package/theme/ui/slider.script.ts +12 -12
- package/theme/ui/sliderUpdate.ts +52 -52
- package/theme/ui/snackbar.script.ts +16 -16
- package/theme/ui/splitButton.script.ts +63 -63
- package/theme/ui/tabs.script.ts +48 -48
- package/theme/ui/textField.script.ts +17 -17
- package/theme/ui/textFieldUpdate.ts +44 -44
- package/theme/ui/timePicker.script.ts +248 -248
- package/theme/ui/toolbar.script.ts +43 -43
- package/theme/ui/tooltip.script.ts +68 -68
- package/worker/consent.js +141 -141
- package/worker/editor-github.js +167 -167
- package/worker/editor-host.js +74 -74
- package/worker/files.js +401 -401
- package/worker/github.js +36 -36
- package/worker/index.js +188 -184
- package/worker/not-found.js +97 -97
- package/worker/oauth.js +95 -95
- package/worker/publish.js +52 -52
- package/worker/push.js +270 -270
- package/worker/r2-status.js +50 -50
- package/worker/session.js +58 -58
- package/worker/store.js +46 -0
package/src/checkSite.ts
CHANGED
|
@@ -1,256 +1,256 @@
|
|
|
1
|
-
// Everything the editor writes, checked by the editor's own rules (check.ts) before the site is built
|
|
2
|
-
// (packages/editor/scripts/check-content.mjs): every block in the text of the pages and posts (src/apps/<app>/blocks/, an unknown
|
|
3
|
-
// one too), and the data files with the fields the editor edits them with: the fixed pages with their
|
|
4
|
-
// sections, the menu and the other single files, the lists (prices, testimonials, tags), the
|
|
5
|
-
// screens under "Virksomheden" and "Blokke", the apps installed and their settings (src/apps/), and the
|
|
6
|
-
// colours ("Farver": colours, readable together). A hidden block is not on the site, so it may be unfinished.
|
|
7
|
-
// The rules of a page's template (collections.ts) too: what may be there, how many, and what must.
|
|
8
|
-
// The redirects (
|
|
9
|
-
// (siteProblems in its rules.ts: a button that opens a promotion not on its page).
|
|
10
|
-
// And the site's texts the editor has no translation of (untranslated).
|
|
11
|
-
import { check } from './check';
|
|
12
|
-
import {
|
|
13
|
-
appearance,
|
|
14
|
-
blockParts,
|
|
15
|
-
collections,
|
|
16
|
-
colorFields,
|
|
17
|
-
colorPairs,
|
|
18
|
-
colorRoles,
|
|
19
|
-
companyParts,
|
|
20
|
-
config,
|
|
21
|
-
frontLeads,
|
|
22
|
-
groups,
|
|
23
|
-
partScreens,
|
|
24
|
-
seoParts,
|
|
25
|
-
siteProblems,
|
|
26
|
-
} from '@site/editor';
|
|
27
|
-
import { colorProblems, lowContrast, MODES } from '../lib/colors';
|
|
28
|
-
import { completeColors, dynamicProblems } from '../lib/dynamic-colors';
|
|
29
|
-
import { APP_HOOKS, hooksOf } from '../lib/hooks';
|
|
30
|
-
import { texts } from '@site/texts';
|
|
31
|
-
import { texts as standardTexts } from '../defaults/texts';
|
|
32
|
-
import type { Part } from './schema';
|
|
33
|
-
import { apps, asStandardTemplate, blockProblems, siteBlocks, standardPageOf, withPageBlocks } from './registry';
|
|
34
|
-
import { installedApps, settingsFile } from './apps';
|
|
35
|
-
import { appFolder, SCREENSHOTS, shotFile, shotOf } from './store';
|
|
36
|
-
import { appFolders } from './appFiles';
|
|
37
|
-
import { colorPairs as shownPairs, sharedParts, translator, writtenApps, writtenBlockParts, writtenCollections } from './site';
|
|
38
|
-
import { splitBlocks } from '../lib/blocks.mjs';
|
|
39
|
-
import { appTexts, DEFAULTS_LOCALE, locales, siteLocale, t, type Texts } from './i18n';
|
|
40
|
-
import { flatten, readAll, REDIRECT_FILES, type AllRedirects } from './
|
|
41
|
-
|
|
42
|
-
const ratio = (n: number) => n.toLocaleString(config.locale, { maximumFractionDigits: 1 });
|
|
43
|
-
|
|
44
|
-
export type SiteFile = { path: string; text: string };
|
|
45
|
-
|
|
46
|
-
/** What is wrong, a line for each file or block ('src/content/pages/x.md: Udfyld "Pris › Pris".'), and
|
|
47
|
-
* how many files and blocks were checked. */
|
|
48
|
-
export function problemsOf(files: SiteFile[]): { problems: string[]; files: number; blocks: number } {
|
|
49
|
-
const problems: string[] = [];
|
|
50
|
-
const byPath = new Map(files.map((f) => [f.path, f]));
|
|
51
|
-
const checked = new Set<string>();
|
|
52
|
-
let blocks = 0;
|
|
53
|
-
|
|
54
|
-
// A data file by the fields it is edited with (at the top or under `at`, over the values it starts
|
|
55
|
-
// with; a file that is only a list under `root`).
|
|
56
|
-
const data = (
|
|
57
|
-
path: string,
|
|
58
|
-
fields: Parameters<typeof check>[0],
|
|
59
|
-
at?: string,
|
|
60
|
-
root?: string,
|
|
61
|
-
defaults?: Record<string, unknown>,
|
|
62
|
-
values?: Record<string, unknown>,
|
|
63
|
-
// An entry of a list as the list has it now (FolderCollection.upgrade: a contact bar is an action bar).
|
|
64
|
-
upgrade?: (value: Record<string, unknown>) => Record<string, unknown>,
|
|
65
|
-
) => {
|
|
66
|
-
const file = byPath.get(path);
|
|
67
|
-
if (!file) return;
|
|
68
|
-
checked.add(path);
|
|
69
|
-
let value: unknown;
|
|
70
|
-
try {
|
|
71
|
-
value = JSON.parse(file.text);
|
|
72
|
-
} catch {
|
|
73
|
-
return problems.push(`${path}: ${t.app.invalidJson}`);
|
|
74
|
-
}
|
|
75
|
-
if (root) value = { [root]: value };
|
|
76
|
-
else if (at) value = { ...defaults, ...((value as Record<string, unknown>)[at] ?? {}) };
|
|
77
|
-
else if (values) value = { ...values, ...(value as Record<string, unknown>) };
|
|
78
|
-
if (upgrade) value = upgrade(value as Record<string, unknown>);
|
|
79
|
-
const problem = check(fields, value as Record<string, unknown>);
|
|
80
|
-
if (problem) problems.push(`${path}: ${problem}`);
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
// With the kinds the apps add to them (a Mailchimp pop-up).
|
|
84
|
-
for (const c of writtenCollections) {
|
|
85
|
-
if (c.kind === 'files')
|
|
86
|
-
for (const f of c.files)
|
|
87
|
-
data(f.file, f.root ? [...f.fields, f.root] : withPageBlocks(f.fields), undefined, f.fields.length ? undefined : f.root?.name);
|
|
88
|
-
if (c.kind === 'folder')
|
|
89
|
-
for (const f of files)
|
|
90
|
-
if (f.path.startsWith(`${c.folder}/`) && f.path.endsWith('.json'))
|
|
91
|
-
data(f.path, c.fields, undefined, undefined, undefined, undefined, c.upgrade && ((v) => c.upgrade!(v, files)));
|
|
92
|
-
}
|
|
93
|
-
for (const part of [
|
|
94
|
-
...companyParts.flatMap((p) => [p, ...(p.parts ?? [])]),
|
|
95
|
-
...partScreens.flatMap((s) => s.parts),
|
|
96
|
-
...appearance,
|
|
97
|
-
...seoParts,
|
|
98
|
-
...writtenBlockParts,
|
|
99
|
-
] as Part[])
|
|
100
|
-
if (part.file && part.fields) data(part.file, part.fields, part.at, undefined, part.defaults);
|
|
101
|
-
// The blocks turned off under "Blokindstillinger" (BlockSettings.tsx): a list of their types.
|
|
102
|
-
data(config.blockSettings, [
|
|
103
|
-
{ name: 'off', label: t.app.blocksOff, type: 'list', field: { name: 'type', label: t.app.block, type: 'string' } },
|
|
104
|
-
]);
|
|
105
|
-
// The apps installed under "Apps" (AppList.tsx): a list of their ids, and each app's settings.
|
|
106
|
-
data(config.apps.installed, [
|
|
107
|
-
{ name: 'installed', label: t.app.appsInstalled, type: 'list', field: { name: 'id', label: t.app.app, type: 'string' } },
|
|
108
|
-
]);
|
|
109
|
-
for (const app of apps)
|
|
110
|
-
if (app.settings) data(settingsFile(app.id), app.settings.fields, undefined, undefined, undefined, app.settings.values);
|
|
111
|
-
// Each app installed is in the site: one from the app store is copied into it when it is installed (store.ts).
|
|
112
|
-
for (const id of installedApps(files))
|
|
113
|
-
if (!apps.some((a) => a.id === id)) problems.push(`${config.apps.installed}: ${t.apps.missing(id, appFolder(id))}`);
|
|
114
|
-
// Each file in an app's hooks/ is a place an app may change the site (@morphemecms/cms/lib/hooks.ts).
|
|
115
|
-
for (const app of writtenApps)
|
|
116
|
-
for (const file of hooksOf(appFolders[app.id]?.files ?? []).unknown)
|
|
117
|
-
problems.push(`${appFolder(app.id)}/${file}: ${t.apps.unknownHook(APP_HOOKS.map((h) => `hooks/${h}.ts`).join(', '))}`);
|
|
118
|
-
// Each picture of an app shows something it has (shotOf) and is in its screenshots/, and each file there is one of them.
|
|
119
|
-
for (const app of writtenApps) {
|
|
120
|
-
const blocks = siteBlocks.filter((b) => b.app === app.id).map((b) => b.type);
|
|
121
|
-
const files = (appFolders[app.id]?.files ?? []).filter((f) => f.startsWith(`${SCREENSHOTS}/`));
|
|
122
|
-
const listed = (app.screenshots ?? []).map((x, i) => `${SCREENSHOTS}/${shotFile(i, x.shows)}`);
|
|
123
|
-
for (const x of app.screenshots ?? [])
|
|
124
|
-
if (!shotOf(x.shows, app, blocks)) problems.push(`${appFolder(app.id)}/index.ts: ${t.apps.unknownScreenshot(x.shows)}`);
|
|
125
|
-
for (const file of listed.filter((f) => !files.includes(f)))
|
|
126
|
-
problems.push(`${appFolder(app.id)}/index.ts: ${t.apps.noScreenshot(file)}`);
|
|
127
|
-
for (const file of files.filter((f) => !listed.includes(f)))
|
|
128
|
-
problems.push(`${appFolder(app.id)}/${file}: ${t.apps.unlistedScreenshot}`);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// The colours under "Farver" (Colors.tsx): every colour in both modes (those a file written before them does not have
|
|
132
|
-
// made, completeColors), readable together, the choice of dynamic colour they were made from, if they were.
|
|
133
|
-
const colorsFile = byPath.get(config.colors);
|
|
134
|
-
if (colorsFile) {
|
|
135
|
-
checked.add(colorsFile.path);
|
|
136
|
-
try {
|
|
137
|
-
const colors = completeColors(JSON.parse(colorsFile.text), colorRoles);
|
|
138
|
-
const bad = colorProblems(
|
|
139
|
-
colors,
|
|
140
|
-
colorFields.map((f) => f.name),
|
|
141
|
-
);
|
|
142
|
-
for (const where of bad) problems.push(`${colorsFile.path}: ${t.colors.notAColorAt(where)}`);
|
|
143
|
-
if (colors.dynamic !== undefined)
|
|
144
|
-
for (const where of dynamicProblems(colors.dynamic)) problems.push(`${colorsFile.path}: ${t.colors.dynamic.wrongAt(where)}`);
|
|
145
|
-
if (!bad.length)
|
|
146
|
-
for (const mode of MODES)
|
|
147
|
-
for (const pair of lowContrast(colors[mode], shownPairs))
|
|
148
|
-
problems.push(`${colorsFile.path}: ${t.colors[mode]}: ${t.colors.tooLow(pair.what, ratio(pair.ratio), ratio(pair.min))}`);
|
|
149
|
-
} catch {
|
|
150
|
-
problems.push(`${colorsFile.path}: ${t.app.invalidJson}`);
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// The redirects in their three files: each straight to where it ends (flatten changes nothing).
|
|
155
|
-
const redirectFiles = files.filter((f) => REDIRECT_FILES.includes(f.path));
|
|
156
|
-
if (redirectFiles.length) {
|
|
157
|
-
redirectFiles.forEach((f) => checked.add(f.path));
|
|
158
|
-
try {
|
|
159
|
-
const all = readAll(redirectFiles.map((f) => ({ ...f, oid: '' })));
|
|
160
|
-
const flat = flatten(all);
|
|
161
|
-
if (typeof flat === 'string') problems.push(flat);
|
|
162
|
-
else for (const line of chainsOf(all, flat)) problems.push(line);
|
|
163
|
-
} catch {
|
|
164
|
-
problems.push(`${config.siteRedirects.map}: ${t.app.invalidJson}`);
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// The blocks in the text of the pages and posts, by the template of their collection.
|
|
169
|
-
for (const f of files) {
|
|
170
|
-
if (!f.path.endsWith('.md')) continue;
|
|
171
|
-
checked.add(f.path);
|
|
172
|
-
blocks += splitBlocks(f.text, null).filter((part) => 'type' in part).length;
|
|
173
|
-
const collection = collections.find((c) => c.kind === 'markdown' && f.path.startsWith(`${c.folder}/`));
|
|
174
|
-
const template = collection?.kind === 'markdown' ? collection.template : undefined;
|
|
175
|
-
// A standard page by its own rules too (the blog page has "Blogindlæg"), and the front page's leads read as the site reads
|
|
176
|
-
// them (packages/editor/site/home.ts).
|
|
177
|
-
const standard = standardPageOf(f, files);
|
|
178
|
-
const front = standard?.key === 'front';
|
|
179
|
-
for (const problem of blockProblems(f.text, asStandardTemplate(template, standard), front ? frontLeads : undefined))
|
|
180
|
-
problems.push(`${f.path}: ${problem}`);
|
|
181
|
-
}
|
|
182
|
-
// The site's own rules (a button that opens a promotion that is not on its page).
|
|
183
|
-
problems.push(...siteProblems(files));
|
|
184
|
-
return { problems, files: checked.size, blocks };
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// A line for each redirect that sends on through another: its file, where it sends and where it ends.
|
|
188
|
-
function chainsOf(all: AllRedirects, flat: AllRedirects): string[] {
|
|
189
|
-
const lines: string[] = [];
|
|
190
|
-
const compare = (file: string, was: Record<string, string>, now: Record<string, string>) => {
|
|
191
|
-
for (const [from, to] of Object.entries(was)) if (now[from] !== to) lines.push(`${file}: ${t.redirects.chain(from, to, now[from])}`);
|
|
192
|
-
};
|
|
193
|
-
compare(config.redirects, all.own, flat.own);
|
|
194
|
-
compare(config.siteRedirects.map, all.map.paths, flat.map.paths);
|
|
195
|
-
compare(config.siteRedirects.map, all.map.ids, flat.map.ids);
|
|
196
|
-
all.rules.forEach((line, i) => {
|
|
197
|
-
if (line !== flat.rules[i]) lines.push(`${config.siteRedirects.rules}: ${t.redirects.chainRule(line.trim(), flat.rules[i].trim())}`);
|
|
198
|
-
});
|
|
199
|
-
return lines;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
/** The site's own texts with no translation, by the editor's other languages, by the file they belong
|
|
203
|
-
* in: the site's in @site/texts, a block's in its texts.ts (or the site's, for what it takes from
|
|
204
|
-
* the site). The editor shows them as they are written. Not the ones said in code (the messages in
|
|
205
|
-
* rules.ts, the lines on the blocks' cards). */
|
|
206
|
-
export function untranslated(): Record<string, Record<string, string[]>> {
|
|
207
|
-
// The editor shows a site's definitions translated already on a site in another language than Danish, so a text the walk
|
|
208
|
-
// meets is traced back to the one it was translated from (the translations' tables are by it). A text is written in the
|
|
209
|
-
// package's language when the package's standard parts or an app have it in Danish (defaults/texts.ts, an app's
|
|
210
|
-
// texts.ts), else in the site's; it needs a translation in each other language.
|
|
211
|
-
const tables = [standardTexts, ...Object.values(appTexts)].map((table) => Object.entries(table['en-US'] ?? {}));
|
|
212
|
-
const danish = new Set(tables.flatMap((entries) => entries.map(([text]) => text)));
|
|
213
|
-
const original = new Map(tables.flatMap((entries) => entries.map(([text, translated]) => [translated, text] as const)));
|
|
214
|
-
const writtenIn = (text: string) => (danish.has(text) ? DEFAULTS_LOCALE : siteLocale);
|
|
215
|
-
const missing = (l: string, own: Record<string, string>, walk: (w: ReturnType<typeof translator>) => void) => {
|
|
216
|
-
const found = new Set<string>();
|
|
217
|
-
walk(
|
|
218
|
-
translator((shown) => {
|
|
219
|
-
const text = original.get(shown) ?? shown;
|
|
220
|
-
if (writtenIn(text) !== l && !(text in own)) found.add(text);
|
|
221
|
-
return shown;
|
|
222
|
-
}),
|
|
223
|
-
);
|
|
224
|
-
return [...found];
|
|
225
|
-
};
|
|
226
|
-
return Object.fromEntries(
|
|
227
|
-
locales
|
|
228
|
-
.filter((l) => l !== siteLocale || l !== DEFAULTS_LOCALE)
|
|
229
|
-
.map((l) => {
|
|
230
|
-
const site = (texts as Texts)[l] ?? {};
|
|
231
|
-
const files: Record<string, string[]> = {
|
|
232
|
-
'src/site/texts.ts': missing(l, site, (w) => {
|
|
233
|
-
collections.forEach(w.collection);
|
|
234
|
-
[...companyParts, ...partScreens.flatMap((s) => s.parts), ...appearance, ...seoParts, ...blockParts].forEach(w.part);
|
|
235
|
-
partScreens.forEach((s) => w.group({ title: s.label, ids: [] }));
|
|
236
|
-
groups.forEach(w.group);
|
|
237
|
-
colorFields.forEach(w.color);
|
|
238
|
-
colorPairs.forEach(w.pair);
|
|
239
|
-
}),
|
|
240
|
-
};
|
|
241
|
-
// An app's block's own texts, and the fields of its shared texts (shared.ts), in its texts.ts.
|
|
242
|
-
for (const block of siteBlocks.filter((b) => b.app))
|
|
243
|
-
files[`src/apps/${block.app}/blocks/${block.type}/texts.ts`] = missing(
|
|
244
|
-
l,
|
|
245
|
-
{ ...site, ...appTexts[`blocks/${block.type}`]?.[l] },
|
|
246
|
-
(w) => {
|
|
247
|
-
w.block(block);
|
|
248
|
-
sharedParts.filter((p) => p.id === block.type).forEach(w.part);
|
|
249
|
-
},
|
|
250
|
-
);
|
|
251
|
-
// An app's name, what it does and its settings, in its texts.ts.
|
|
252
|
-
for (const app of apps) files[`src/apps/${app.id}/texts.ts`] = missing(l, { ...site, ...appTexts[app.id]?.[l] }, (w) => w.app(app));
|
|
253
|
-
return [l, Object.fromEntries(Object.entries(files).filter(([, list]) => list.length))];
|
|
254
|
-
}),
|
|
255
|
-
);
|
|
256
|
-
}
|
|
1
|
+
// Everything the editor writes, checked by the editor's own rules (check.ts) before the site is built
|
|
2
|
+
// (packages/editor/scripts/check-content.mjs): every block in the text of the pages and posts (src/apps/<app>/blocks/, an unknown
|
|
3
|
+
// one too), and the data files with the fields the editor edits them with: the fixed pages with their
|
|
4
|
+
// sections, the menu and the other single files, the lists (prices, testimonials, tags), the
|
|
5
|
+
// screens under "Virksomheden" and "Blokke", the apps installed and their settings (src/apps/), and the
|
|
6
|
+
// colours ("Farver": colours, readable together). A hidden block is not on the site, so it may be unfinished.
|
|
7
|
+
// The rules of a page's template (collections.ts) too: what may be there, how many, and what must.
|
|
8
|
+
// The redirects (redirectData.ts): none sends on through another, and none in a loop. And the site's own rules
|
|
9
|
+
// (siteProblems in its rules.ts: a button that opens a promotion not on its page).
|
|
10
|
+
// And the site's texts the editor has no translation of (untranslated).
|
|
11
|
+
import { check } from './check';
|
|
12
|
+
import {
|
|
13
|
+
appearance,
|
|
14
|
+
blockParts,
|
|
15
|
+
collections,
|
|
16
|
+
colorFields,
|
|
17
|
+
colorPairs,
|
|
18
|
+
colorRoles,
|
|
19
|
+
companyParts,
|
|
20
|
+
config,
|
|
21
|
+
frontLeads,
|
|
22
|
+
groups,
|
|
23
|
+
partScreens,
|
|
24
|
+
seoParts,
|
|
25
|
+
siteProblems,
|
|
26
|
+
} from '@site/editor';
|
|
27
|
+
import { colorProblems, lowContrast, MODES } from '../lib/colors';
|
|
28
|
+
import { completeColors, dynamicProblems } from '../lib/dynamic-colors';
|
|
29
|
+
import { APP_HOOKS, hooksOf } from '../lib/hooks';
|
|
30
|
+
import { texts } from '@site/texts';
|
|
31
|
+
import { texts as standardTexts } from '../defaults/texts';
|
|
32
|
+
import type { Part } from './schema';
|
|
33
|
+
import { apps, asStandardTemplate, blockProblems, siteBlocks, standardPageOf, withPageBlocks } from './registry';
|
|
34
|
+
import { installedApps, settingsFile } from './apps';
|
|
35
|
+
import { appFolder, SCREENSHOTS, shotFile, shotOf } from './store';
|
|
36
|
+
import { appFolders } from './appFiles';
|
|
37
|
+
import { colorPairs as shownPairs, sharedParts, translator, writtenApps, writtenBlockParts, writtenCollections } from './site';
|
|
38
|
+
import { splitBlocks } from '../lib/blocks.mjs';
|
|
39
|
+
import { appTexts, DEFAULTS_LOCALE, locales, siteLocale, t, type Texts } from './i18n';
|
|
40
|
+
import { flatten, readAll, REDIRECT_FILES, type AllRedirects } from './redirectData';
|
|
41
|
+
|
|
42
|
+
const ratio = (n: number) => n.toLocaleString(config.locale, { maximumFractionDigits: 1 });
|
|
43
|
+
|
|
44
|
+
export type SiteFile = { path: string; text: string };
|
|
45
|
+
|
|
46
|
+
/** What is wrong, a line for each file or block ('src/content/pages/x.md: Udfyld "Pris › Pris".'), and
|
|
47
|
+
* how many files and blocks were checked. */
|
|
48
|
+
export function problemsOf(files: SiteFile[]): { problems: string[]; files: number; blocks: number } {
|
|
49
|
+
const problems: string[] = [];
|
|
50
|
+
const byPath = new Map(files.map((f) => [f.path, f]));
|
|
51
|
+
const checked = new Set<string>();
|
|
52
|
+
let blocks = 0;
|
|
53
|
+
|
|
54
|
+
// A data file by the fields it is edited with (at the top or under `at`, over the values it starts
|
|
55
|
+
// with; a file that is only a list under `root`).
|
|
56
|
+
const data = (
|
|
57
|
+
path: string,
|
|
58
|
+
fields: Parameters<typeof check>[0],
|
|
59
|
+
at?: string,
|
|
60
|
+
root?: string,
|
|
61
|
+
defaults?: Record<string, unknown>,
|
|
62
|
+
values?: Record<string, unknown>,
|
|
63
|
+
// An entry of a list as the list has it now (FolderCollection.upgrade: a contact bar is an action bar).
|
|
64
|
+
upgrade?: (value: Record<string, unknown>) => Record<string, unknown>,
|
|
65
|
+
) => {
|
|
66
|
+
const file = byPath.get(path);
|
|
67
|
+
if (!file) return;
|
|
68
|
+
checked.add(path);
|
|
69
|
+
let value: unknown;
|
|
70
|
+
try {
|
|
71
|
+
value = JSON.parse(file.text);
|
|
72
|
+
} catch {
|
|
73
|
+
return problems.push(`${path}: ${t.app.invalidJson}`);
|
|
74
|
+
}
|
|
75
|
+
if (root) value = { [root]: value };
|
|
76
|
+
else if (at) value = { ...defaults, ...((value as Record<string, unknown>)[at] ?? {}) };
|
|
77
|
+
else if (values) value = { ...values, ...(value as Record<string, unknown>) };
|
|
78
|
+
if (upgrade) value = upgrade(value as Record<string, unknown>);
|
|
79
|
+
const problem = check(fields, value as Record<string, unknown>);
|
|
80
|
+
if (problem) problems.push(`${path}: ${problem}`);
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
// With the kinds the apps add to them (a Mailchimp pop-up).
|
|
84
|
+
for (const c of writtenCollections) {
|
|
85
|
+
if (c.kind === 'files')
|
|
86
|
+
for (const f of c.files)
|
|
87
|
+
data(f.file, f.root ? [...f.fields, f.root] : withPageBlocks(f.fields), undefined, f.fields.length ? undefined : f.root?.name);
|
|
88
|
+
if (c.kind === 'folder')
|
|
89
|
+
for (const f of files)
|
|
90
|
+
if (f.path.startsWith(`${c.folder}/`) && f.path.endsWith('.json'))
|
|
91
|
+
data(f.path, c.fields, undefined, undefined, undefined, undefined, c.upgrade && ((v) => c.upgrade!(v, files)));
|
|
92
|
+
}
|
|
93
|
+
for (const part of [
|
|
94
|
+
...companyParts.flatMap((p) => [p, ...(p.parts ?? [])]),
|
|
95
|
+
...partScreens.flatMap((s) => s.parts),
|
|
96
|
+
...appearance,
|
|
97
|
+
...seoParts,
|
|
98
|
+
...writtenBlockParts,
|
|
99
|
+
] as Part[])
|
|
100
|
+
if (part.file && part.fields) data(part.file, part.fields, part.at, undefined, part.defaults);
|
|
101
|
+
// The blocks turned off under "Blokindstillinger" (BlockSettings.tsx): a list of their types.
|
|
102
|
+
data(config.blockSettings, [
|
|
103
|
+
{ name: 'off', label: t.app.blocksOff, type: 'list', field: { name: 'type', label: t.app.block, type: 'string' } },
|
|
104
|
+
]);
|
|
105
|
+
// The apps installed under "Apps" (AppList.tsx): a list of their ids, and each app's settings.
|
|
106
|
+
data(config.apps.installed, [
|
|
107
|
+
{ name: 'installed', label: t.app.appsInstalled, type: 'list', field: { name: 'id', label: t.app.app, type: 'string' } },
|
|
108
|
+
]);
|
|
109
|
+
for (const app of apps)
|
|
110
|
+
if (app.settings) data(settingsFile(app.id), app.settings.fields, undefined, undefined, undefined, app.settings.values);
|
|
111
|
+
// Each app installed is in the site: one from the app store is copied into it when it is installed (store.ts).
|
|
112
|
+
for (const id of installedApps(files))
|
|
113
|
+
if (!apps.some((a) => a.id === id)) problems.push(`${config.apps.installed}: ${t.apps.missing(id, appFolder(id))}`);
|
|
114
|
+
// Each file in an app's hooks/ is a place an app may change the site (@morphemecms/cms/lib/hooks.ts).
|
|
115
|
+
for (const app of writtenApps)
|
|
116
|
+
for (const file of hooksOf(appFolders[app.id]?.files ?? []).unknown)
|
|
117
|
+
problems.push(`${appFolder(app.id)}/${file}: ${t.apps.unknownHook(APP_HOOKS.map((h) => `hooks/${h}.ts`).join(', '))}`);
|
|
118
|
+
// Each picture of an app shows something it has (shotOf) and is in its screenshots/, and each file there is one of them.
|
|
119
|
+
for (const app of writtenApps) {
|
|
120
|
+
const blocks = siteBlocks.filter((b) => b.app === app.id).map((b) => b.type);
|
|
121
|
+
const files = (appFolders[app.id]?.files ?? []).filter((f) => f.startsWith(`${SCREENSHOTS}/`));
|
|
122
|
+
const listed = (app.screenshots ?? []).map((x, i) => `${SCREENSHOTS}/${shotFile(i, x.shows)}`);
|
|
123
|
+
for (const x of app.screenshots ?? [])
|
|
124
|
+
if (!shotOf(x.shows, app, blocks)) problems.push(`${appFolder(app.id)}/index.ts: ${t.apps.unknownScreenshot(x.shows)}`);
|
|
125
|
+
for (const file of listed.filter((f) => !files.includes(f)))
|
|
126
|
+
problems.push(`${appFolder(app.id)}/index.ts: ${t.apps.noScreenshot(file)}`);
|
|
127
|
+
for (const file of files.filter((f) => !listed.includes(f)))
|
|
128
|
+
problems.push(`${appFolder(app.id)}/${file}: ${t.apps.unlistedScreenshot}`);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// The colours under "Farver" (Colors.tsx): every colour in both modes (those a file written before them does not have
|
|
132
|
+
// made, completeColors), readable together, the choice of dynamic colour they were made from, if they were.
|
|
133
|
+
const colorsFile = byPath.get(config.colors);
|
|
134
|
+
if (colorsFile) {
|
|
135
|
+
checked.add(colorsFile.path);
|
|
136
|
+
try {
|
|
137
|
+
const colors = completeColors(JSON.parse(colorsFile.text), colorRoles);
|
|
138
|
+
const bad = colorProblems(
|
|
139
|
+
colors,
|
|
140
|
+
colorFields.map((f) => f.name),
|
|
141
|
+
);
|
|
142
|
+
for (const where of bad) problems.push(`${colorsFile.path}: ${t.colors.notAColorAt(where)}`);
|
|
143
|
+
if (colors.dynamic !== undefined)
|
|
144
|
+
for (const where of dynamicProblems(colors.dynamic)) problems.push(`${colorsFile.path}: ${t.colors.dynamic.wrongAt(where)}`);
|
|
145
|
+
if (!bad.length)
|
|
146
|
+
for (const mode of MODES)
|
|
147
|
+
for (const pair of lowContrast(colors[mode], shownPairs))
|
|
148
|
+
problems.push(`${colorsFile.path}: ${t.colors[mode]}: ${t.colors.tooLow(pair.what, ratio(pair.ratio), ratio(pair.min))}`);
|
|
149
|
+
} catch {
|
|
150
|
+
problems.push(`${colorsFile.path}: ${t.app.invalidJson}`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// The redirects in their three files: each straight to where it ends (flatten changes nothing).
|
|
155
|
+
const redirectFiles = files.filter((f) => REDIRECT_FILES.includes(f.path));
|
|
156
|
+
if (redirectFiles.length) {
|
|
157
|
+
redirectFiles.forEach((f) => checked.add(f.path));
|
|
158
|
+
try {
|
|
159
|
+
const all = readAll(redirectFiles.map((f) => ({ ...f, oid: '' })));
|
|
160
|
+
const flat = flatten(all);
|
|
161
|
+
if (typeof flat === 'string') problems.push(flat);
|
|
162
|
+
else for (const line of chainsOf(all, flat)) problems.push(line);
|
|
163
|
+
} catch {
|
|
164
|
+
problems.push(`${config.siteRedirects.map}: ${t.app.invalidJson}`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// The blocks in the text of the pages and posts, by the template of their collection.
|
|
169
|
+
for (const f of files) {
|
|
170
|
+
if (!f.path.endsWith('.md')) continue;
|
|
171
|
+
checked.add(f.path);
|
|
172
|
+
blocks += splitBlocks(f.text, null).filter((part) => 'type' in part).length;
|
|
173
|
+
const collection = collections.find((c) => c.kind === 'markdown' && f.path.startsWith(`${c.folder}/`));
|
|
174
|
+
const template = collection?.kind === 'markdown' ? collection.template : undefined;
|
|
175
|
+
// A standard page by its own rules too (the blog page has "Blogindlæg"), and the front page's leads read as the site reads
|
|
176
|
+
// them (packages/editor/site/home.ts).
|
|
177
|
+
const standard = standardPageOf(f, files);
|
|
178
|
+
const front = standard?.key === 'front';
|
|
179
|
+
for (const problem of blockProblems(f.text, asStandardTemplate(template, standard), front ? frontLeads : undefined))
|
|
180
|
+
problems.push(`${f.path}: ${problem}`);
|
|
181
|
+
}
|
|
182
|
+
// The site's own rules (a button that opens a promotion that is not on its page).
|
|
183
|
+
problems.push(...siteProblems(files));
|
|
184
|
+
return { problems, files: checked.size, blocks };
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// A line for each redirect that sends on through another: its file, where it sends and where it ends.
|
|
188
|
+
function chainsOf(all: AllRedirects, flat: AllRedirects): string[] {
|
|
189
|
+
const lines: string[] = [];
|
|
190
|
+
const compare = (file: string, was: Record<string, string>, now: Record<string, string>) => {
|
|
191
|
+
for (const [from, to] of Object.entries(was)) if (now[from] !== to) lines.push(`${file}: ${t.redirects.chain(from, to, now[from])}`);
|
|
192
|
+
};
|
|
193
|
+
compare(config.redirects, all.own, flat.own);
|
|
194
|
+
compare(config.siteRedirects.map, all.map.paths, flat.map.paths);
|
|
195
|
+
compare(config.siteRedirects.map, all.map.ids, flat.map.ids);
|
|
196
|
+
all.rules.forEach((line, i) => {
|
|
197
|
+
if (line !== flat.rules[i]) lines.push(`${config.siteRedirects.rules}: ${t.redirects.chainRule(line.trim(), flat.rules[i].trim())}`);
|
|
198
|
+
});
|
|
199
|
+
return lines;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/** The site's own texts with no translation, by the editor's other languages, by the file they belong
|
|
203
|
+
* in: the site's in @site/texts, a block's in its texts.ts (or the site's, for what it takes from
|
|
204
|
+
* the site). The editor shows them as they are written. Not the ones said in code (the messages in
|
|
205
|
+
* rules.ts, the lines on the blocks' cards). */
|
|
206
|
+
export function untranslated(): Record<string, Record<string, string[]>> {
|
|
207
|
+
// The editor shows a site's definitions translated already on a site in another language than Danish, so a text the walk
|
|
208
|
+
// meets is traced back to the one it was translated from (the translations' tables are by it). A text is written in the
|
|
209
|
+
// package's language when the package's standard parts or an app have it in Danish (defaults/texts.ts, an app's
|
|
210
|
+
// texts.ts), else in the site's; it needs a translation in each other language.
|
|
211
|
+
const tables = [standardTexts, ...Object.values(appTexts)].map((table) => Object.entries(table['en-US'] ?? {}));
|
|
212
|
+
const danish = new Set(tables.flatMap((entries) => entries.map(([text]) => text)));
|
|
213
|
+
const original = new Map(tables.flatMap((entries) => entries.map(([text, translated]) => [translated, text] as const)));
|
|
214
|
+
const writtenIn = (text: string) => (danish.has(text) ? DEFAULTS_LOCALE : siteLocale);
|
|
215
|
+
const missing = (l: string, own: Record<string, string>, walk: (w: ReturnType<typeof translator>) => void) => {
|
|
216
|
+
const found = new Set<string>();
|
|
217
|
+
walk(
|
|
218
|
+
translator((shown) => {
|
|
219
|
+
const text = original.get(shown) ?? shown;
|
|
220
|
+
if (writtenIn(text) !== l && !(text in own)) found.add(text);
|
|
221
|
+
return shown;
|
|
222
|
+
}),
|
|
223
|
+
);
|
|
224
|
+
return [...found];
|
|
225
|
+
};
|
|
226
|
+
return Object.fromEntries(
|
|
227
|
+
locales
|
|
228
|
+
.filter((l) => l !== siteLocale || l !== DEFAULTS_LOCALE)
|
|
229
|
+
.map((l) => {
|
|
230
|
+
const site = (texts as Texts)[l] ?? {};
|
|
231
|
+
const files: Record<string, string[]> = {
|
|
232
|
+
'src/site/texts.ts': missing(l, site, (w) => {
|
|
233
|
+
collections.forEach(w.collection);
|
|
234
|
+
[...companyParts, ...partScreens.flatMap((s) => s.parts), ...appearance, ...seoParts, ...blockParts].forEach(w.part);
|
|
235
|
+
partScreens.forEach((s) => w.group({ title: s.label, ids: [] }));
|
|
236
|
+
groups.forEach(w.group);
|
|
237
|
+
colorFields.forEach(w.color);
|
|
238
|
+
colorPairs.forEach(w.pair);
|
|
239
|
+
}),
|
|
240
|
+
};
|
|
241
|
+
// An app's block's own texts, and the fields of its shared texts (shared.ts), in its texts.ts.
|
|
242
|
+
for (const block of siteBlocks.filter((b) => b.app))
|
|
243
|
+
files[`src/apps/${block.app}/blocks/${block.type}/texts.ts`] = missing(
|
|
244
|
+
l,
|
|
245
|
+
{ ...site, ...appTexts[`blocks/${block.type}`]?.[l] },
|
|
246
|
+
(w) => {
|
|
247
|
+
w.block(block);
|
|
248
|
+
sharedParts.filter((p) => p.id === block.type).forEach(w.part);
|
|
249
|
+
},
|
|
250
|
+
);
|
|
251
|
+
// An app's name, what it does and its settings, in its texts.ts.
|
|
252
|
+
for (const app of apps) files[`src/apps/${app.id}/texts.ts`] = missing(l, { ...site, ...appTexts[app.id]?.[l] }, (w) => w.app(app));
|
|
253
|
+
return [l, Object.fromEntries(Object.entries(files).filter(([, list]) => list.length))];
|
|
254
|
+
}),
|
|
255
|
+
);
|
|
256
|
+
}
|
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
// The visitors' answers in the cookie banner, as the site's Worker keeps them (packages/editor/worker/consent.js, /api/consent on the
|
|
2
|
-
// editor's own address), for "Samtykker" under "Privatliv" (Consents.tsx). Asked with the GitHub sign-in (the Worker's
|
|
3
|
-
// cookie, sent with the request): the Worker lets only someone who can save the site see or delete them.
|
|
4
|
-
import type { ConsentCategory } from '../lib/cookieCategories.mjs';
|
|
5
|
-
|
|
6
|
-
/** An answer: the visitor's consent id, when (ISO), the yes or no to each category the banner asked about, and the
|
|
7
|
-
* commit the site was built from (the banner they answered). */
|
|
8
|
-
export type ConsentAnswer = { id: string; at: string; choices: Partial<Record<ConsentCategory, boolean>>; version: string };
|
|
9
|
-
|
|
10
|
-
export const CONSENT_API = '/api/consent';
|
|
11
|
-
|
|
12
|
-
/** The newest answers (at most 500), those of a consent id starting with `query`, and how many there are in all; null
|
|
13
|
-
* when the site does not keep them (no Worker, no storage). */
|
|
14
|
-
export async function loadConsents(query = ''): Promise<{ entries: ConsentAnswer[]; total: number } | null> {
|
|
15
|
-
const res = await fetch(`${CONSENT_API}${query ? `?q=${encodeURIComponent(query)}` : ''}`, { cache: 'no-store' });
|
|
16
|
-
if (res.status === 503 || !res.headers.get('content-type')?.includes('application/json')) return null;
|
|
17
|
-
if (!res.ok) throw new Error(String(res.status));
|
|
18
|
-
return (await res.json()) as { entries: ConsentAnswer[]; total: number };
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/** Deletes every answer with a consent id (a visitor asked for it); how many. */
|
|
22
|
-
export async function deleteConsents(id: string): Promise<number> {
|
|
23
|
-
const res = await fetch(CONSENT_API, { method: 'DELETE', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ id }) });
|
|
24
|
-
if (!res.ok) throw new Error(String(res.status));
|
|
25
|
-
return ((await res.json()) as { deleted: number }).deleted;
|
|
26
|
-
}
|
|
1
|
+
// The visitors' answers in the cookie banner, as the site's Worker keeps them (packages/editor/worker/consent.js, /api/consent on the
|
|
2
|
+
// editor's own address), for "Samtykker" under "Privatliv" (Consents.tsx). Asked with the GitHub sign-in (the Worker's
|
|
3
|
+
// cookie, sent with the request): the Worker lets only someone who can save the site see or delete them.
|
|
4
|
+
import type { ConsentCategory } from '../lib/cookieCategories.mjs';
|
|
5
|
+
|
|
6
|
+
/** An answer: the visitor's consent id, when (ISO), the yes or no to each category the banner asked about, and the
|
|
7
|
+
* commit the site was built from (the banner they answered). */
|
|
8
|
+
export type ConsentAnswer = { id: string; at: string; choices: Partial<Record<ConsentCategory, boolean>>; version: string };
|
|
9
|
+
|
|
10
|
+
export const CONSENT_API = '/api/consent';
|
|
11
|
+
|
|
12
|
+
/** The newest answers (at most 500), those of a consent id starting with `query`, and how many there are in all; null
|
|
13
|
+
* when the site does not keep them (no Worker, no storage). */
|
|
14
|
+
export async function loadConsents(query = ''): Promise<{ entries: ConsentAnswer[]; total: number } | null> {
|
|
15
|
+
const res = await fetch(`${CONSENT_API}${query ? `?q=${encodeURIComponent(query)}` : ''}`, { cache: 'no-store' });
|
|
16
|
+
if (res.status === 503 || !res.headers.get('content-type')?.includes('application/json')) return null;
|
|
17
|
+
if (!res.ok) throw new Error(String(res.status));
|
|
18
|
+
return (await res.json()) as { entries: ConsentAnswer[]; total: number };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Deletes every answer with a consent id (a visitor asked for it); how many. */
|
|
22
|
+
export async function deleteConsents(id: string): Promise<number> {
|
|
23
|
+
const res = await fetch(CONSENT_API, { method: 'DELETE', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ id }) });
|
|
24
|
+
if (!res.ok) throw new Error(String(res.status));
|
|
25
|
+
return ((await res.json()) as { deleted: number }).deleted;
|
|
26
|
+
}
|