diff --git a/config/authors.mjs b/config/authors.mjs index d2188510459..b8195d5a706 100644 --- a/config/authors.mjs +++ b/config/authors.mjs @@ -5,6 +5,8 @@ */ export const authors = { joostdecock: { id: 0, name: 'Joost De Cock' }, + 'Prof. dr. Sorcha Ní Dhubhghaill': { id: 0, name: 'Prof. dr. Sorcha Ní Dhubhghaill' }, + mocked: { id: 0, name: 'Unknown (mocked in dev)' }, benjamesben: { id: 0, name: 'Benjamin' }, nikhil: { id: 0, name: 'nikhil' }, jackseye: { id: 0, name: 'jackseye' }, @@ -30,8 +32,7 @@ export const authors = { Natalia: { id: 0, name: 'Natalia Sayang' }, chri5b: { id: 0, name: 'chri5b' }, tangerineshark: { id: 0, name: 'tangerineshark' }, - 'MA-TATAS': { id: 0, name: 'MA-TATAS' }, - 'Ivo Bek': { id: 0, name: 'Ivo Bek' }, + 'bekivo@gmail.com': { id: 0, name: 'Ivo Bek' }, } /* @@ -47,4 +48,5 @@ export const gitToAuthor = { 'bobgeorgethe3rd@googlemail.com': 'bobgeorgethe3rd', '70777269+tangerineshark@users.noreply.github.com': 'tangerineshark', 'thijs.assies@gmail.com': 'MA-TATAS', + 'Natalia Sayang': 'Natalia', } diff --git a/config/dependencies.yaml b/config/dependencies.yaml index 9d38b5da2d9..b390cc6643c 100644 --- a/config/dependencies.yaml +++ b/config/dependencies.yaml @@ -104,7 +104,7 @@ new-design: 'chalk': '5.0.1' 'execa': '7.1.1' 'mustache': '4.2.0' - 'ora': '6.1.0' + 'ora': '6.3.1' 'prompts': '2.4.2' 'recursive-readdir': '2.2.3' noble: diff --git a/config/scripts.yaml b/config/scripts.yaml index 1210e5a9e6f..eb44ffa003f 100644 --- a/config/scripts.yaml +++ b/config/scripts.yaml @@ -24,8 +24,6 @@ core: prettier: "npx prettier --write 'src/*.mjs' 'tests/*.mjs'" lint: "npx eslint 'src/*.mjs' 'tests/*.mjs'" jsdoc: 'jsdoc -c jsdoc.json -r src' -i18n: - prebuild: 'node scripts/prebuilder.mjs' models: test: 'npx mocha tests/*.test.mjs' new-design: @@ -67,13 +65,14 @@ dev: build: &nextBuild 'next build' cibuild: 'yarn build && node scripts/algolia.mjs' clean: &nextClean 'rimraf prebuild/* && rimraf public/locales/*/* && rimraf public/feeds/* && rimraf ../shared/prebuild/data/*' - predev: 'FAST=1 SITE=dev node --experimental-json-modules ../shared/prebuild/index.mjs' dev: &nextDev 'next dev -p 8000' develop: *nextDev i18n: "SITE=dev node ../shared/prebuild/i18n-only.mjs" lint: &nextLint 'next lint' buildsitedeps: &buildsitedeps 'cd ../../ && yarn buildall && cd -' - prebuild: 'yarn buildsitedeps && SITE=dev node --experimental-json-modules ../shared/prebuild/index.mjs' + prebuild: 'yarn buildsitedeps && node --experimental-json-modules ./prebuild.mjs' + prebuildonly: 'node --experimental-json-modules ./prebuild.mjs' + predev: 'node --experimental-json-modules ./prebuild.mjs' serve: "pm2 start npm --name 'dev' -- run start" start: &nextStart 'yarn prebuild && yarn dev' @@ -91,20 +90,22 @@ lab: e2e: &e2e 'yarn playwright test' lint: *nextLint buildsitedeps: *buildsitedeps - prebuild: 'yarn buildsitedeps && SITE=lab node --experimental-json-modules ../shared/prebuild/index.mjs' + prebuild: 'yarn buildsitedeps && node --experimental-json-modules ./prebuild.mjs' + prebuildonly: 'node --experimental-json-modules ./prebuild.mjs' start: *nextStart org: build: *nextBuild cibuild: 'yarn build' clean: *nextClean - predev: 'FAST=1 SITE=org node --experimental-json-modules ../shared/prebuild/index.mjs' dev: *nextDev develop: *nextDev i18n: 'SITE=org node ../shared/prebuild/i18n-only.mjs' lint: *nextLint buildsitedeps: *buildsitedeps - prebuild: 'yarn buildsitedeps && SITE=org node --experimental-json-modules ../shared/prebuild/index.mjs' + prebuild: 'yarn buildsitedeps && node --experimental-json-modules ./prebuild.mjs' + prebuildonly: 'node --experimental-json-modules ./prebuild.mjs' + predev: 'node --experimental-json-modules ./prebuild.mjs' start: *nextStart sanity: diff --git a/markdown/dev/tutorials/getting-started-codespaces/en.md b/markdown/dev/tutorials/getting-started-codespaces/en.md index 55fa8f33d48..5a6a8d9cb26 100644 --- a/markdown/dev/tutorials/getting-started-codespaces/en.md +++ b/markdown/dev/tutorials/getting-started-codespaces/en.md @@ -108,7 +108,8 @@ After the lab, dev, or org website starts: custom URL. - You can also access the custom URL via the "Ports" panel. - + + An example of a custom URL: `https://username-ominous-space-waffle-rwpgzw5q15vqc52q9-8000.preview.app.github.dev/` diff --git a/packages/new-design/package.json b/packages/new-design/package.json index 8c56ff7ab24..8655eaa731a 100644 --- a/packages/new-design/package.json +++ b/packages/new-design/package.json @@ -39,7 +39,7 @@ "chalk": "5.0.1", "execa": "7.1.1", "mustache": "4.2.0", - "ora": "6.1.0", + "ora": "6.3.1", "prompts": "2.4.2", "recursive-readdir": "2.2.3" }, diff --git a/sites/dev/components/navigation/modal-menu.mjs b/sites/dev/components/navigation/modal-menu.mjs index 9d4dc4dea65..f83ca281cc2 100644 --- a/sites/dev/components/navigation/modal-menu.mjs +++ b/sites/dev/components/navigation/modal-menu.mjs @@ -1,5 +1,7 @@ +// Dependencies +import { NavigationContext } from 'shared/context/navigation-context.mjs' // Hooks -import { useNavigation } from 'site/hooks/use-navigation.mjs' +import { useContext } from 'react' // Components import { SectionsMenu, ns as sectionsNs } from 'site/components/navigation/sections-menu.mjs' import { ModalWrapper } from 'shared/components/wrappers/modal.mjs' @@ -11,7 +13,8 @@ import { NavLinks, Breadcrumbs } from 'shared/components/navigation/sitenav.mjs' export const ns = nsMerge(sectionsNs) export const ModalMenu = ({ slug }) => { - const { siteNav } = useNavigation() + // Grab siteNav from the navigation context + const { siteNav } = useContext(NavigationContext) return ( diff --git a/sites/dev/components/navigation/sections-menu.mjs b/sites/dev/components/navigation/sections-menu.mjs index 79badb22fe7..5d909d94d16 100644 --- a/sites/dev/components/navigation/sections-menu.mjs +++ b/sites/dev/components/navigation/sections-menu.mjs @@ -1,9 +1,10 @@ +import { useContext } from 'react' +import { NavigationContext } from 'shared/context/navigation-context.mjs' import Link from 'next/link' import { icons, ns as sectionsNs } from 'shared/components/navigation/primary.mjs' import { useTranslation } from 'next-i18next' import orderBy from 'lodash.orderby' import { colors } from 'shared/components/header.mjs' -import { useNavigation } from 'site/hooks/use-navigation.mjs' export const ns = sectionsNs @@ -11,7 +12,7 @@ const onlySections = (tree) => orderBy(tree, ['t'], ['asc']).filter((entry) => e export const SectionsMenu = ({ bOnly = false }) => { const { t } = useTranslation(ns) - const { siteNav } = useNavigation() + const { siteNav } = useContext(NavigationContext) const output = [] let i = 1 diff --git a/sites/dev/hooks/use-navigation.mjs b/sites/dev/hooks/use-navigation.mjs deleted file mode 100644 index e4f7f2ace42..00000000000 --- a/sites/dev/hooks/use-navigation.mjs +++ /dev/null @@ -1,108 +0,0 @@ -import { prebuildNavigation as pbn } from 'site/prebuild/navigation.mjs' -import { orderedSlugLut } from 'shared/hooks/use-navigation-helpers.mjs' - -/* - * prebuildNavvigation[locale] holds the navigation structure based on MDX content. - * The entire website only has a few pages that are now MDX-based: - * - 404 => no navigation shown - * - home page => no navvigation shown - * - /contact => Added below - * - * Remember Mc_Shifton: - * Note: Set 'm' to truthy to show this as a main section in the side-navigation (optional) - * Note: Set 'c' to set the control level to hide things from users (optional) - * Note: Set 's' to the slug (optional insofar as it's not a real page (a spacer for the header)) - * Note: Set '_' to never show the page in the site navigation (like the tags pages) - * Note: Set 'h' to indicate this is a top-level page that should be hidden from the side-nav (like search) - * Note: Set 'i' when something should be included as top-level in the collapse side-navigation (optional) - * Note: Set 'f' to add the page to the footer - * Note: Set 't' to the title - * Note: Set 'o' to set the order (optional) - * Note: Set 'n' to mark this as a noisy entry that should always be closed unless active (like blog) - */ - -export const ns = ['account', 'sections', 'design', 'tags'] - -const sitePages = () => { - const pages = { - // Top-level pages that are the sections menu - api: { - m: 1, - s: 'api', - t: 'API Documentation', - o: 10, - }, - design: { - m: 1, - s: 'design', - t: 'Design Sewing Patterns', - o: 10, - }, - contribute: { - m: 1, - s: 'contribute', - t: 'Contribute to FreeSewing', - o: 20, - }, - i18n: { - m: 1, - s: 'i18n', - t: 'Help Translate FreeSewing', - o: 40, - }, - infra: { - m: 1, - s: 'infra', - t: 'FreeSewing Infrastructure', - o: 50, - }, - about: { - m: 1, - s: 'about', - f: 1, - t: 'About FreeSewing', - o: 60, - }, - support: { - m: 1, - s: 'support', - f: 1, - t: 'Support FreeSewing', - o: 70, - }, - search: { - s: 'search', - h: 1, - f: 1, - t: 'Search', - o: 270, - }, - sitemap: { - s: 'sitemap', - h: 1, - f: 1, - t: 'Sitemap', - o: 270, - }, - } - return pages -} - -export const useNavigation = () => { - // Dev is EN only - const siteNav = { ...pbn.en, ...sitePages() } - - // Make top-level documentation entries appear in i-list - for (const page of ['tutorials', 'guides', 'howtos', 'reference', 'training']) { - siteNav[page].o = 1000 - siteNav[page].i = 1 - } - - // Hide contact from the sitenav - siteNav.contact.h = 1 - - return { - siteNav, // Site navigation - slugLut: orderedSlugLut(siteNav), // Slug lookup table - } -} diff --git a/sites/dev/package.json b/sites/dev/package.json index 114c91bac26..c6f70de3542 100644 --- a/sites/dev/package.json +++ b/sites/dev/package.json @@ -17,13 +17,14 @@ "build": "next build", "cibuild": "yarn build && node scripts/algolia.mjs", "clean": "rimraf prebuild/* && rimraf public/locales/*/* && rimraf public/feeds/* && rimraf ../shared/prebuild/data/*", - "predev": "FAST=1 SITE=dev node --experimental-json-modules ../shared/prebuild/index.mjs", "dev": "next dev -p 8000", "develop": "next dev -p 8000", "i18n": "SITE=dev node ../shared/prebuild/i18n-only.mjs", "lint": "next lint", "buildsitedeps": "cd ../../ && yarn buildall && cd -", - "prebuild": "yarn buildsitedeps && SITE=dev node --experimental-json-modules ../shared/prebuild/index.mjs", + "prebuild": "yarn buildsitedeps && node --experimental-json-modules ./prebuild.mjs", + "prebuildonly": "node --experimental-json-modules ./prebuild.mjs", + "predev": "node --experimental-json-modules ./prebuild.mjs", "serve": "pm2 start npm --name 'dev' -- run start", "start": "yarn prebuild && yarn dev" }, diff --git a/sites/dev/pages/404.mjs b/sites/dev/pages/404.mjs index a331e200e76..250e45b92b4 100644 --- a/sites/dev/pages/404.mjs +++ b/sites/dev/pages/404.mjs @@ -1,64 +1,71 @@ -// Hooks -import { useNavigation } from 'site/hooks/use-navigation.mjs' +// Dependencies +import { serverSideTranslations } from 'next-i18next/serverSideTranslations' // Components import Head from 'next/head' -import { PageWrapper } from 'shared/components/wrappers/page.mjs' +import { PageWrapper, ns as pageNs } from 'shared/components/wrappers/page.mjs' import { Robot } from 'shared/components/robot/index.mjs' import { Popout } from 'shared/components/popout.mjs' import { PageLink } from 'shared/components/page-link.mjs' import { BaseLayout, BaseLayoutLeft, BaseLayoutWide } from 'shared/components/base-layout.mjs' import { NavLinks, MainSections } from 'shared/components/navigation/sitenav.mjs' -const Page404 = () => { - const title = '404: Page not found' - const { siteNav } = useNavigation({ ignoreControl: true }) - const slug = '404' +const namespaces = [...pageNs] - return ( - - - - - - - - - - - - - - - - - - - -
-

404: Page not found

-
- -
-

We could not find what you are looking for

-
- -
Did you arrive here from a link?
-

In that case, that link is broken.

-

- If it was one of our links, please {' '} - so we can fix it. -

-
-
+const Page404 = () => ( + + + + + + + + + + + + + + + + + + + +
+

404: Page not found

+
+
- - - - ) -} +

We could not find what you are looking for

+
+ +
Did you arrive here from a link?
+

In that case, that link is broken.

+

+ If it was one of our links, please so + we can fix it. +

+
+
+
+
+
+
+) export default Page404 + +export async function getStaticProps() { + return { + props: { + ...(await serverSideTranslations('en', namespaces)), + page: { + path: ['404'], + }, + }, + } +} diff --git a/sites/dev/pages/[...slug].mjs b/sites/dev/pages/[...slug].mjs index 7e8a8daeb04..dbe61634675 100644 --- a/sites/dev/pages/[...slug].mjs +++ b/sites/dev/pages/[...slug].mjs @@ -1,10 +1,9 @@ // Used in static paths -import { mdxPaths } from 'site/prebuild/mdx-paths.en.mjs' +import { pages } from 'site/prebuild/docs.en.mjs' // Dependencies import { serverSideTranslations } from 'next-i18next/serverSideTranslations' // Hooks import { useState, useEffect } from 'react' -import { useNavigation } from 'site/hooks/use-navigation.mjs' // Components import Head from 'next/head' import { PageWrapper, ns } from 'shared/components/wrappers/page.mjs' @@ -31,11 +30,6 @@ import { const DocsPage = ({ page, slug }) => { const [frontmatter, setFrontmatter] = useState({ title: 'FreeSewing.dev' }) const [MDX, setMDX] = useState() - /* - * Get the siteNav object from the useNavigation hook - * FIXME: ignorecontrol is not yet implmented here - */ - const { siteNav } = useNavigation({ ignoreControl: true }) /* Load MDX dynamically */ useEffect(() => { @@ -71,12 +65,12 @@ const DocsPage = ({ page, slug }) => { - - + +
- +

{frontmatter.title}

@@ -126,7 +120,7 @@ export async function getStaticProps({ params }) { */ export async function getStaticPaths() { return { - paths: mdxPaths.map((slug) => '/' + slug), + paths: Object.keys(pages).map((slug) => '/' + slug), fallback: false, } } diff --git a/sites/dev/pages/about.mjs b/sites/dev/pages/about.mjs index ad569c4afbf..6574a4edd1b 100644 --- a/sites/dev/pages/about.mjs +++ b/sites/dev/pages/about.mjs @@ -1,8 +1,6 @@ // Dependencies import { serverSideTranslations } from 'next-i18next/serverSideTranslations' import { freeSewingConfig } from 'shared/config/freesewing.config.mjs' -// Hooks -import { useNavigation } from 'site/hooks/use-navigation.mjs' // Components import Head from 'next/head' import { PageWrapper } from 'shared/components/wrappers/page.mjs' @@ -17,13 +15,8 @@ import { BaseLayoutRight, } from 'shared/components/base-layout.mjs' -const ContactPage = ({ page, slug }) => { - /* - * Get the siteNav object from the useNavigation hook - * FIXME: ignorecontrol is not yet implmented here - */ - const { siteNav } = useNavigation({ ignoreControl: true }) - const title = siteNav.about.t +const ContactPage = ({ page }) => { + const title = 'About FreeSewing' return ( @@ -45,12 +38,12 @@ const ContactPage = ({ page, slug }) => { - - + +
- +

{title}

@@ -187,7 +180,6 @@ export async function getStaticProps() { return { props: { ...(await serverSideTranslations('en')), - slug: 'about', page: { path: ['about'], }, diff --git a/sites/dev/pages/search.mjs b/sites/dev/pages/search.mjs index 693ffcda751..4448a4baaff 100644 --- a/sites/dev/pages/search.mjs +++ b/sites/dev/pages/search.mjs @@ -1,9 +1,7 @@ // Dependencies import { serverSideTranslations } from 'next-i18next/serverSideTranslations' -// Hooks -import { useNavigation } from 'site/hooks/use-navigation.mjs' // Components -import { PageWrapper } from 'shared/components/wrappers/page.mjs' +import { PageWrapper, ns as pageNs } from 'shared/components/wrappers/page.mjs' import { Search } from 'site/components/search.mjs' import { Popout } from 'shared/components/popout.mjs' import { PageLink } from 'shared/components/page-link.mjs' @@ -15,13 +13,10 @@ import { BaseLayoutRight, } from 'shared/components/base-layout.mjs' -const SearchPage = ({ page, slug }) => { - /* - * Get the siteNav object from the useNavigation hook - * FIXME: ignorecontrol is not yet implmented here - */ - const { siteNav } = useNavigation({ ignoreControl: true }) - const title = siteNav.about.t +const namespaces = [...pageNs] + +const SearchPage = ({ page }) => { + const title = 'Search' const tip = ( @@ -33,12 +28,12 @@ const SearchPage = ({ page, slug }) => { - - + +
- +

{title}

{tip}
@@ -55,8 +50,7 @@ export default SearchPage export async function getStaticProps() { return { props: { - ...(await serverSideTranslations('en')), - slug: 'search', + ...(await serverSideTranslations('en', namespaces)), page: { path: ['search'], }, diff --git a/sites/dev/prebuild.mjs b/sites/dev/prebuild.mjs new file mode 100644 index 00000000000..c157776e7ea --- /dev/null +++ b/sites/dev/prebuild.mjs @@ -0,0 +1,93 @@ +import { prebuildRunner } from '../shared/prebuild/runner.mjs' + +/* + * This handles the prebuild step for FreeSewing.dev + * It runs via an NPM run script, so in a pure NodeJS context + */ +prebuildRunner({ + /* + * Pass the site to the runner + */ + site: 'dev', + + /* + * This prebuild config determines which prebuild step to run + * For each step, the options are: + * + * - true: Always run + * - false: Never run + * - 'productionOnly': Run in production, mock or skip in develop to save time + * + */ + prebuild: { + // ALWAYS PREBUILD //////////////////////////////////////////////////////// + + /* + * Always prebuild the designs + */ + designs: true, + + /* + * Always prebuild the MDX documentation + */ + docs: true, + + /* + * Always prebuild the translation files + * Even if we only support English on FreeSewing.dev, + * we still rely on the (English) translation of strings + */ + i18n: true, + + /* + * Always prebuild the navigation object (sitenav) and slug lookup tables (sluglut) + */ + navigation: true, + + // PREBUILD IN PRUDUCTION - MOCK/SKIP IN DEV /////////////////////////////// + + /* + * Only prebuild the contributor info in production + * Will be mocked in development mode to save time + */ + contributors: 'productionOnly', + + /* + * Only prebuild the crowdin info (translation statistics) in production + * Will be mocked in development mode to save time + */ + crowdin: 'productionOnly', + + /* + * Only prebuild the favicon files in production + * Will be mocked in development mode to save time + */ + favicon: 'productionOnly', + + /* + * Only prebuild the git author info in production + * Will be mocked in development mode to save time + */ + git: 'productionOnly', + + /* + * Only prebuild the Open Graph (og) images in production + * Will be skipped in development mode to save time + */ + ogImages: 'productionOnly', + + /* + * Only prebuild the patron info in production + * Will be mocked in development mode to save time + */ + patrons: 'productionOnly', + + // NEVER PREBUILD ////////////////////////////////////////////////////////// + + /* + * Never prebuild the MDX posts because there are no such posts on FreeSewing.dev + * We could have leave this step out, but it's included here for documenation purposes + */ + posts: false, + }, +}) diff --git a/sites/lab/package.json b/sites/lab/package.json index 26d3fcde1e4..c50a5e516a5 100644 --- a/sites/lab/package.json +++ b/sites/lab/package.json @@ -23,7 +23,8 @@ "e2e": "yarn playwright test", "lint": "next lint", "buildsitedeps": "cd ../../ && yarn buildall && cd -", - "prebuild": "yarn buildsitedeps && SITE=lab node --experimental-json-modules ../shared/prebuild/index.mjs", + "prebuild": "yarn buildsitedeps && node --experimental-json-modules ./prebuild.mjs", + "prebuildonly": "node --experimental-json-modules ./prebuild.mjs", "start": "yarn prebuild && yarn dev" }, "peerDependencies": {}, diff --git a/sites/org/components/layouts/default.mjs b/sites/org/components/layouts/default.mjs index 20a436c956f..593201184ca 100644 --- a/sites/org/components/layouts/default.mjs +++ b/sites/org/components/layouts/default.mjs @@ -1,36 +1,24 @@ -// Hooks -import { useNavigation } from 'site/hooks/use-navigation.mjs' // Components import { BaseLayout, BaseLayoutLeft, BaseLayoutWide } from 'shared/components/base-layout.mjs' import { NavLinks, Breadcrumbs, MainSections } from 'shared/components/navigation/sitenav.mjs' -export const ns = [] //navNs +export const ns = [] -export const DefaultLayout = ({ children = [], slug, pageTitle = false }) => { - const { siteNav } = useNavigation({ ignoreControl: true }) +export const DefaultLayout = ({ children = [], pageTitle = false }) => ( + + + + + - return ( - - - {slug ? ( - <> - - - - ) : ( -

Slug not passed to layout

- )} -
- - - {pageTitle && ( -
- {slug && } -

{pageTitle}

-
- )} -
{children}
-
-
- ) -} + + {pageTitle && ( +
+ +

{pageTitle}

+
+ )} +
{children}
+
+
+) diff --git a/sites/org/components/layouts/docs.mjs b/sites/org/components/layouts/docs.mjs index 87ef7dd7b83..ffc2d63159c 100644 --- a/sites/org/components/layouts/docs.mjs +++ b/sites/org/components/layouts/docs.mjs @@ -1,5 +1,6 @@ +import { NavigationContext } from 'shared/context/navigation-context.mjs' // Hooks -import { useNavigation } from 'site/hooks/use-navigation.mjs' +import { useContext } from 'react' // Components import Head from 'next/head' import { @@ -45,21 +46,21 @@ export const FrontmatterHead = ({ frontmatter, slug, locale }) => ( ) -export const DocsLayout = ({ children = [], slug, frontmatter, locale }) => { - const { siteNav } = useNavigation({ ignoreControl: true }) +export const DocsLayout = ({ children = [], frontmatter }) => { + const { slug, locale } = useContext(NavigationContext) return ( <> - - + +
- +

{frontmatter.title}

diff --git a/sites/org/components/layouts/post.mjs b/sites/org/components/layouts/post.mjs index 62ce471d6bd..2f050ff3223 100644 --- a/sites/org/components/layouts/post.mjs +++ b/sites/org/components/layouts/post.mjs @@ -1,5 +1,3 @@ -// Hooks -import { useNavigation } from 'site/hooks/use-navigation.mjs' // Components import { FrontmatterHead } from './docs.mjs' import { @@ -17,40 +15,36 @@ import { import { Toc } from 'shared/components/mdx/toc.mjs' import { PrevNext } from 'shared/components/prev-next.mjs' -export const ns = [navNs, 'docs'] //navNs +export const ns = [navNs, 'docs'] const isEndSlug = (slug) => slug.split('/').length === 1 -export const PostLayout = ({ children = [], slug, frontmatter, locale }) => { - const { siteNav } = useNavigation({ ignoreControl: true }) +export const PostLayout = ({ children = [], slug, frontmatter, locale }) => ( + <> + + + + + + - return ( - <> - - - - - - - - -
- -

{frontmatter.title}

-
- -
-
- {children} - -
- - -
+ +
+ +

{frontmatter.title}

+
- - - - ) -} +
+ {children} + +
+ + +
+ +
+
+ + +) diff --git a/sites/org/components/mdx/posts/utils.mjs b/sites/org/components/mdx/posts/utils.mjs index 5ef4c4a88c1..faa1a57af8c 100644 --- a/sites/org/components/mdx/posts/utils.mjs +++ b/sites/org/components/mdx/posts/utils.mjs @@ -1,22 +1,23 @@ import { localePath } from 'shared/utils.mjs' -const preGenerate = 6 -export const numPerPage = 12 +import { siteConfig as config } from 'site/site.config.mjs' -export const getPostSlugPaths = (order) => { +export const getPostSlugPaths = (posts) => { const paths = [] - for (const lang in order) { - for (let i = 0; i < preGenerate; i++) { - paths.push(localePath(lang, `${order[lang][i]}`)) - } + for (const lang in posts) { + paths.push( + ...Object.keys(posts[lang]) + .slice(0, config.posts.preGenerate) + .map((slug) => localePath(lang, slug)) + ) } return paths } -export const getPostIndexPaths = (order, type) => { +export const getPostIndexPaths = (posts, type) => { const paths = [] - for (const language in order) { + for (const language in posts) { paths.push(localePath(language, `${type}/page/1`)) paths.push(localePath(language, `${type}/page/2`)) } @@ -24,13 +25,18 @@ export const getPostIndexPaths = (order, type) => { return paths } -export const getPostIndexProps = (locale, params, order, postInfo) => { - const pageNum = parseInt(params.page) - const numLocPages = Math.ceil(order[locale].length / numPerPage) +export const getPostIndexProps = (pagenr, posts, meta) => { + const pageNum = parseInt(pagenr) + const numLocPages = Math.ceil(Object.keys(posts).length / config.posts.perPage) if (pageNum > numLocPages) return false - const postSlugs = order[locale].slice(numPerPage * (pageNum - 1), numPerPage * pageNum) - const posts = postSlugs.map((s) => ({ ...postInfo[locale][s], s })) + const pagePosts = Object.entries(posts) + .slice(config.posts.perPage * (pageNum - 1), config.posts.perPage * pageNum) + .map(([slug, post]) => ({ + s: slug, + ...post, + ...meta[slug], + })) - return { posts, current: pageNum, total: numLocPages } + return { posts: pagePosts, current: pageNum, total: numLocPages } } diff --git a/sites/org/hooks/use-navigation.mjs b/sites/org/hooks/use-navigation.mjs deleted file mode 100644 index 9776004de03..00000000000 --- a/sites/org/hooks/use-navigation.mjs +++ /dev/null @@ -1,219 +0,0 @@ -import { prebuildNavigation as pbn } from 'site/prebuild/navigation.mjs' -import { useTranslation } from 'next-i18next' -import { freeSewingConfig as conf } from 'shared/config/freesewing.config.mjs' -import { useAccount } from 'shared/hooks/use-account.mjs' -import { designs, tags } from 'shared/config/designs.mjs' -import { objUpdate } from 'shared/utils.mjs' -import { orderedSlugLut } from 'shared/hooks/use-navigation-helpers.mjs' -import { useRouter } from 'next/router' -import { useMemo } from 'react' - -/* - * prebuildNavvigation[locale] holds the navigation structure based on MDX content. - * The entire website only has a few pages that are now MDX-based: - * - 404 => no navigation shown - * - home page => no navvigation shown - * - /contact => Added below - * - * Remember Mc_Shifton: - * Note: Set 'm' to truthy to show this as a main section in the side-navigation (optional) - * Note: Set 'c' to set the control level to hide things from users (optional) - * Note: Set 's' to the slug (optional insofar as it's not a real page (a spacer for the header)) - * Note: Set '_' to never show the page in the site navigation (like the tags pages) - * Note: Set 'h' to indicate this is a top-level page that should be hidden from the side-nav (like search) - * Note: Set 'i' when something should be included as top-level in the collapse side-navigation (optional) - * Note: Set 'f' to add the page to the footer - * Note: Set 't' to the title - * Note: Set 'o' to set the order (optional) - * Note: Set 'n' to mark this as a noisy entry that should always be closed unless active (like blog) - */ - -export const ns = ['account', 'sections', 'design', 'tags', 'designs'] - -const sitePages = (t = false, control = 99) => { - // Handle t not being present - if (!t) t = (string) => string - const pages = { - // Top-level pages that are the sections menu - designs: { - m: 1, - s: 'designs', - t: t('sections:designs'), - n: 1, - tags: { - _: 1, - s: 'designs/tags', - t: t('design:tags'), - o: 'aaa', - }, - }, - patterns: { - m: 1, - s: 'patterns', - t: t('sections:patterns'), - }, - sets: { - m: 1, - s: 'sets', - t: t('sections:sets'), - }, - community: { - m: 1, - s: 'community', - t: t('sections:community'), - }, - account: { - m: 1, - s: 'account', - t: t('sections:account'), - n: 1, - }, - // Top-level pages that are not in the sections menu - apikeys: { - _: 1, - s: 'apikeys', - h: 1, - t: t('apikeys'), - }, - curate: { - s: 'curate', - h: 1, - t: t('curate'), - sets: { - t: t('curateSets'), - s: 'curate/sets', - }, - }, - new: { - m: 1, - s: 'new', - h: 1, - t: t('sections:new'), - pattern: { - t: t('patternNew'), - s: 'new/pattern', - o: 10, - }, - set: { - t: t('newSet'), - s: 'new/set', - 0: 20, - }, - }, - profile: { - s: 'profile', - h: 1, - t: t('yourProfile'), - }, - translation: { - s: 'translation', - h: 1, - t: t('translation'), - join: { - t: t('translation:joinATranslationTeam'), - s: 'translation', - }, - 'suggest-language': { - t: t('translation:suggestLanguage'), - s: 'translation', - }, - }, - sitemap: { - s: 'sitemap', - h: 1, - t: t('sitemap'), - }, - - // Not translated, this is a developer page - typography: { - s: 'typography', - h: 1, - t: 'Typography', - }, - } - for (const section in conf.account.fields) { - for (const [field, controlScore] of Object.entries(conf.account.fields[section])) { - if (Number(control) >= controlScore) - pages.account[field] = { - s: `account/${field}`, - t: t(`account:${field}`), - } - } - } - if (Number(control) >= conf.account.fields.developer.apikeys) - pages.new.apikey = { - s: 'new/apikey', - t: t('newApikey'), - o: 30, - } - pages.account.reload = { - s: `account/reload`, - t: t(`account:reload`), - } - for (const design in designs) { - // pages.designs[design] = { - // t: t(`designs:${design}.t`), - // s: `designs/${design}`, - // } - pages.new.pattern[design] = { - s: `new/${design}`, - t: t(`account:generateANewThing`, { thing: t(`designs:${design}.t`) }), - } - } - for (const tag of tags) { - pages.designs.tags[tag] = { - s: `designs/tags/${tag}`, - t: t(`tags:${tag}`), - } - } - - return pages -} - -export const useNavigation = (param = {}, extra = []) => { - const { ignoreControl = false } = param - // Passing in the locale is not very DRY so let's just grab it from the router - const { locale } = useRouter() - // We need translation - const { t } = useTranslation(ns) - // We need the account if we want to take control into account - const { account } = useAccount() - - const control = ignoreControl ? undefined : account.control - - const value = useMemo(() => { - const siteNav = { - ...pbn[locale], - ...sitePages(t, control), - } - for (const [_path, _data] of extra) { - objUpdate(siteNav, _path, _data) - } - - // Apply some tweaks - siteNav.blog.m = 1 - siteNav.blog.n = 1 - siteNav.showcase.m = 1 - siteNav.showcase.n = 1 - siteNav.docs.m = 1 - siteNav.newsletter._ = true - - // Set order on main sections - siteNav.designs.o = 10 - siteNav.docs.o = 20 - siteNav.blog.o = 30 - siteNav.showcase.o = 40 - siteNav.community.o = 50 - siteNav.patterns.o = 60 - siteNav.sets.o = 70 - siteNav.account.o = 80 - siteNav.new.o = 90 - - return { - siteNav, // Site navigation - slugLut: orderedSlugLut(siteNav), // Slug lookup table - } - }, [locale, extra, control]) - - return value -} diff --git a/sites/org/package.json b/sites/org/package.json index a6444f4e4ef..899f8d71bfe 100644 --- a/sites/org/package.json +++ b/sites/org/package.json @@ -17,13 +17,14 @@ "build": "next build", "cibuild": "yarn build", "clean": "rimraf prebuild/* && rimraf public/locales/*/* && rimraf public/feeds/* && rimraf ../shared/prebuild/data/*", - "predev": "FAST=1 SITE=org node --experimental-json-modules ../shared/prebuild/index.mjs", "dev": "next dev -p 8000", "develop": "next dev -p 8000", "i18n": "SITE=org node ../shared/prebuild/i18n-only.mjs", "lint": "next lint", "buildsitedeps": "cd ../../ && yarn buildall && cd -", - "prebuild": "yarn buildsitedeps && SITE=org node --experimental-json-modules ../shared/prebuild/index.mjs", + "prebuild": "yarn buildsitedeps && node --experimental-json-modules ./prebuild.mjs", + "prebuildonly": "node --experimental-json-modules ./prebuild.mjs", + "predev": "node --experimental-json-modules ./prebuild.mjs", "start": "yarn prebuild && yarn dev" }, "peerDependencies": {}, diff --git a/sites/org/pages/blog/[slug].mjs b/sites/org/pages/blog/[slug].mjs index 0aa095e42e0..b7e67d7d0f9 100644 --- a/sites/org/pages/blog/[slug].mjs +++ b/sites/org/pages/blog/[slug].mjs @@ -1,4 +1,4 @@ -import { order } from 'site/prebuild/blog-paths.mjs' +import { pages as posts } from 'site/prebuild/blog.mjs' import { getPostSlugPaths } from 'site/components/mdx/posts/utils.mjs' import { serverSideTranslations } from 'next-i18next/serverSideTranslations' import { useDynamicMdx } from 'shared/hooks/use-dynamic-mdx.mjs' @@ -68,7 +68,7 @@ export async function getStaticProps({ params, locale }) { export const getStaticPaths = async () => { return { - paths: getPostSlugPaths(order), + paths: getPostSlugPaths(posts), fallback: 'blocking', } } diff --git a/sites/org/pages/blog/page/[page].mjs b/sites/org/pages/blog/page/[page].mjs index bc15271e869..4d666295cb9 100644 --- a/sites/org/pages/blog/page/[page].mjs +++ b/sites/org/pages/blog/page/[page].mjs @@ -1,6 +1,7 @@ // Dependencies import { serverSideTranslations } from 'next-i18next/serverSideTranslations' -import { postInfo, order } from 'site/prebuild/blog-paths.mjs' +import { pages as posts } from 'site/prebuild/blog.mjs' +import { meta } from 'site/prebuild/blog-meta.mjs' import { getPostIndexPaths, getPostIndexProps } from 'site/components/mdx/posts/utils.mjs' // Hooks import { useTranslation } from 'next-i18next' @@ -91,7 +92,7 @@ const BlogIndexPage = ({ posts, page, current, total }) => { export default BlogIndexPage export async function getStaticProps({ locale, params }) { - const props = getPostIndexProps(locale, params, order, postInfo) + const props = getPostIndexProps(params.page, posts[locale], meta) if (props === false) return { notFound: true } @@ -110,7 +111,7 @@ export async function getStaticProps({ locale, params }) { export const getStaticPaths = async () => { return { - paths: getPostIndexPaths(order, 'blog'), + paths: getPostIndexPaths(posts, 'blog'), fallback: 'blocking', } } diff --git a/sites/org/pages/docs/[...slug].mjs b/sites/org/pages/docs/[...slug].mjs index df78f5fda3e..17b9180ba52 100644 --- a/sites/org/pages/docs/[...slug].mjs +++ b/sites/org/pages/docs/[...slug].mjs @@ -1,5 +1,5 @@ // Used in static paths -import { mdxPaths } from 'site/prebuild/mdx-paths.en.mjs' +import { pages } from 'site/prebuild/docs.en.mjs' // Dependencies import { serverSideTranslations } from 'next-i18next/serverSideTranslations' // Hooks @@ -85,7 +85,7 @@ export async function getStaticProps({ locale, params }) { * To learn more, see: https://nextjs.org/docs/basic-features/data-fetching */ export async function getStaticPaths() { - const somePaths = mdxPaths + const somePaths = Object.keys(pages) .filter((path) => path.split('/').length < 5) .filter((path) => path !== 'docs') @@ -96,6 +96,7 @@ export async function getStaticPaths() { ...somePaths.map((key) => `/de/${key}`), ...somePaths.map((key) => `/fr/${key}`), ...somePaths.map((key) => `/nl/${key}`), + ...somePaths.map((key) => `/uk/${key}`), ], fallback: 'blocking', } diff --git a/sites/org/pages/sitemap.mjs b/sites/org/pages/sitemap.mjs index 38d7c8a8f6b..ac7cb313c71 100644 --- a/sites/org/pages/sitemap.mjs +++ b/sites/org/pages/sitemap.mjs @@ -5,9 +5,8 @@ import { useTranslation } from 'next-i18next' // Components import { PageWrapper, ns as pageNs } from 'shared/components/wrappers/page.mjs' import { ReadMore } from 'shared/components/mdx/read-more.mjs' -import { ns as navNs } from 'site/hooks/use-navigation.mjs' -const ns = [...pageNs, navNs, 'common'] +const ns = [...pageNs, 'common'] const SitemapPage = ({ page }) => { const { t } = useTranslation(ns) diff --git a/sites/org/prebuild.mjs b/sites/org/prebuild.mjs new file mode 100644 index 00000000000..888bc9c9fd8 --- /dev/null +++ b/sites/org/prebuild.mjs @@ -0,0 +1,90 @@ +import { prebuildRunner } from '../shared/prebuild/runner.mjs' + +/* + * This handles the prebuild step for FreeSewing.org + * It runs via an NPM run script, so in a pure NodeJS context + */ +prebuildRunner({ + /* + * Pass the site to the runner + */ + site: 'org', + + /* + * This prebuild config determines which prebuild step to run + * For each step, the options are: + * + * - true: Always run + * - false: Never run + * - 'productionOnly': Run in production, mock or skip in develop to save time + * + */ + prebuild: { + // ALWAYS PREBUILD //////////////////////////////////////////////////////// + + /* + * Always prebuild the designs + */ + designs: true, + + /* + * Always prebuild the MDX documentation + */ + docs: true, + + /* + * Always prebuild the MDX posts + */ + posts: true, + + /* + * Always prebuild the translation files + * Even if we only support English on FreeSewing.dev, + * we still rely on the (English) translation of strings + */ + i18n: true, + + /* + * Always prebuild the navigation object (sitenav) and slug lookup tables (sluglut) + */ + navigation: true, + + // PREBUILD IN PRUDUCTION - MOCK/SKIP IN DEV /////////////////////////////// + + /* + * Only prebuild the contributor info in production + * Will be mocked in development mode to save time + */ + contributors: 'productionOnly', + + /* + * Only prebuild the crowdin info (translation statistics) in production + * Will be mocked in development mode to save time + */ + crowdin: 'productionOnly', + + /* + * Only prebuild the favicon files in production + * Will be mocked in development mode to save time + */ + favicon: 'productionOnly', + + /* + * Only prebuild the git author info in production + * Will be mocked in development mode to save time + */ + git: 'productionOnly', + + /* + * Only prebuild the Open Graph (og) images in production + * Will be skipped in development mode to save time + */ + ogImages: 'productionOnly', + + /* + * Only prebuild the patron info in production + * Will be mocked in development mode to save time + */ + patrons: 'productionOnly', + }, +}) diff --git a/sites/org/site.config.mjs b/sites/org/site.config.mjs index 453896d2f4f..c1d0d415da4 100644 --- a/sites/org/site.config.mjs +++ b/sites/org/site.config.mjs @@ -20,4 +20,8 @@ export const siteConfig = { languagesWip: [], site: 'FreeSewing.org', tld: 'org', + posts: { + preGenerate: 6, + perPage: 12, + }, } diff --git a/sites/shared/components/base-layout.mjs b/sites/shared/components/base-layout.mjs index 1edbea306f9..c7db16e48fe 100644 --- a/sites/shared/components/base-layout.mjs +++ b/sites/shared/components/base-layout.mjs @@ -2,7 +2,7 @@ * The default full-page FreeSewing layout */ export const BaseLayout = ({ children = [] }) => ( -
+
{children}
) diff --git a/sites/shared/components/designs/design.mjs b/sites/shared/components/designs/design.mjs index de05ba7eee5..58c86414f4f 100644 --- a/sites/shared/components/designs/design.mjs +++ b/sites/shared/components/designs/design.mjs @@ -4,7 +4,7 @@ import { Difficulty } from 'shared/components/designs/difficulty.mjs' import { designs } from 'shared/config/designs.mjs' import { DesignTag } from 'shared/components/designs/tag.mjs' -export const ns = ['design', 'designs', 'tags'] +export const ns = ['designs', 'tags'] const defaultLink = (design) => `/new/${design}` @@ -27,7 +27,7 @@ export const Design = ({ name, hrefBuilder = false }) => {
{t(`designs:${name}.t`)} - {t('design:difficulty')} + {t('tags:difficulty')}
diff --git a/sites/shared/components/footer/index.mjs b/sites/shared/components/footer/index.mjs index 6c3a13a5152..157e7f894b9 100644 --- a/sites/shared/components/footer/index.mjs +++ b/sites/shared/components/footer/index.mjs @@ -1,7 +1,8 @@ // Dependencies import orderBy from 'lodash.orderby' +import { NavigationContext } from 'shared/context/navigation-context.mjs' // Hooks -import { useNavigation } from 'site/hooks/use-navigation.mjs' +import { useContext } from 'react' // Components import Link from 'next/link' import { Ribbon } from 'shared/components/ribbon.mjs' @@ -14,7 +15,8 @@ export const ns = ['footer', ...sponsorsNs] const onlyFooterLinks = (tree) => orderBy(tree, ['t'], ['asc']).filter((entry) => entry.f) export const Footer = () => { - const { siteNav } = useNavigation({ ignoreControl: true }) + // Grab siteNav from the navigation context + const { siteNav } = useContext(NavigationContext) return (