From 8202a3a7f591fc71975c59c35751f232ba28e19d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9cate=20Kleidukos?= Date: Fri, 2 Aug 2024 23:23:45 +0200 Subject: [PATCH] Prepare the blog --- .github/workflows/deploy.yml | 43 ++ .gitignore | 15 + CNAME | 1 + README.md | 53 +++ config.toml | 311 +++++++++++++ ...2024-08-03-documentation-best-practices.md | 137 ++++++ content/_index.md | 5 + content/archive/_index.md | 6 + content/pages/_index.md | 3 + content/pages/about.md | 16 + content/pages/privacy.md | 15 + content/static/stork_toml.md | 5 + content/static/tinysearch_json.md | 5 + package.json | 20 + package_abridge.js | 419 ++++++++++++++++++ sass/_extra.scss | 26 ++ sass/abridge.scss | 181 ++++++++ sass/fonts/_Ralewway.scss | 16 + sass/fonts/_SourceSans.scss | 8 + sass/fonts/_UbuntuMono.scss | 8 + static/CNAME | 1 + static/android-chrome-192x192.png | Bin 0 -> 2832 bytes static/android-chrome-512x512.png | Bin 0 -> 22233 bytes static/apple-touch-icon.png | Bin 0 -> 2605 bytes static/browserconfig.xml | 9 + static/favicon-32x32.png | Bin 0 -> 1105 bytes static/favicon.ico | Bin 0 -> 15086 bytes static/favicon.svg | 6 + static/fonts/raleway-v28-latin-700.woff2 | Bin 0 -> 21440 bytes static/fonts/raleway-v28-latin-900.woff2 | Bin 0 -> 20696 bytes .../source-sans-3-v9-latin-regular.woff2 | Bin 0 -> 15008 bytes .../fonts/ubuntu-mono-v15-latin-regular.woff2 | Bin 0 -> 27384 bytes static/images/haskell.svg | 6 + static/js/abridge.min.js | 1 + static/js/abridge_nopwa.min.js | 1 + static/js/searchChange.js | 68 +++ static/js/sw_load.js | 14 + static/js/sw_load.min.js | 1 + static/manifest.json | 19 + static/manifest.min.json | 1 + static/mstile-150x150.png | Bin 0 -> 1608 bytes static/safari-pinned-tab.svg | 271 +++++++++++ static/sw.js | 191 ++++++++ static/sw.min.js | 1 + templates/authors/list.html | 33 ++ templates/authors/single.html | 30 ++ 46 files changed, 1946 insertions(+) create mode 100644 .github/workflows/deploy.yml create mode 100644 .gitignore create mode 100644 CNAME create mode 100644 README.md create mode 100644 config.toml create mode 100644 content/2024-08-03-documentation-best-practices.md create mode 100644 content/_index.md create mode 100644 content/archive/_index.md create mode 100644 content/pages/_index.md create mode 100644 content/pages/about.md create mode 100644 content/pages/privacy.md create mode 100644 content/static/stork_toml.md create mode 100644 content/static/tinysearch_json.md create mode 100644 package.json create mode 100644 package_abridge.js create mode 100644 sass/_extra.scss create mode 100644 sass/abridge.scss create mode 100644 sass/fonts/_Ralewway.scss create mode 100644 sass/fonts/_SourceSans.scss create mode 100644 sass/fonts/_UbuntuMono.scss create mode 100644 static/CNAME create mode 100644 static/android-chrome-192x192.png create mode 100644 static/android-chrome-512x512.png create mode 100644 static/apple-touch-icon.png create mode 100644 static/browserconfig.xml create mode 100644 static/favicon-32x32.png create mode 100644 static/favicon.ico create mode 100644 static/favicon.svg create mode 100644 static/fonts/raleway-v28-latin-700.woff2 create mode 100644 static/fonts/raleway-v28-latin-900.woff2 create mode 100644 static/fonts/source-sans-3-v9-latin-regular.woff2 create mode 100644 static/fonts/ubuntu-mono-v15-latin-regular.woff2 create mode 100644 static/images/haskell.svg create mode 100644 static/js/abridge.min.js create mode 100644 static/js/abridge_nopwa.min.js create mode 100644 static/js/searchChange.js create mode 100644 static/js/sw_load.js create mode 100644 static/js/sw_load.min.js create mode 100644 static/manifest.json create mode 100644 static/manifest.min.json create mode 100644 static/mstile-150x150.png create mode 100644 static/safari-pinned-tab.svg create mode 100644 static/sw.js create mode 100644 static/sw.min.js create mode 100644 templates/authors/list.html create mode 100644 templates/authors/single.html diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..7182ba7 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,43 @@ +on: + push: + branches: + - main + pull_request: +jobs: + build: + runs-on: ubuntu-latest + if: github.ref != 'refs/heads/main' + steps: + - name: 'Checkout' + uses: actions/checkout@main + - run: touch site/.nojekyll + - uses: actions/setup-node@v4 + with: + node-version: 20 + - run: npm install + - run: npm run abridge + - name: 'Build only' + uses: shalzz/zola-deploy-action@0.19 + env: + BUILD_DIR: . + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + BUILD_ONLY: true + + build_and_deploy: + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/main' + steps: + - name: 'Checkout' + uses: actions/checkout@main + - run: touch site/.nojekyll + - uses: actions/setup-node@v4 + with: + node-version: 20 + - run: npm install + - run: npm run abridge + - name: 'Build and deploy' + uses: shalzz/zola-deploy-action@0.19 + env: + PAGES_BRANCH: gh-pages + BUILD_DIR: . + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4929e66 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +.env +public +themes +build +storage +node_modules +package-lock.json +static/demo.html +static/tinysearch_engine.js +static/tinysearch_engine.d.ts +static/tinysearch_engine_bg.wasm.d.ts +static/package.json +static/js/pagefind.*.pf_meta +static/js/index +static/js/fragment \ No newline at end of file diff --git a/CNAME b/CNAME new file mode 100644 index 0000000..e02bff5 --- /dev/null +++ b/CNAME @@ -0,0 +1 @@ +blog.haskell.org diff --git a/README.md b/README.md new file mode 100644 index 0000000..1b19050 --- /dev/null +++ b/README.md @@ -0,0 +1,53 @@ +# Haskell Blog + +## Local installation + +The blog is made with [Zola], based on the [Abridge theme]. + +You will need: + * The `zola` binary v0.19.1 or higher + * `npm` + +Once you have cloned this repository, run `npm install` and `npm run abridge` to initialise the front-end features like full-text search. + +## Contribute content + +### Blog post structure + +#### Name + +Blog posts are located in the `content` directory, as markdown files. The files themselves start with the (expected) date of publication +and contain their title in a "slug" format (alphanumeric characters, words separated by a hyphen, all lowercase): + +``` +2024-08-03-documentation-best-practices.md +``` + +#### Front-matter + +The file itself must contain a front-matter in TOML format that has the following properties: + +``` ++++ +title = "Title of the post" +date = YYYY-MM-DD +[taxonomies] +authors = ["Author's Name"] # The author's name. Will be indexed. +categories = ["Haddock"] # A minima should be the name of the team ("Haddock", "HLS", "Cabal"). Will be indexed. +tags = ["Practices"] # Something more precise like "Release", "Practice", "JavaScript", can be aded. Will be indexed. ++++ +``` + +#### Summary + +The eye-catcher that you wish to show on the front-page of the blog is the first paragraph. +You can manually delimit where it ends by inserting `` on a newline between this paragraph and the rest of the content. + +Otherwise, in the absence of this comment, the first 150 characters will be used by Zola + +### Local preview + +Run `zola serve --drafts` in order to serve the website and automatically render it when files change. + +[Zola]: https://www.getzola.org/ +[Abridge theme]: https://abridge.pages.dev/overview-abridge/ diff --git a/config.toml b/config.toml new file mode 100644 index 0000000..70cbef3 --- /dev/null +++ b/config.toml @@ -0,0 +1,311 @@ +# do NOT include a trailing slash on the base URL +base_url = "https://blog.haskell.org" +# Usable site directly from disk, Including Search: "/home/jieiku/.dev/abridge/public" +# Also set index format = "elasticlunr_javascript", and in [extra] uglyurls = true, integrity = false +# If you use the npm/node script then all you have to do is set offline = true, and everything else is automatic. + +title = "The Haskell Programming Language's blog" +description = "" +theme = "abridge" + +default_language = "en" +compile_sass = true +minify_html = false +build_search_index = true +generate_feeds = true +taxonomies = [ + {name = "categories", feed = true}, + {name = "authors"} +] + +#feed_filename = "atom.xml" +#output_dir = "public" +#ignored_content = ["*.{graphml,xlsx}", "temp.*"] +#hard_link_static = false # set to true to hard link instead of copying, useful for very large files. + +[search] # Options specific to elasticlunr search. +# index format can be: elasticlunr_json or elasticlunr_javascript or fuse_json +index_format = "elasticlunr_json" +include_title = true # include title of page/section in index +include_description = true # include description of page/section in index +include_content = true # include rendered content of page/section in index +# truncate_content_length = 100 # Truncate at nth character. May be useful if index is getting too large. + +[markdown] +highlight_code = true +highlight_theme = "css" +render_emoji = false +external_links_target_blank = true # rel="noopener" +external_links_no_follow = false # rel="nofollow" +external_links_no_referrer = false # rel="noreferrer" +smart_punctuation = false # `...` to `…`, `"quote"` to `“curly”` etc + +[extra] +############################################################################### +### Layout & Format +### position: top, bottom, both, false(hidden) +### size: s150, s140, s130, s120, s110, s95, s90, s85, s80, s75, s70, false(full size) +### divider: " " "·" "•" +############################################################################### + +menu = [ + {url = "https://haskell.org", name = "Haskell", slash = false, size = "s110"}, + {url = "archive", name = "Posts", slash = true, blank = false, size="s110"}, + {url = "categories", name = "Categories", slash = true, blank = false, size="s110"}, + {url = "about", name = "About", slash = true, blank = false, size="s110"}, +] + +toc = true +recent = true # TOC / index +recent_items = 15 +series = true +series_items = 9 # Max number of items to display in series list, use 0 to disable +#Series_parts = "$NUMBER_OF_PARTS part Series" + +#arrow_pagination = true # If set to true then the pagination will use the old arrows mode, be sure to also enable the icons. + +meta_index = { position="bottom", size="s90", author=false, readtime=false, readstring="min", date=true, updated=false, categories_tags=true, divider="" } +meta_post = { position="top", size="s95", author=true, readtime=false, readstring="min read", date=true, updated=true, categories_tags=true, divider="" } + +hide_section_dates = false # hides the date for sections that use posts.html as their template. +hide_page_nextprev_titles = false # hides the next/previous titles for pages that use page.html as their template. +title_size_index = "s85" +footer_size = "s80" # the size of the copyright and powered by text +footer_credit = true +#footer_credit_override = '

Powered by Zola & Abridge

' +#archive_reverse = true # Set to True to sort posts chronologically per year instead of newest first on the archive page: example.com/archive/ + +### Uncomment one of the below lines, or neither, depending on which type of logo you want to use: +logo = { file="images/haskell.svg", width="40", height="28", alt="Haskell", text="Haskell Blog" } + +sitedesc = false # enables or disables the display of the site description below the logo. +headhr = false # show or hide horizontal rule below header +foothr = false # show or hide horizontal rule above footer + +### $CURRENT_YEAR and $SITE_TITLE can be used anywhere within the copyright, you can change their position or you can also delete them and type whatever you want instead. +#copyright = false # set to false to disable the copyright. +#copyright_override = '© 2019-$CURRENT_YEAR $SITE_TITLE' +#copyright_override = '© $CURRENT_YEAR $SITE_TITLE • Website content is licensed CC BY 4.0.' + + +############################################################################### +### meta/seo/analytic tags +### To disable title_addition but keep built in page additions eg, Posts page: "Posts | Abridge" set title_addition to an empty string +### To always set the page title exactly eg "Abridge" then comment out title_addition or set to false (not recommend, bad for SEO) +############################################################################### + +title_separator = "|" # Separator between title and title_addition, set as |, -, _, etc +title_addition = "Fast & Lightweight Zola Theme" # a default value for title addition +author = "Haskell.org" +keywords = "haskell, functional programming, blog" # used for the primary site index +banner = "banner.png" # Used as default image for OpenGraph/Twitter if page specific image is undefined. +seo = true # enable or disable seo-related meta tags: opengraph, facebook, twitter +#dev = false # development mode, if true then robots.txt should prevent search indexing. + +#head_extra = '' + + +############################################################################### +### Footer social links; these are used in macros/social.html +### https://github.com/Jieiku/abridge/blob/master/templates/macros/social.html +############################################################################### + +feed = true # this adds the RSS feed icon in the footer. +mastodon = "https://fosstodon.org/@haskell" +twitter = "haskellOrg" +github = "haskell" + + +############################################################################### +### Resource Files +### You can load extra css files if you need to, just separate by comma: +### stylesheets = [ "abridge.css", "extra.css" ] +### search_library, library to use. valid values: +### false, "elasticlunr", "tinysearch", "stork", "pagefind" +### offline: implies uglyurls=true and integrity=false, when true NPM/node will +### automatically set the path for the base_url, it will build the site, +### then set the base_url back to what it was. This is a way to build a completely +### offline site, a feature not possible with Zola alone. +### The PWA feature is another way to build an offline site, so there are now two +### different ways to build an offline site with Abridge. +### +### For most people the value of online_url will be the same as base_url. +### online_url is used to restore the base_url after generating an Offline site. +### When you set offline = true and run the npm script, the base_url is set to the absolute path on disk. +### Once you set offline = false, the base_url will be set back to the value of online_url when you run the npm script again. +############################################################################### + +# do NOT include a trailing slash on the online URL +online_indexformat = "elasticlunr_json"# used to restore your preferred index format when offline = false +offline = false # implies uglyurls=true and integrity=false, when true NPM/node will automatically set the path for the base_url, it will build the site, then set the base_url back to what it was. + +uglyurls = false # if set to true then links are generated with the full path. eg https://abridge.netlify.app/index.html +integrity = true # increases site security, should normally be true. (setting to false is useful during js development) +js_bundle = true # multiple javascript files combined into a single file (setting to false is useful during js development) + +js_copycode = true # The copy button on code blocks that allows you to copy them to the clipboard. +js_email_encode = true # obfuscates email address in footer +js_prestyle = true # used to preload: FontAwesome, Katex, external Google Fonts +js_switcher = false # The button that allows manually changing between light/dark mode. +js_switcher_default = "light" # default nojs switcher mode: dark, light (make sure to also set $switcherDefault in abridge.scss) + +search_library = "elasticlunr" +stylesheets = ["abridge.css"] + +webmanifest = "manifest.min.json" # Required for PWAs + + +############################################################################### +### PWA (Progressive Web Application) +### By default Abridge has pwa_NORM_TTL and pwa_LONG_TTL set to 0, this essential turns the PWA cache strategy into network first. +### Abridge uses cachebust hashing on js and css files, so anytime a page cache is updated, these resources would also get updated if changed. +### Media files rarely change, especially font files, so it is a good idea cache indefinitely. +### For pwa_TTL_EXEMPT indefinitely cached resources, you can force a new cache by incrementing the pwa_VER (cache version number). +### If you would like to try a cache first strategy then set a value higher than 0 for pwa_NORM_TTL and pwa_LONG_TTL. +### The options below other than pwa=true, only come into play when the npm/node script is ran. +############################################################################### + +pwa = true # true to load the service worker +pwa_VER = '3.11.0' # Service Worker cache version. (increment if you need to force a new cache) + +### 3600=1hour, 28800=8hours, 86400=1day, 604800=1week, 1209600=2weeks +pwa_NORM_TTL = 0 # 86400 is reasonable. html, json, xml, anything else undefined +pwa_LONG_TTL = 0 # 604800 is reasonable. + +### list of files that overrides TTL_LONG/TTL_EXEMPT to be a NORM TTL. +pwa_TTL_NORM = '"sw.min.js", "sw_load.min.js"' + +### TTL_LONG file extensions will be cached for the LONG_TTL duration. +pwa_TTL_LONG = '"jpg", "jpeg", "png", "gif", "webp", "avif", "ico", "svg", "xsl", "txt"' + +### TTL_EXEMPT file extensions will be cached indefinitely unless sw_load version is incremented, which would invalidate any existing cache. (and a new cache would be started) +pwa_TTL_EXEMPT = '"js", "css", "otf", "eot", "ttf", "woff", "woff2", "mp4", "webm", "mp3", "ogg"' + +### If set to true then the entire site is cached. (useful for making an entire site usable while offline) +pwa_cache_all = false + +### List of Files for the PWA to initially Cache, used if pwa_cache_all = false +pwa_BASE_CACHE_FILES = "'/js/theme.min.js','/js/theme_light.min.js','/abridge.css','/js/abridge.min.js','/','/404.html','/offline/','/manifest.min.json'" + + +############################################################################### +### Favicons, comment out a line to disable loading some or all of these if needed. +############################################################################### + +favicon_theme_color = "#333333" +favicon_ms_color = "#333333" +favicon_mask = "safari-pinned-tab.svg" # safari-pinned-tab.svg +favicon_mask_color = "#ff9900" +# favicon_svg = "favicon.svg" # favicon.svg +favicon180 = "apple-touch-icon.png" # apple-touch-icon.png +favicon32 = "favicon-32x32.png" # favicon-32x32.png +favicon16 = "favicon-16x16.png" # favicon-16x16.png + + +############################################################################### +### Commenting System for visitors to leave comments on pages. +### hyvor talk +############################################################################### + +#comments.hyvor = "9366" # hyvor website id, comment out to disable. +#comments.hyvorcolor = "os" # set the color property for hyvor + + +############################################################################### +### Icons +### Loading the entire fontawesome icon collection will negatively impact your sites performance. +### For a lightweight solution consider adding only the icons that you need. +### you can load individual scss based svg icons by including them in the _extra.scss file +### https://github.com/Jieiku/abridge/blob/master/COPY-TO-ROOT-SASS/_extra.scss +############################################################################### + +### To disable any of these icons set them to "false" (will default to unicode icons instead) +#icon_search = "svgs svgm search" # Search button in search box. +#icon_adjust = "svgs adjust" # Theme Switcher button in top menu. (add class svgh to change colors on hover) +#icon_first = "svgs svgh angll" # Pagination First Page. +#icon_prev = "svgs svgh angl" # Pagination Previous Page. +#icon_next = "svgs svgh angr" # Pagination Next Page. +#icon_last = "svgs svgh angrr" # Pagination Last Page. +#icon_top = "svgs svgh angu" # Back to Top Button. + +#icon_read = "svgs fa-solid fa-glasses" # displayed in metadata on index and below title on page. +#icon_date = "svgs fa-solid fa-calendar" # displayed in metadata on index and below title on page. +#icon_info = "svgs fa-solid fa-circle-info" # displayed in metadata on index and below title on page for categories/tags +#icon_author = "svgs fa-solid fa-pen-fancy" # displayed in metadata on index and below title on page. + +### Uncomment below line to load fontawesome, eg: +#fontawesome = "https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.1.1/css/all.min.css" +#icon_read = "svgs fa-solid fa-glasses" # displayed in metadata on index and below title on page. +#icon_date = "svgs fa-solid fa-calendar" # displayed in metadata on index and below title on page. +#icon_info = "svgs fa-solid fa-circle-info" # displayed in metadata on index and below title on page for categories/tags +#icon_author = "svgs fa-solid fa-pen-fancy" # displayed in metadata on index and below title on page. + + +############################################################################### +### Security Settings +### Security Headers should preferably be set by your webserver (Nginx, Apache) +### https://observatory.mozilla.org https://csp-evaluator.withgoogle.com/ +### If you are unable to load your security headers with your webserver, this will load a couple of them as meta tags. +### There are many other security related headers most of which can only be set by the webserver method. +### UnComment any of these lines to enable their meta tags. +############################################################################### + +#security_header_referrer = "strict-origin-when-cross-origin" + +#security_header_csp = "default-src 'none'; object-src 'none'; base-uri 'self'; manifest-src 'self'; connect-src 'self'; form-action 'self'; script-src 'self'; img-src 'self' data: cdn.cloudflare.com; frame-src 'self' www.youtube-nocookie.com player.vimeo.com; media-src 'self' data: cdn.cloudflare.com www.youtube-nocookie.com player.vimeo.com; font-src 'self' cdn.cloudflare.com cdn.jsdelivr.net fonts.gstatic.com; style-src 'self' cdn.cloudflare.com cdn.jsdelivr.net fonts.googleapis.com;" + + +############################################################################### +### FONTS - Abridge by default uses the System Font Stack +### https://css-tricks.com/snippets/css/system-font-stack/ +### However if you need you can load a specific font below, +### make sure to have the relevant woff2 fonts in your static/fonts folder +### I measured the least Cumulative Layout Shift with: Roboto, Lato, Arimo +############################################################################### + +### For externally loaded Fonts, make sure to include the FULL url including the https prefix: + +#fonts = [ {url = "https://fonts.googleapis.com/css?family=Roboto:400,700,italic|Roboto+Mono:400,italic"} ] + +### Local fonts are defined in the css, https://github.com/Jieiku/abridge/tree/master/COPY-TO-ROOT-SASS/fonts/_Arimo.scss +### To load a local font resource, look at the bottom of this file: https://github.com/Jieiku/abridge/blob/master/COPY-TO-ROOT-SASS/abridge.scss + +### After loading them in the CSS, You can also define them below and it will add the preload tag to the head. +### preloading fonts will eliminate Content Layout Shift, but will hurt with page load time. (not recommended) + +fonts = [ + {url = "fonts/source-sans-3-v9-latin-regular.woff2"}, + {url = "fonts/ubuntu-mono-v15-latin-regular.woff2"}, + {url = "fonts/raleway-v28-latin-700.woff2"}, + {url = "fonts/raleway-v28-latin-900.woff2"}, +] + +############################################################################### +### Katex - math js library, used to to render mathematical notations +### It's best to enable katex on a per page bases as I did here: +### https://abridge.netlify.app/overview-math/ +### https://github.com/Jieiku/abridge/blob/master/content/overview-math.md?plain=1#L11-L13 +### Otherwise you will load the katex related javascript on every page! +############################################################################### + +#katex_options = "js/katexoptions.js" +#katex_bundle = "js/katexbundle.min.js" + +### Load Katex Local Resources +#katex_css = "katex.min.css" # Fonts - load the css/fonts locally +#katex_js = "js/katex.min.js" # use local js, so that we dont have to whitelist cdn.jsdelivr.net for script src in CSP +#mathtex_js = "js/mathtex-script-type.min.js" # use local js, so that we dont have to whitelist cdn.jsdelivr.net for script src in CSP +#katex_autorender_js = "js/katex-auto-render.min.js" + +### Load Katex External Resources +#katex_css = "https://cdn.jsdelivr.net/npm/katex@0.15.6/dist/katex.min.css" # Fonts - use remote fonts +#katex_css_integrity = "sha384-ZPe7yZ91iWxYumsBEOn7ieg8q/o+qh/hQpSaPow8T6BwALcXSCS6C6fSRPIAnTQs" +#katex_js = "https://cdn.jsdelivr.net/npm/katex@0.15.6/dist/katex.min.js" +#katex_js_integrity = "sha384-ljao5I1l+8KYFXG7LNEA7DyaFvuvSCmedUf6Y6JI7LJqiu8q5dEivP2nDdFH31V4" +#katex_autorender_js = "https://cdn.jsdelivr.net/npm/katex@0.15.6/dist/contrib/auto-render.min.js" +#katex_autorender_js_integrity = "sha384-+XBljXPPiv+OzfbB3cVmLHf4hdUFHlWNZN5spNQ7rmHTXpd7WvJum6fIACpNNfIR" +#mathtex_js = "https://cdn.jsdelivr.net/npm/katex@0.15.6/dist/contrib/mathtex-script-type.min.js" +#mathtex_js_integrity = "sha384-jiBVvJ8NGGj5n7kJaiWwWp9AjC+Yh8rhZY3GtAX8yU28azcLgoRo4oukO87g7zDT" + +#math = false # Recommended setting false, and enable on per page bases instead. +#math_auto_render = false # Recommended setting false, and enable on per page bases instead. diff --git a/content/2024-08-03-documentation-best-practices.md b/content/2024-08-03-documentation-best-practices.md new file mode 100644 index 0000000..33e70c3 --- /dev/null +++ b/content/2024-08-03-documentation-best-practices.md @@ -0,0 +1,137 @@ ++++ +title = "Documentation Best Practices" +date = 2024-08-02 +[taxonomies] +authors = ["Hécate"] +categories = ["Haddock"] +tags = ["Practices"] ++++ + +In the Haddock team, part of our mission is to help with writing documentation, and promoting best practices. This article will help you write the best documentation you can! + + + +We adapt documentation outside practices to our ecosystem, and leverage our own technologies to empower Haskell users with their documentation work. + +Let us see some of these techniques, and how the Haddock team can be of help. + +## Writing documentation for your software project + +### Justify yourself + +When you create software, there is a pipeline from your brain straight to your code. Your decisions — such as the libraries you’ve used, +or your program architecture — shape how your code is structured and written. + +Unfortunately, simply writing the code isn’t enough.The reasoning behind the decisions you made is as important as the decisions themselves. In the short term, solving a problem may let you move ahead immediately, but what keeps you on the correct path is understanding what +brought you to that solution. + +Indeed, your choices may not be as popular as you think they are! Of course, you decided on them because you already convinced yourself +that they’re best. But you have a user base to convince as well, and they may not see things the same way you do. + +As such, it is vitally important to document which decisions you made and to justify why you made them. If it’s not immediately obvious +why a behaviour or a pattern exists, future maintainers might be tempted to drop it — only to discover too late why it was needed. + +### The reference flow of documentation + +Not all types of documentation have the same life cycle. Different pieces of documentation are more or less stable, and this determines +which can act as a conceptual and theoretical foundation for your project. + +Examples of stable documentation include: + +* A README without code +* A vision statement +* The value proposition and the core domain + +These ought not to change much, because they describe the basic problems that your code aims to address, solve or support in the long run. +While it is normal to fiddle around with the boundaries of your project at the beginning, in general these should change infrequently. + +Some other documentation is called volatile, like: + +* Documentation generated at runtime +* Code examples +* Tests +* Configuration + +These are *expected* to change frequently, as your project changes, your API evolves, and you change configuration options. +Volatile documentation is expensive to maintain, but also very valuable, as it shows in a concrete way how the user can interact with +your project. + + +> “When you refer to something, make sure the direction of the reference is from the more volatile to the more stable elements” +> -- Cyrille Martraire, Living Documentation, 2019 + + +As such, here is a simplified model of the documentation cascade for a typical Haskell project, from the most volatile to the most stable +sources: + +``` +Haddocks of your library or a third-party library +├──> Official specs for your domain +├──> Architecture Document +└─┬> Haddocks of a core library (base, text, vector, etc) + ├──> GHC Manual + ├──> Official specs for what the core libs provide + └──> Papers (without paywalls) +``` + +Keep in mind that while the Haddocks of a project can refer to the project specs, or to an architecture document, these documents should +never refer to the project's current implementation. If you must refer to the code, point to where it's located. +The (current, volatile) code cannot be the justification for the (planned, stable) architecture. + +The GHC manual is much more stable than the haddocks of a Core library, which is why documentation should flow from +the library to the manual. + +Finally, papers serve the same purpose as architecture documents, where they describe techniques that may be implemented, +but theyshould not point to code that is subject to change – lest they point to a library that has evolved so much +that it no longer relates to the paper. + +#### Example: The Set data structure + +The [Haddocks for the `Set` datatype](https://hackage.haskell.org/package/containers-0.6.5.1/docs/Data-Set.html) +(from the `containers` library) are an example of documentation which follows this model well: + +* They point to an overview of the API ([here](https://haskell-containers.readthedocs.io/en/latest/set.html): _volatile_) +* They refer to the papers that have informed the design of its implementation: the absence of working links may be annoying, +but the references can still be followed (_stable_) + +### Understand for whom you write + +This section introduces the Diátaxis Framework for documentation: + + + +> -- Diátaxis Framework, by Daniele Procida, https://diataxis.fr + + +Diátaxis maps out the entire life cycle of one’s interaction with a system. Each of its four quadrants describes a different +situation in which a user may find themselves. + + +Diátaxis is not just about filling out all the quadrants like a checklist (although they are all good to have!). +Instead, it is about understanding how each section focusses on a particular combination of user needs and situations. +If a new user in need of actively acquiring some practice with the project, they can safely be pointed to the "Tutorials" part +of your documentation, as it is the part that focuses on "_Acquisition_" of knowledge through "_Action_". +The focus of the tutorial is to make a prospective user acquire basic competence in handling the software. It is an ice-breaker. + +However someone who is in need of a deeper – but perhaps less immediately applicable understanding of the project – +will be better served by the Explanation, which serves the need for thought (or _Cognition_) + + +In short, the message of Diátaxis is that you are not meant to write The One Documentation that covers everything — +inevitably, this produces documentation which is shallow due to its breadth. Instead, focus on the strategic aspects of your documentation, +and you will produce documentation of better quality, with a clear purpose that it can fulfill more easily. + +Through the lens of Diátaxis, the module API documentation produced by Haddock is a *Reference*. + +## Reach Out + +Should you need any help in writing or proof-reading documentation, please stop by the [Matrix chatroom](https://matrix.to/#/#haddock:matrix.org) of the Haddock team, +or ping us with the [@haddock](https://gitlab.haskell.org/groups/haddock/-/group_members?sort=last_joined) group tag on the +[Haskell Gitlab](https://gitlab.haskell.org/). We would be more than happy to lend you a hand and discuss how to best serve your users, +you included. + +## Read More + +* [Haddock manual](https://haskell-haddock.readthedocs.io/latest/) +* [The theory behind Diátaxis](https://diataxis.fr/theory/) +* [How to contribute to Haddock](https://gitlab.haskell.org/ghc/ghc/-/blob/master/utils/haddock/CONTRIBUTING.md?ref_type=heads) diff --git a/content/_index.md b/content/_index.md new file mode 100644 index 0000000..a31a970 --- /dev/null +++ b/content/_index.md @@ -0,0 +1,5 @@ ++++ +paginate_by = 3 +sort_by = "date" +template = "index.html" ++++ diff --git a/content/archive/_index.md b/content/archive/_index.md new file mode 100644 index 0000000..85198a5 --- /dev/null +++ b/content/archive/_index.md @@ -0,0 +1,6 @@ ++++ +template = "archive.html" + +[extra] +sec = "" ++++ diff --git a/content/pages/_index.md b/content/pages/_index.md new file mode 100644 index 0000000..800a244 --- /dev/null +++ b/content/pages/_index.md @@ -0,0 +1,3 @@ ++++ +render = false ++++ diff --git a/content/pages/about.md b/content/pages/about.md new file mode 100644 index 0000000..8cb8848 --- /dev/null +++ b/content/pages/about.md @@ -0,0 +1,16 @@ ++++ +title = "About this blog" +path = "about" +template = "pages.html" +draft = false ++++ + +## About this blog + +Welcome to the Haskell Project's blog! + +This is the place where the various teams that power the language and its ecosystem communicate about their progress, innovations, +and new releases. + +The Haskell.org Committee is the publisher of this website. Please contact us at `committee haskell org` if you wish to +signal content that goes against our [Guidelines For Respectful Communication](https://haskell.foundation/guidelines-for-respectful-communication/). diff --git a/content/pages/privacy.md b/content/pages/privacy.md new file mode 100644 index 0000000..1b7eec5 --- /dev/null +++ b/content/pages/privacy.md @@ -0,0 +1,15 @@ ++++ +title = "Privacy Policy" +description = "This page outlines the Privacy Policy for this site, and the date at which this policy was put into affect." +path = "privacy" +template = "pages.html" +draft = false ++++ + +## Privacy + +- This site does not set or use cookies. +- This site does not store data in the browser to be shared, sent, or sold to third-parties. +- No personal information is shared, sent, or sold to third-parties. + +**Effective Date:** _1st Jan 2022_ diff --git a/content/static/stork_toml.md b/content/static/stork_toml.md new file mode 100644 index 0000000..e427a93 --- /dev/null +++ b/content/static/stork_toml.md @@ -0,0 +1,5 @@ ++++ +path = "data_stork" +template = "stork_toml.html" +draft = true ++++ diff --git a/content/static/tinysearch_json.md b/content/static/tinysearch_json.md new file mode 100644 index 0000000..c91a639 --- /dev/null +++ b/content/static/tinysearch_json.md @@ -0,0 +1,5 @@ ++++ +path = "data_tinysearch" +template = "tinysearch_json.html" +draft = true ++++ diff --git a/package.json b/package.json new file mode 100644 index 0000000..954e327 --- /dev/null +++ b/package.json @@ -0,0 +1,20 @@ +{ + "name": "abridge-bundle", + "version": "2.0.0", + "description": "Abridge - set PWA cache files list, bundle and minify js", + "author": "Jake G <106644+Jieiku@users.noreply.github.com>", + "license": "MIT", + "homepage": "https://github.com/Jieiku/abridge", + "scripts": { + "abridge": "node -e \"if ( require('fs').existsSync('./themes/abridge/package_abridge.js')) {require('fs').copyFileSync('./themes/abridge/package_abridge.js', './package_abridge.js')}\" && node package_abridge.js", + "search:pagefind": "node -e \"if ( require('fs').existsSync('./themes/abridge/static/js/searchChange.js')) {require('fs').copyFileSync('./themes/abridge/static/js/searchChange.js', './static/js/searchChange.js')}\" && node ./static/js/searchChange.js --pagefind", + "search:elasticlunr": "node -e \"if ( require('fs').existsSync('./themes/abridge/static/js/searchChange.js')) {require('fs').copyFileSync('./themes/abridge/static/js/searchChange.js', './static/js/searchChange.js')}\" && node ./static/js/searchChange.js --elasticlunr" + }, + "dependencies": { + "fast-toml": "^0.5.4", + "jsonminify": "^0.4.2", + "pagefind": "^1.1.0", + "replace-in-file": "^8.1.0", + "uglify-js": "^3.17.4" + } +} diff --git a/package_abridge.js b/package_abridge.js new file mode 100644 index 0000000..70cb40c --- /dev/null +++ b/package_abridge.js @@ -0,0 +1,419 @@ +const fs = require('fs'); +const path = require("path"); +const TOML = require('fast-toml'); +const UglifyJS = require('uglify-js'); +const jsonminify = require("jsonminify"); +const util = require("util"); +const { exec } = require("child_process"); +const { exit } = require('process'); +const execPromise = util.promisify(exec); + +if (!(fs.existsSync('config.toml'))) { + throw new Error('ERROR: cannot find config.toml!'); +} +const tomlString = String(fs.readFileSync('config.toml')); +const data = TOML.parse(tomlString); +const js_prestyle = data.extra.js_prestyle; +const js_switcher = data.extra.js_switcher; +const js_email_encode = data.extra.js_email_encode; +const js_copycode = data.extra.js_copycode; +const search_library = data.extra.search_library; +const index_format = data.search.index_format; +const uglyurls = data.extra.uglyurls; +const js_bundle = data.extra.js_bundle; +const offline = data.extra.offline; +const online_url = data.extra.online_url; +const online_indexformat = data.extra.online_indexformat; +const pwa = data.extra.pwa; +const pwa_VER = data.extra.pwa_VER; +const pwa_NORM_TTL = data.extra.pwa_NORM_TTL; +const pwa_LONG_TTL = data.extra.pwa_LONG_TTL; +const pwa_TTL_NORM = data.extra.pwa_TTL_NORM; +const pwa_TTL_LONG = data.extra.pwa_TTL_LONG; +const pwa_TTL_EXEMPT = data.extra.pwa_TTL_EXEMPT; +const pwa_cache_all = data.extra.pwa_cache_all; +const pwa_BASE_CACHE_FILES = data.extra.pwa_BASE_CACHE_FILES; + +// This is used to pass arguments to zola via npm, for example: +// npm run abridge -- "--base-url https://abridge.pages.dev" +const args = process.argv[2] ? ' '+process.argv[2] : ''; + +async function execWrapper(cmd) { + const { stdout, stderr } = await execPromise(cmd); + if (stdout) { + console.log(stdout); + } + if (stderr) { + console.log('ERROR: '+stderr); + } +} + +async function abridge() { + await sync(); + const { replaceInFileSync } = await import('replace-in-file'); + if (offline === false) { + if (typeof online_url !== 'undefined' && typeof online_indexformat !== 'undefined') { + replaceInFileSync({files: 'config.toml', from: /base_url.*=.*/g, to: "base_url = \""+online_url+"\""}); + replaceInFileSync({files: 'config.toml', from: /index_format.*=.*/g, to: "index_format = \""+online_indexformat+"\""}); + } + } else if (offline === true) { + if (typeof online_url !== 'undefined' && typeof online_indexformat !== 'undefined') { + replaceInFileSync({files: 'config.toml', from: /base_url.*=.*/g, to: "base_url = \""+__dirname+"\/public\""}); + replaceInFileSync({files: 'config.toml', from: /index_format.*=.*/g, to: "index_format = \"elasticlunr_javascript\""}); + } else { + throw new Error('ERROR: offline = true requires that online_url and online_indexformat are set in config.toml, so that the base_url and index_format can be restored if offline is later set to false.'); + } + } + + console.log('Zola Build to generate files for minification:'); + await execWrapper('zola build'+args); + + //check that static/js exists, do this after zola build, it will handle creating static if missing. + var jsdir = 'static/js'; + try { + fs.mkdirSync(jsdir); + } catch(e) { + if (e.code != 'EEXIST') throw e; + } + + // check if abridge is used directly or as a theme. + bpath = ''; + if (fs.existsSync('./themes')) { + bpath = 'themes/abridge/'; + } + + base_url = data.base_url; + if (base_url.slice(-1) == "/") { + base_url = base_url.slice(0, -1); + } + + if (search_library === 'elasticlunr') { + if (fs.existsSync('content/static/stork_toml.md')) { + replaceInFileSync({files: 'content/static/stork_toml.md', from: /draft.*=.*/g, to: "draft = true"}); + } + if (fs.existsSync('content/static/tinysearch_json.md')) { + replaceInFileSync({files: 'content/static/tinysearch_json.md', from: /draft.*=.*/g, to: "draft = true"}); + } + } else if (search_library === 'tinysearch') { + if (!fs.existsSync('content/static/tinysearch_json.md')) {// 'content/static/tinysearch_json.md' file is missing, copy from abridge theme. + fs.copyFileSync(bpath+'content/static/tinysearch_json.md', 'content/static/tinysearch_json.md',fs.constants.COPYFILE_EXCL); + } + if (fs.existsSync('content/static/stork_toml.md')) { + replaceInFileSync({files: 'content/static/stork_toml.md', from: /draft.*=.*/g, to: "draft = true"}); + } + if (fs.existsSync('content/static/tinysearch_json.md')) { + replaceInFileSync({files: 'content/static/tinysearch_json.md', from: /draft.*=.*/g, to: "draft = false"}); + } + // zola build && mkdir -p tmp && tinysearch --optimize --path tmp public/data_tinysearch/index.html && rsync -avz tmp/*.wasm static/ && rm -rf tmp + } else if (search_library === 'stork') { + + if (!fs.existsSync('content/static/stork_toml.md')) {// 'content/static/stork_toml.md' file is missing, copy from abridge theme. + fs.copyFileSync(bpath+'content/static/stork_toml.md', 'content/static/stork_toml.md',fs.constants.COPYFILE_EXCL); + } + if (fs.existsSync('content/static/stork_toml.md')) { + replaceInFileSync({files: 'content/static/stork_toml.md', from: /draft.*=.*/g, to: "draft = false"}); + } + if (fs.existsSync('content/static/tinysearch_json.md')) { + replaceInFileSync({files: 'content/static/tinysearch_json.md', from: /draft.*=.*/g, to: "draft = true"}); + } + // zola build && stork build --input public/data_stork/index.html --output static/stork.st + } else if (search_library === 'pagefind') { + if (fs.existsSync('content/static/stork_toml.md')) { + replaceInFileSync({files: 'content/static/stork_toml.md', from: /draft.*=.*/g, to: "draft = true"}); + } + if (fs.existsSync('content/static/tinysearch_json.md')) { + replaceInFileSync({files: 'content/static/tinysearch_json.md', from: /draft.*=.*/g, to: "draft = true"}); + } + + // Run the pagefind script to generate the index files. + // Has to happen at start otherwise, it happens too late asyncronously. + const createIndex = require('./static/js/pagefind.index.js'); // run the pagefind index.js script + await createIndex(); // makes program wait for pagefind build execution + } + + if (pwa) {// Update pwa settings, file list, and hashes. + if (typeof pwa_VER !== 'undefined' && typeof pwa_NORM_TTL !== 'undefined' && typeof pwa_LONG_TTL !== 'undefined' && typeof pwa_TTL_NORM !== 'undefined' && typeof pwa_TTL_LONG !== 'undefined' && typeof pwa_TTL_EXEMPT !== 'undefined') { + // update from abridge theme. + fs.copyFileSync(bpath+'static/sw.js', 'static/sw.js'); + fs.copyFileSync(bpath+'static/js/sw_load.js', 'static/js/sw_load.js'); + // Update settings in PWA javascript file, using options parsed from config.toml. sw.min.js?v=3.10.0", "++" + if (fs.existsSync('static/js/sw_load.js')) { + sw_load_min = '.js?v='; + if (js_bundle) { + sw_load_min = '.min.js?v='; + } + replaceInFileSync({files: 'static/js/sw_load.js', from: /sw.*v=.*/g, to: "sw"+sw_load_min+pwa_VER+"\","}); + } + if (fs.existsSync('static/sw.js')) { + replaceInFileSync({files: 'static/sw.js', from: /NORM_TTL.*=.*/g, to: "NORM_TTL = "+pwa_NORM_TTL+";"}); + replaceInFileSync({files: 'static/sw.js', from: /LONG_TTL.*=.*/g, to: "LONG_TTL = "+pwa_LONG_TTL+";"}); + replaceInFileSync({files: 'static/sw.js', from: /TTL_NORM.*=.*/g, to: "TTL_NORM = ["+pwa_TTL_NORM+"];"}); + replaceInFileSync({files: 'static/sw.js', from: /TTL_LONG.*=.*/g, to: "TTL_LONG = ["+pwa_TTL_LONG+"];"}); + replaceInFileSync({files: 'static/sw.js', from: /TTL_EXEMPT.*=.*/g, to: "TTL_EXEMPT = ["+pwa_TTL_EXEMPT+"];"}); + } + + if (pwa_cache_all === true) { + console.log('info: pwa_cache_all = true in config.toml, so caching the entire site.\n'); + // Generate array from the list of files, for the entire site. + + var dir = 'public'; + try { + fs.mkdirSync(dir); + } catch(e) { + if (e.code != 'EEXIST') throw e; + } + const path = './public/'; + cache = 'this.BASE_CACHE_FILES = ['; + files = fs.readdirSync(path, { recursive: true, withFileTypes: false }) + .forEach( + (file) => { + // check if is directory, if not then add the path/file + if (!fs.lstatSync(path+file).isDirectory()) { + // format output + item = "/"+file.replace(/index\.html$/i,'');// strip index.html from path + item = item.replace(/\\/g,'/');// replace backslash with forward slash for Windows + item = item.replace(/^\/sw(\.min)?\.js/i,'');// dont cache service worker + + // if formatted output is not empty line then append it to cache var + if (item != '') {// skip empty lines + cache = cache+"'"+item+"',"; + } + } + } + ); + cache = cache.slice(0, -1)+'];'// remove the last comma and close the array + } else if (pwa_BASE_CACHE_FILES) { + cache = 'this.BASE_CACHE_FILES = ['+pwa_BASE_CACHE_FILES+'];'; + } + + // update the BASE_CACHE_FILES variable in the sw.js service worker file + results = replaceInFileSync({ + files: 'static/sw.js', + from: /this\.BASE_CACHE_FILES =.*/g, + to: cache, + countMatches: true, + }); + } else { + throw new Error('ERROR: pwa requires that pwa_VER, pwa_NORM_TTL, pwa_LONG_TTL, pwa_TTL_NORM, pwa_TTL_LONG, pwa_TTL_EXEMPT are set in config.toml.'); + } + } + + if (bpath === '') {// abridge used directly + // These are truely static js files, so they should only need to be updated by the abridge maintainer or contributors. + minify(['static/js/theme.js']); + minify(['static/js/theme_light.js']); + // Something went wrong with minifying katexbundle, so commenting this out for now + // minify(['static/js/katex.min.js','static/js/mathtex-script-type.min.js','static/js/katex-auto-render.min.js','static/js/katexoptions.js'],'static/js/katexbundle.min.js'); + minify(['static/js/elasticlunr.min.js','static/js/search.js'],'static/js/search_elasticlunr.min.js'); + minify(['static/js/stork.js','static/js/stork_config.js'],'static/js/search_stork.min.js'); + minify(['static/js/tinysearch.js'],'static/js/search_tinysearch.min.js'); + minify(['static/js/prestyle.js','static/js/theme_button.js','static/js/email.js','static/js/codecopy.js','static/js/sw_load.js'],'static/js/abridge_nosearch.min.js'); + minify(['static/js/prestyle.js','static/js/theme_button.js','static/js/email.js','static/js/codecopy.js'],'static/js/abridge_nosearch_nopwa.min.js'); + minify(['static/js/sw_load.js']); + minify(['static/sw.js']); + } else if (pwa) { + minify(['static/js/sw_load.js']); + minify(['static/sw.js']); + } + + // if manifest.json is present, then minify it. + if (fs.existsSync('static/manifest.json')) { + let out; + try { + out = JSON.minify(fs.readFileSync('static/manifest.json', {encoding:"utf-8"})); + } catch(err) { + console.log(err); + } + fs.writeFileSync('static/manifest.min.json', out); + } + + abridge_bundle = bundle(bpath,js_prestyle,js_switcher,js_email_encode,js_copycode,search_library,index_format,uglyurls,false); + minify(abridge_bundle,'static/js/abridge_nopwa.min.js'); + + abridge_bundle = bundle(bpath,js_prestyle,js_switcher,js_email_encode,js_copycode,search_library,index_format,uglyurls,pwa); + minify(abridge_bundle,'static/js/abridge.min.js'); + + console.log('Zola Build to generate new integrity hashes for the previously minified files:'); + await execWrapper('zola build'+args); +} + +function bundle(bpath,js_prestyle,js_switcher,js_email_encode,js_copycode,search_library,index_format,uglyurls,pwa) { + minify_files = []; + + if (js_prestyle) { + minify_files.push(bpath+'static/js/prestyle.js'); + } + if (js_switcher) { + minify_files.push(bpath+'static/js/theme_button.js'); + } + if (js_email_encode) { + minify_files.push(bpath+'static/js/email.js'); + } + if (js_copycode) { + minify_files.push(bpath+'static/js/codecopy.js'); + } + if (search_library) { + if ((search_library === 'elasticlunr' && offline === true) || (search_library === 'elasticlunr' && index_format === 'elasticlunr_javascript' && uglyurls === true)) { + minify_files.push('public/search_index.en.js'); + minify_files.push(bpath+'static/js/elasticlunr.min.js'); + minify_files.push(bpath+'static/js/searchjavaugly.js'); + } else if (search_library === 'elasticlunr' && index_format === 'elasticlunr_javascript') { + minify_files.push('public/search_index.en.js'); + minify_files.push(bpath+'static/js/elasticlunr.min.js'); + minify_files.push(bpath+'static/js/searchjava.js'); + } else if (search_library === 'elasticlunr') {//abridge default + minify_files.push(bpath+'static/js/elasticlunr.min.js'); + minify_files.push(bpath+'static/js/search.js'); + } else if (search_library === 'stork') { + minify_files.push(bpath+'static/js/stork.js'); + minify_files.push(bpath+'static/js/stork_config.js'); + } else if (search_library === 'tinysearch') { + minify_files.push(bpath+'static/js/tinysearch.js'); + } else if (search_library === 'pagefind') { + minify_files.push(bpath+'static/js/pagefind.js'); + minify_files.push(bpath+'static/js/pagefind.search.js'); + } + } + if (pwa) { + minify_files.push('static/js/sw_load.js'); + } + return minify_files; +} + +function minify(fileA,outfile) { + const options = { + mangle: true, + compress: { + //expression: true,//Parse a single expression, rather than a program (for parsing JSON). + //global_defs: false,// a way to pass parameters + //module: true,//Process input as ES module (implies --toplevel) + //toplevel: true,//Compress and/or mangle variables in top level scope. + hoist_funs: true,//hoist function declarations + unsafe: true, + unsafe_comps: true, + unsafe_Function: true, + unsafe_math: true, + unsafe_proto: true, + unsafe_regexp: true, + unsafe_undefined: true, + drop_console: true + } + } + if (!outfile) {// outfile parameter omitted, infer based on input + outfile = fileA[0].slice(0,-2)+'min.js'; + } + var filesContents = fileA.map(function (file) {// array input to support multiple files + return fs.readFileSync(file, 'utf8'); + }); + + result = UglifyJS.minify(filesContents, options); + fs.writeFileSync(outfile, result.code); + +} + +abridge(); + +async function sync() { + // Check if the submodule is present, if not skip entire function + if (!fs.existsSync(path.join(__dirname, "themes/abridge"))) { + return; + } + + // Checks for changes from local version in static, package.json and config.toml + // and if there are changes it sync from the submodule + + // Check for changes in static + const staticFolder = path.join(__dirname, "static/js"); + const submoduleFolder = path.join(__dirname, "themes/abridge/static/js"); + + const files = fs.readdirSync(staticFolder); + + files.forEach((file) => { + if (file.endsWith(".js") && !file.endsWith(".min.js")) { + try { + const localFile = path.join(staticFolder, file); + const submoduleFile = path.join(submoduleFolder, file); + const localFileContent = fs.readFileSync(localFile, "utf-8"); + const submoduleFileContent = fs.readFileSync(submoduleFile, "utf-8"); + + if (localFileContent !== submoduleFileContent) { + console.log(`Updating ${file} from submodule`); + fs.copyFileSync(submoduleFile, localFile); + } + } catch (error) { + console.log(`Skipping ${file} due to error: ${error}`); + } + } + }); + + // Check for changes in package.json + const packageJson = path.join(__dirname, "package.json"); + const submodulePackageJson = path.join( + __dirname, + "themes/abridge/package.json" + ); + + const packageJsonContent = fs.readFileSync(packageJson, "utf-8"); + const submodulePackageJsonContent = fs.readFileSync( + submodulePackageJson, + "utf-8" + ); + + // Check for changes in dependencies - prompting an npm update + let checkPackageVersion = function (content) { + let matches = content.match(/"dependencies": \{([^}]+)\}/)[1]; // Look in the dependencies section + return [...matches.matchAll(/"(\w+-\w+|\w+)": "[^0-9]*([0-9])/g)].map(match => ({ // Extract all packages and their major version number (aka for breaking changes which need an update) + name: match[1], + majorVersion: match[2] + })).sort((a, b) => a.name.localeCompare(b.name)); + }; + + const packageVersionLocal = checkPackageVersion(packageJsonContent); + const packageVersionSubmodule = checkPackageVersion(submodulePackageJsonContent); + if (JSON.stringify(packageVersionLocal) !== JSON.stringify(packageVersionSubmodule)) { + console.log( + "\x1b[31m%s\x1b[0m", + "warning:", + "The packages are out of date, please run `npm install` to update them." + ); + exit(1); + } + console.log(packageVersionLocal, packageVersionSubmodule); + + if (packageJsonContent !== submodulePackageJsonContent) { + console.log("Updating package.json from submodule"); + fs.copyFileSync(submodulePackageJson, packageJson); + } + const configToml = path.join(__dirname, "config.toml"); + const submoduleConfigToml = path.join( + __dirname, + "themes/abridge/config.toml" + ); + + let adjustTomlContent = function (content) { + content = content.replace(/^\s+|\s+$|\s+(?=\s)/g, ""); // Remove all leading and trailing whitespaces and multiple whitespaces + content = content.replace(/(^#)(?=\s*\w+\s*=\s*)|[[:blank:]]*#.*$/gm, ""); // A regex to selectively remove all comments, and to uncomment all commented config lines + content = content.replace(/(\[([^]]*)\])|(\{([^}]*)\})/gs, ""); // A regex to remove all tables and arrays + content = content.replace( + /(^#.*$|(["']).*?\2|(?<=\s)#.*$|\btrue\b|\bfalse\b)/gm, + "" + ); // A regex to remove all user added content, (so you can tell if the .toml format has changed) + return content.trim(); // Finally remove any leading or trailing white spaces + }; + + const configTomlContent = adjustTomlContent( + fs.readFileSync(configToml, "utf-8") + ); + const submoduleConfigTomlContent = adjustTomlContent( + fs.readFileSync(submoduleConfigToml, "utf-8") + ); + + if (configTomlContent !== submoduleConfigTomlContent) { + // This should say info: then the message in blue (which works in every terminal) + console.log( + "\x1b[34m%s\x1b[0m", + "info:", + "The config.toml file format may have changed, please update it manually." + ); + } +} diff --git a/sass/_extra.scss b/sass/_extra.scss new file mode 100644 index 0000000..3f86323 --- /dev/null +++ b/sass/_extra.scss @@ -0,0 +1,26 @@ +/****************************************************************************** + * Extra - Put your extra SASS/CSS here, it will get bundled with abridge.css + *****************************************************************************/ + +body { + font-size: 16px; +} + +a { + color: #9E358F; + text-decoration: underline ; +} + +a:hover, a:focus { + color: #65225b; + text-decoration: underline; +} + +a:focus { + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} + +h1 { + font-family: "Raleway",Helvetica,Arial,sans-serif; +} diff --git a/sass/abridge.scss b/sass/abridge.scss new file mode 100644 index 0000000..f9844a0 --- /dev/null +++ b/sass/abridge.scss @@ -0,0 +1,181 @@ +@use '../themes/abridge/sass/abridge' with ( + /// LINES HERE END WITH COMMA AFTER THE VALUE! + /// The things your less likely to need to override have been commented out. + + /// Enable a centered viewport for
,
,