lightnet 3.12.2 → 4.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +55 -0
- package/exports/content.ts +7 -2
- package/exports/i18n.ts +0 -1
- package/exports/index.ts +1 -5
- package/exports/utils.ts +0 -1
- package/package.json +26 -12
- package/src/astro-integration/config.ts +60 -49
- package/src/astro-integration/integration.ts +13 -24
- package/src/astro-integration/tailwind.ts +86 -0
- package/src/astro-integration/validators/validate-inline-translations.ts +51 -0
- package/src/astro-integration/validators/validate-languages.ts +39 -0
- package/src/astro-integration/virtual.d.ts +8 -6
- package/src/astro-integration/vite-plugin-lightnet-config.ts +29 -9
- package/src/components/CarouselSection.astro +7 -11
- package/src/components/CategoriesSection.astro +2 -2
- package/src/components/HighlightSection.astro +4 -7
- package/src/components/Icon.tsx +2 -2
- package/src/components/MediaGallerySection.astro +88 -68
- package/src/components/MediaList.astro +9 -7
- package/src/components/SearchInput.astro +7 -4
- package/src/components/Section.astro +7 -5
- package/src/components/VideoPlayer.astro +2 -3
- package/src/content/content-schema.ts +129 -142
- package/src/content/get-categories.ts +52 -28
- package/src/content/get-languages.ts +29 -8
- package/src/content/get-media-collections.ts +43 -0
- package/src/content/get-media-types.ts +41 -7
- package/src/content/query-media-items.ts +23 -13
- package/src/i18n/bcp-47.ts +8 -0
- package/src/i18n/get-locale-paths.ts +1 -3
- package/src/i18n/locals.d.ts +21 -3
- package/src/i18n/locals.ts +18 -11
- package/src/i18n/resolve-current-locale.ts +18 -0
- package/src/i18n/resolve-language.ts +10 -5
- package/src/i18n/translate-map.ts +70 -0
- package/src/i18n/translate.ts +68 -47
- package/src/layouts/Page.astro +5 -3
- package/src/layouts/components/LanguagePicker.astro +22 -17
- package/src/layouts/components/Menu.astro +2 -5
- package/src/layouts/components/MenuItem.astro +1 -1
- package/src/layouts/components/PageNavigation.astro +29 -29
- package/src/layouts/components/PageTitle.astro +23 -7
- package/src/pages/404Route.astro +2 -1
- package/src/pages/RootRoute.astro +6 -1
- package/src/pages/details-page/DefaultDetailsPage.astro +9 -2
- package/src/pages/details-page/DetailsPageRoute.astro +1 -2
- package/src/pages/details-page/components/AudioPanel.astro +7 -3
- package/src/pages/details-page/components/AudioPlayer.astro +2 -2
- package/src/pages/details-page/components/ContentSection.astro +67 -44
- package/src/pages/details-page/components/MediaCollection.astro +8 -4
- package/src/pages/details-page/components/MediaCollectionsSection.astro +3 -6
- package/src/pages/details-page/components/main-details/EditButton.astro +22 -10
- package/src/pages/details-page/components/main-details/OpenButton.astro +17 -12
- package/src/pages/details-page/components/main-details/ShareButton.astro +3 -2
- package/src/pages/details-page/components/more-details/Categories.astro +5 -3
- package/src/pages/details-page/components/more-details/Languages.astro +12 -7
- package/src/pages/details-page/utils/create-content-metadata.ts +24 -9
- package/src/pages/details-page/utils/get-translations.ts +6 -0
- package/src/pages/search-page/components/LoadingSkeleton.tsx +6 -5
- package/src/pages/search-page/components/SearchFilter.astro +10 -21
- package/src/pages/search-page/components/SearchFilter.tsx +2 -2
- package/src/pages/search-page/components/SearchList.astro +10 -7
- package/src/pages/search-page/components/SearchListItem.tsx +5 -4
- package/src/pages/search-page/hooks/use-search.ts +5 -2
- package/src/utils/lazy.ts +20 -0
- package/src/utils/paths.ts +40 -3
- package/src/utils/urls.ts +1 -2
- package/src/utils/verify-schema.ts +12 -10
- package/tailwind.config.ts +1 -25
- package/__e2e__/admin.spec.ts +0 -20
- package/__e2e__/basics-fixture.ts +0 -77
- package/__e2e__/fixtures/basics/astro.config.mjs +0 -40
- package/__e2e__/fixtures/basics/node_modules/.bin/astro +0 -21
- package/__e2e__/fixtures/basics/node_modules/.bin/tailwind +0 -21
- package/__e2e__/fixtures/basics/node_modules/.bin/tailwindcss +0 -21
- package/__e2e__/fixtures/basics/node_modules/.bin/tsc +0 -21
- package/__e2e__/fixtures/basics/node_modules/.bin/tsserver +0 -21
- package/__e2e__/fixtures/basics/package.json +0 -21
- package/__e2e__/fixtures/basics/public/favicon.svg +0 -1
- package/__e2e__/fixtures/basics/public/files/example.pdf +0 -0
- package/__e2e__/fixtures/basics/src/assets/logo.png +0 -0
- package/__e2e__/fixtures/basics/src/content/categories/christian-living.json +0 -3
- package/__e2e__/fixtures/basics/src/content/categories/teens.json +0 -3
- package/__e2e__/fixtures/basics/src/content/categories/theology.json +0 -3
- package/__e2e__/fixtures/basics/src/content/media/faithful-freestyle--en.json +0 -13
- package/__e2e__/fixtures/basics/src/content/media/how-to-kickflip--de.json +0 -12
- package/__e2e__/fixtures/basics/src/content/media/images/cover.jpg +0 -0
- package/__e2e__/fixtures/basics/src/content/media/images/how-to-kickflip--en.webp +0 -0
- package/__e2e__/fixtures/basics/src/content/media/skate-sounds--en.json +0 -15
- package/__e2e__/fixtures/basics/src/content/media-collections/how-to-articles.json +0 -3
- package/__e2e__/fixtures/basics/src/content/media-types/audio.json +0 -7
- package/__e2e__/fixtures/basics/src/content/media-types/book.json +0 -9
- package/__e2e__/fixtures/basics/src/content/media-types/video.json +0 -7
- package/__e2e__/fixtures/basics/src/content.config.ts +0 -3
- package/__e2e__/fixtures/basics/src/pages/[locale]/index.astro +0 -16
- package/__e2e__/fixtures/basics/src/translations/de.yml +0 -9
- package/__e2e__/fixtures/basics/src/translations/en.yml +0 -9
- package/__e2e__/fixtures/basics/tailwind.config.mjs +0 -8
- package/__e2e__/global.teardown.ts +0 -5
- package/__e2e__/homepage.spec.ts +0 -123
- package/__e2e__/search.spec.ts +0 -14
- package/__tests__/pages/details-page/create-content-metadata.spec.ts +0 -135
- package/__tests__/utils/markdown.spec.ts +0 -74
- package/__tests__/utils/urls.spec.ts +0 -27
- package/playwright.config.ts +0 -31
- package/src/astro-integration/project-context.ts +0 -5
- package/src/content/compare-media-collection-items.ts +0 -24
- package/src/i18n/resolve-default-locale.ts +0 -19
- package/src/i18n/resolve-locales.ts +0 -5
- package/src/pages/details-page/utils/get-collection-items.ts +0 -29
- package/vitest.config.js +0 -20
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,60 @@
|
|
|
1
1
|
# lightnet
|
|
2
2
|
|
|
3
|
+
## 4.0.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#368](https://github.com/LightNetDev/LightNet/pull/368) [`e47f57b`](https://github.com/LightNetDev/LightNet/commit/e47f57b620ef269f49341e976d71b50e42c23f8a) Thanks [@smn-cds](https://github.com/smn-cds)! - Limit published package contents to runtime files only.
|
|
8
|
+
|
|
9
|
+
## 4.0.0
|
|
10
|
+
|
|
11
|
+
### Major Changes
|
|
12
|
+
|
|
13
|
+
- [#361](https://github.com/LightNetDev/LightNet/pull/361) [`65b3eec`](https://github.com/LightNetDev/LightNet/commit/65b3eec5b68565237b46c6423d21257ad4747dce) Thanks [@smn-cds](https://github.com/smn-cds)! - LightNet now targets Astro v6, so sites using LightNet should upgrade their Astro app and follow the Astro v6 migration guide.
|
|
14
|
+
|
|
15
|
+
- [#361](https://github.com/LightNetDev/LightNet/pull/361) [`65b3eec`](https://github.com/LightNetDev/LightNet/commit/65b3eec5b68565237b46c6423d21257ad4747dce) Thanks [@smn-cds](https://github.com/smn-cds)! - LightNet no longer injects Astro `i18n` routing config during setup, and locale-aware code should now use `Astro.locals.i18n.currentLocale`.
|
|
16
|
+
|
|
17
|
+
- [#361](https://github.com/LightNetDev/LightNet/pull/361) [`65b3eec`](https://github.com/LightNetDev/LightNet/commit/65b3eec5b68565237b46c6423d21257ad4747dce) Thanks [@smn-cds](https://github.com/smn-cds)! - Label fields now use explicit inline locale maps across config and content, and `t(...)` treats string input strictly as a translation key.
|
|
18
|
+
|
|
19
|
+
- [#361](https://github.com/LightNetDev/LightNet/pull/361) [`65b3eec`](https://github.com/LightNetDev/LightNet/commit/65b3eec5b68565237b46c6423d21257ad4747dce) Thanks [@smn-cds](https://github.com/smn-cds)! - `MediaGallerySection` props were renamed for clarity, with `layout` becoming `itemWidth`, `viewLayout` becoming `layout`, and the default layout changing to `"carousel"`.
|
|
20
|
+
|
|
21
|
+
- [#361](https://github.com/LightNetDev/LightNet/pull/361) [`65b3eec`](https://github.com/LightNetDev/LightNet/commit/65b3eec5b68565237b46c6423d21257ad4747dce) Thanks [@smn-cds](https://github.com/smn-cds)! - Media item `content` entries now require explicit typed objects with `type: "upload"` or `type: "link"`.
|
|
22
|
+
|
|
23
|
+
- [#361](https://github.com/LightNetDev/LightNet/pull/361) [`65b3eec`](https://github.com/LightNetDev/LightNet/commit/65b3eec5b68565237b46c6423d21257ad4747dce) Thanks [@smn-cds](https://github.com/smn-cds)! - DaisyUI was removed from LightNet's built-in Tailwind configuration, so projects using `dy-` classes must install and configure DaisyUI themselves.
|
|
24
|
+
|
|
25
|
+
- [#361](https://github.com/LightNetDev/LightNet/pull/361) [`65b3eec`](https://github.com/LightNetDev/LightNet/commit/65b3eec5b68565237b46c6423d21257ad4747dce) Thanks [@smn-cds](https://github.com/smn-cds)! - Deprecated `media-types.detailsPage.coverStyle` support was removed, and cover styling now belongs at top-level `coverImageStyle`.
|
|
26
|
+
|
|
27
|
+
- [#361](https://github.com/LightNetDev/LightNet/pull/361) [`65b3eec`](https://github.com/LightNetDev/LightNet/commit/65b3eec5b68565237b46c6423d21257ad4747dce) Thanks [@smn-cds](https://github.com/smn-cds)! - The `verifySchema` and `verifySchemaAsync` utility exports were removed from `lightnet/utils`.
|
|
28
|
+
Replace them with Zod's native parse functions.
|
|
29
|
+
|
|
30
|
+
- [#361](https://github.com/LightNetDev/LightNet/pull/361) [`65b3eec`](https://github.com/LightNetDev/LightNet/commit/65b3eec5b68565237b46c6423d21257ad4747dce) Thanks [@smn-cds](https://github.com/smn-cds)! - Collection ownership was reversed from `media[].collections` to `media-collections[].mediaItems`, so collections now define membership and order directly.
|
|
31
|
+
|
|
32
|
+
### Minor Changes
|
|
33
|
+
|
|
34
|
+
- [#361](https://github.com/LightNetDev/LightNet/pull/361) [`65b3eec`](https://github.com/LightNetDev/LightNet/commit/65b3eec5b68565237b46c6423d21257ad4747dce) Thanks [@smn-cds](https://github.com/smn-cds)! - User translation keys no longer need the `x.` prefix, so custom keys can use direct names like `site.title` while built-in LightNet keys remain under `ln.*`.
|
|
35
|
+
|
|
36
|
+
- [#361](https://github.com/LightNetDev/LightNet/pull/361) [`65b3eec`](https://github.com/LightNetDev/LightNet/commit/65b3eec5b68565237b46c6423d21257ad4747dce) Thanks [@smn-cds](https://github.com/smn-cds)! - LightNet now uses Lucide icons, and projects should update custom icon names from the deprecated `mdi--` prefix to `lucide--`.
|
|
37
|
+
|
|
38
|
+
- [#361](https://github.com/LightNetDev/LightNet/pull/361) [`65b3eec`](https://github.com/LightNetDev/LightNet/commit/65b3eec5b68565237b46c6423d21257ad4747dce) Thanks [@smn-cds](https://github.com/smn-cds)! - LightNet no longer depends on `@astrojs/tailwind`, so sites should remove that package and use LightNet's built-in Tailwind implementation instead.
|
|
39
|
+
|
|
40
|
+
### Patch Changes
|
|
41
|
+
|
|
42
|
+
- [#361](https://github.com/LightNetDev/LightNet/pull/361) [`65b3eec`](https://github.com/LightNetDev/LightNet/commit/65b3eec5b68565237b46c6423d21257ad4747dce) Thanks [@smn-cds](https://github.com/smn-cds)! - `commonId` is now optional for media items in both `lightnet` schema validation and `@lightnet/sveltia-admin`.
|
|
43
|
+
|
|
44
|
+
- [#361](https://github.com/LightNetDev/LightNet/pull/361) [`65b3eec`](https://github.com/LightNetDev/LightNet/commit/65b3eec5b68565237b46c6423d21257ad4747dce) Thanks [@smn-cds](https://github.com/smn-cds)! - Avoid a dev-time Vite dependency re-optimization caused by mutating Astro integrations during startup.
|
|
45
|
+
|
|
46
|
+
- [#361](https://github.com/LightNetDev/LightNet/pull/361) [`65b3eec`](https://github.com/LightNetDev/LightNet/commit/65b3eec5b68565237b46c6423d21257ad4747dce) Thanks [@smn-cds](https://github.com/smn-cds)! - Export `pathWithBase` from `lightnet/utils` and use that public entrypoint inside `@lightnet/sveltia-admin` so published installs resolve the media edit button controller correctly.
|
|
47
|
+
|
|
48
|
+
- [#361](https://github.com/LightNetDev/LightNet/pull/361) [`65b3eec`](https://github.com/LightNetDev/LightNet/commit/65b3eec5b68565237b46c6423d21257ad4747dce) Thanks [@smn-cds](https://github.com/smn-cds)! - Strip the Astro `base` path before resolving the current locale from the URL pathname so localized pages render with the correct site language under deployments like `/docs/de/...`.
|
|
49
|
+
|
|
50
|
+
- [#361](https://github.com/LightNetDev/LightNet/pull/361) [`65b3eec`](https://github.com/LightNetDev/LightNet/commit/65b3eec5b68565237b46c6423d21257ad4747dce) Thanks [@smn-cds](https://github.com/smn-cds)! - Preserve inline Vite PostCSS options when LightNet injects Tailwind and Autoprefixer so existing user plugins and PostCSS settings are not dropped.
|
|
51
|
+
|
|
52
|
+
- [#361](https://github.com/LightNetDev/LightNet/pull/361) [`65b3eec`](https://github.com/LightNetDev/LightNet/commit/65b3eec5b68565237b46c6423d21257ad4747dce) Thanks [@smn-cds](https://github.com/smn-cds)! - Fix i18n missing-key detection so translations are validated by key presence instead of comparing the translated value to the key.
|
|
53
|
+
|
|
54
|
+
- [#361](https://github.com/LightNetDev/LightNet/pull/361) [`65b3eec`](https://github.com/LightNetDev/LightNet/commit/65b3eec5b68565237b46c6423d21257ad4747dce) Thanks [@smn-cds](https://github.com/smn-cds)! - Respect Astro `base` paths when LightNet builds internal page and API URLs so localized links, redirects, search form actions, and search API requests work correctly under subpath deployments such as `/docs`.
|
|
55
|
+
|
|
56
|
+
- [#361](https://github.com/LightNetDev/LightNet/pull/361) [`65b3eec`](https://github.com/LightNetDev/LightNet/commit/65b3eec5b68565237b46c6423d21257ad4747dce) Thanks [@smn-cds](https://github.com/smn-cds)! - Deduplicate media-collection memberships so repeated media items in one collection do not render the same collection multiple times.
|
|
57
|
+
|
|
3
58
|
## 3.12.2
|
|
4
59
|
|
|
5
60
|
### Patch Changes
|
package/exports/content.ts
CHANGED
|
@@ -13,6 +13,11 @@ import {
|
|
|
13
13
|
queryMediaItems,
|
|
14
14
|
} from "../src/content/query-media-items"
|
|
15
15
|
|
|
16
|
-
export const getMediaItems = (
|
|
16
|
+
export const getMediaItems = async (
|
|
17
17
|
query?: MediaItemQuery<CollectionEntry<"media">>,
|
|
18
|
-
) =>
|
|
18
|
+
) =>
|
|
19
|
+
queryMediaItems(
|
|
20
|
+
getCollection("media"),
|
|
21
|
+
getCollection("media-collections"),
|
|
22
|
+
query ?? {},
|
|
23
|
+
)
|
package/exports/i18n.ts
CHANGED
package/exports/index.ts
CHANGED
package/exports/utils.ts
CHANGED
package/package.json
CHANGED
|
@@ -3,12 +3,15 @@
|
|
|
3
3
|
"description": "LightNet makes it easy to run your own digital media library.",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
|
-
"version": "
|
|
6
|
+
"version": "4.0.1",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
9
9
|
"url": "https://github.com/LightNetDev/lightnet",
|
|
10
10
|
"directory": "packages/lightnet"
|
|
11
11
|
},
|
|
12
|
+
"publishConfig": {
|
|
13
|
+
"access": "public"
|
|
14
|
+
},
|
|
12
15
|
"bugs": {
|
|
13
16
|
"url": "https://github.com/LightNetDev/lightnet/issues"
|
|
14
17
|
},
|
|
@@ -16,6 +19,13 @@
|
|
|
16
19
|
"keywords": [
|
|
17
20
|
"astro-integration"
|
|
18
21
|
],
|
|
22
|
+
"files": [
|
|
23
|
+
"exports",
|
|
24
|
+
"src",
|
|
25
|
+
"tailwind.config.ts",
|
|
26
|
+
"README.md",
|
|
27
|
+
"CHANGELOG.md"
|
|
28
|
+
],
|
|
19
29
|
"exports": {
|
|
20
30
|
".": "./exports/index.ts",
|
|
21
31
|
"./content": "./exports/content.ts",
|
|
@@ -35,38 +45,42 @@
|
|
|
35
45
|
"./api/versions.ts": "./src/api/versions.ts"
|
|
36
46
|
},
|
|
37
47
|
"peerDependencies": {
|
|
38
|
-
"astro": "^
|
|
48
|
+
"astro": "^6.0.0",
|
|
39
49
|
"react": "^19.0.0",
|
|
40
50
|
"react-dom": "^19.0.0",
|
|
41
51
|
"tailwindcss": ">=3.4.0 <4.0.0"
|
|
42
52
|
},
|
|
43
53
|
"dependencies": {
|
|
44
|
-
"@astrojs/react": "^
|
|
45
|
-
"@
|
|
54
|
+
"@astrojs/react": "^5.0.2",
|
|
55
|
+
"@iconify-json/lucide": "^1.2.100",
|
|
46
56
|
"@iconify-json/mdi": "^1.2.3",
|
|
47
57
|
"@iconify/tailwind": "^1.2.0",
|
|
48
58
|
"@tailwindcss/typography": "^0.5.19",
|
|
49
|
-
"@tanstack/react-virtual": "^3.13.
|
|
50
|
-
"
|
|
59
|
+
"@tanstack/react-virtual": "^3.13.23",
|
|
60
|
+
"autoprefixer": "^10.4.27",
|
|
51
61
|
"embla-carousel": "^8.6.0",
|
|
52
62
|
"embla-carousel-wheel-gestures": "^8.1.0",
|
|
53
63
|
"fuse.js": "^7.1.0",
|
|
54
|
-
"i18next": "^25.
|
|
55
|
-
"
|
|
56
|
-
"
|
|
64
|
+
"i18next": "^25.10.10",
|
|
65
|
+
"lucide-react": "^1.7.0",
|
|
66
|
+
"marked": "^17.0.5",
|
|
67
|
+
"postcss": "^8.5.8",
|
|
68
|
+
"postcss-load-config": "^6.0.1",
|
|
69
|
+
"yaml": "^2.8.3"
|
|
57
70
|
},
|
|
58
71
|
"devDependencies": {
|
|
72
|
+
"@internal/e2e-test-utils": "^0.0.1",
|
|
59
73
|
"@playwright/test": "^1.58.2",
|
|
60
|
-
"@types/node": "^22.19.11",
|
|
61
74
|
"@types/react": "^19.2.14",
|
|
75
|
+
"astro": "^6.1.1",
|
|
62
76
|
"typescript": "^5.9.3",
|
|
63
|
-
"vitest": "^4.
|
|
77
|
+
"vitest": "^4.1.2"
|
|
64
78
|
},
|
|
65
79
|
"engines": {
|
|
66
80
|
"node": ">=22"
|
|
67
81
|
},
|
|
68
82
|
"scripts": {
|
|
69
83
|
"test": "vitest",
|
|
70
|
-
"e2e": "playwright
|
|
84
|
+
"e2e": "playwright test"
|
|
71
85
|
}
|
|
72
86
|
}
|
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
import { z } from "astro/zod"
|
|
2
2
|
|
|
3
|
+
import { isBcp47 } from "../i18n/bcp-47"
|
|
4
|
+
import { validateInlineTranslations } from "./validators/validate-inline-translations"
|
|
5
|
+
import { validateLanguages } from "./validators/validate-languages"
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Translations by BCP-47 tags.
|
|
9
|
+
* We can only do basic validation here because we cannot access locales
|
|
10
|
+
* from the same config.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* {
|
|
14
|
+
* de: "Hallo",
|
|
15
|
+
* en: "Hello"
|
|
16
|
+
* }
|
|
17
|
+
*/
|
|
18
|
+
export const inlineTranslationSchema = z.record(
|
|
19
|
+
z.string(),
|
|
20
|
+
z.string().nonempty(),
|
|
21
|
+
)
|
|
22
|
+
|
|
3
23
|
/**
|
|
4
24
|
* Link Schema.
|
|
5
25
|
*/
|
|
@@ -12,9 +32,10 @@ const linkSchema = z.object({
|
|
|
12
32
|
href: z.string(),
|
|
13
33
|
/**
|
|
14
34
|
* Label to be used for the link.
|
|
15
|
-
*
|
|
35
|
+
* Must define a value for the default site locale.
|
|
36
|
+
* Other configured site locales are optional.
|
|
16
37
|
*/
|
|
17
|
-
label:
|
|
38
|
+
label: inlineTranslationSchema,
|
|
18
39
|
/**
|
|
19
40
|
* If this is set to true the currentLocale will be appended to
|
|
20
41
|
* the href path. Eg. for href="/about"
|
|
@@ -27,63 +48,35 @@ const linkSchema = z.object({
|
|
|
27
48
|
requiresLocale: z.boolean().default(true),
|
|
28
49
|
})
|
|
29
50
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
51
|
+
const bcp47Schema = z.string().refine(isBcp47, {
|
|
52
|
+
message: "Invalid BCP-47 language code",
|
|
53
|
+
})
|
|
54
|
+
|
|
33
55
|
const languageSchema = z
|
|
34
56
|
.object({
|
|
35
57
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* This will be the identifier of this language and will
|
|
39
|
-
* also appear on the URL paths of the website.
|
|
58
|
+
* BCP-47 code for this language.
|
|
40
59
|
*/
|
|
41
|
-
code:
|
|
60
|
+
code: bcp47Schema,
|
|
42
61
|
/**
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
* Can either be a fixed string or a translation key.
|
|
62
|
+
* Display name for this language.
|
|
46
63
|
*/
|
|
47
|
-
label:
|
|
64
|
+
label: inlineTranslationSchema,
|
|
48
65
|
/**
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
* Make sure to provide translations inside the `src/translations/` folder.
|
|
52
|
-
*
|
|
53
|
-
* Default is `false`
|
|
66
|
+
* Whether this language should be exposed as a site UI language.
|
|
54
67
|
*/
|
|
55
68
|
isSiteLanguage: z.boolean().default(false),
|
|
56
69
|
/**
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
* The default language will be used as a fallback when translations are missing
|
|
60
|
-
* also this will be the language selected when a user visits the site on the `/` path.
|
|
61
|
-
*
|
|
62
|
-
* Setting this to `true` will also set `isSiteLanguage` to `true`.
|
|
63
|
-
*
|
|
64
|
-
* Default is `false`
|
|
70
|
+
* Whether this language is the default site language.
|
|
65
71
|
*/
|
|
66
72
|
isDefaultSiteLanguage: z.boolean().default(false),
|
|
67
73
|
/**
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
* This is used when no translation key is defined for this language.
|
|
71
|
-
* The system will iterate over this array in order and use the first language for which a
|
|
72
|
-
* matching translation key is found.
|
|
73
|
-
*
|
|
74
|
-
* If no match is found from the fallback languages, the system will
|
|
75
|
-
* attempt the translation using the default site language.
|
|
76
|
-
*
|
|
77
|
-
* If the translation still cannot be resolved, it will then fall back to the English
|
|
78
|
-
* translation as a final resort.
|
|
79
|
-
*
|
|
80
|
-
* @example ["fr", "it"]
|
|
74
|
+
* Ordered fallback language codes used when a translation key is missing.
|
|
81
75
|
*/
|
|
82
|
-
fallbackLanguages: z.
|
|
76
|
+
fallbackLanguages: z.array(bcp47Schema).default([]),
|
|
83
77
|
})
|
|
84
78
|
.transform((language) => ({
|
|
85
79
|
...language,
|
|
86
|
-
// if language is default site language also set is site language to true.
|
|
87
80
|
isSiteLanguage: language.isDefaultSiteLanguage || language.isSiteLanguage,
|
|
88
81
|
}))
|
|
89
82
|
|
|
@@ -123,11 +116,11 @@ export const configSchema = z.object({
|
|
|
123
116
|
/**
|
|
124
117
|
* Title of the web site.
|
|
125
118
|
*/
|
|
126
|
-
title:
|
|
119
|
+
title: inlineTranslationSchema,
|
|
127
120
|
/**
|
|
128
|
-
*
|
|
121
|
+
* Languages supported by this site.
|
|
129
122
|
*/
|
|
130
|
-
languages:
|
|
123
|
+
languages: z.array(languageSchema).min(1).superRefine(validateLanguages),
|
|
131
124
|
/**
|
|
132
125
|
* Favicons for your site.
|
|
133
126
|
*/
|
|
@@ -155,9 +148,10 @@ export const configSchema = z.object({
|
|
|
155
148
|
src: z.string(),
|
|
156
149
|
/**
|
|
157
150
|
* Alt attribute to add for screen reader etc.
|
|
158
|
-
*
|
|
151
|
+
* Must define a value for the default site locale.
|
|
152
|
+
* Other configured site locales are optional.
|
|
159
153
|
*/
|
|
160
|
-
alt:
|
|
154
|
+
alt: inlineTranslationSchema.optional(),
|
|
161
155
|
/**
|
|
162
156
|
* Size in px to use for the logo on the header bar.
|
|
163
157
|
* The size will be applied to the shorter side of your logo image.
|
|
@@ -223,8 +217,25 @@ export const configSchema = z.object({
|
|
|
223
217
|
experimental: z.object({}).optional(),
|
|
224
218
|
})
|
|
225
219
|
|
|
226
|
-
export
|
|
220
|
+
export const extendedConfigSchema = configSchema.transform((config, ctx) => {
|
|
221
|
+
const locales = config.languages
|
|
222
|
+
.filter((language) => language.isSiteLanguage)
|
|
223
|
+
.map((language) => language.code)
|
|
224
|
+
const defaultLocale =
|
|
225
|
+
config.languages.find((language) => language.isDefaultSiteLanguage)?.code ??
|
|
226
|
+
""
|
|
227
|
+
|
|
228
|
+
validateInlineTranslations(config, locales, defaultLocale, ctx)
|
|
229
|
+
|
|
230
|
+
return {
|
|
231
|
+
...config,
|
|
232
|
+
locales,
|
|
233
|
+
defaultLocale,
|
|
234
|
+
}
|
|
235
|
+
})
|
|
236
|
+
|
|
227
237
|
export type Link = z.input<typeof linkSchema>
|
|
238
|
+
export type Language = z.output<typeof languageSchema>
|
|
228
239
|
|
|
229
240
|
export type LightnetConfig = z.input<typeof configSchema>
|
|
230
|
-
export type
|
|
241
|
+
export type ExtendedLightnetConfig = z.output<typeof extendedConfigSchema>
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
/// <reference path="../i18n/locals.d.ts" />
|
|
2
2
|
import react from "@astrojs/react"
|
|
3
|
-
import tailwind from "@astrojs/tailwind"
|
|
4
3
|
import type { AstroIntegration } from "astro"
|
|
4
|
+
import { AstroError } from "astro/errors"
|
|
5
5
|
|
|
6
|
-
import { resolveDefaultLocale } from "../i18n/resolve-default-locale"
|
|
7
|
-
import { resolveLocales } from "../i18n/resolve-locales"
|
|
8
6
|
import { verifySchema } from "../utils/verify-schema"
|
|
9
|
-
import {
|
|
7
|
+
import { extendedConfigSchema, type LightnetConfig } from "./config"
|
|
8
|
+
import tailwind from "./tailwind"
|
|
10
9
|
import { vitePluginLightnetConfig } from "./vite-plugin-lightnet-config"
|
|
11
10
|
|
|
12
11
|
export function lightnet(lightnetConfig: LightnetConfig): AstroIntegration {
|
|
@@ -20,11 +19,18 @@ export function lightnet(lightnetConfig: LightnetConfig): AstroIntegration {
|
|
|
20
19
|
logger,
|
|
21
20
|
addMiddleware,
|
|
22
21
|
}) => {
|
|
22
|
+
if (!astroConfig.site) {
|
|
23
|
+
throw new AstroError(
|
|
24
|
+
"Invalid LightNet configuration",
|
|
25
|
+
"Set `site` in your Astro config. LightNet requires Astro `site` to be configured.",
|
|
26
|
+
)
|
|
27
|
+
}
|
|
28
|
+
|
|
23
29
|
const config = verifySchema(
|
|
24
|
-
|
|
30
|
+
extendedConfigSchema,
|
|
25
31
|
lightnetConfig,
|
|
26
32
|
"Invalid LightNet configuration",
|
|
27
|
-
"Fix these errors on the LightNet configuration
|
|
33
|
+
"Fix these errors on the LightNet configuration:",
|
|
28
34
|
)
|
|
29
35
|
|
|
30
36
|
injectRoute({
|
|
@@ -65,28 +71,11 @@ export function lightnet(lightnetConfig: LightnetConfig): AstroIntegration {
|
|
|
65
71
|
|
|
66
72
|
addMiddleware({ entrypoint: "lightnet/locals", order: "pre" })
|
|
67
73
|
|
|
68
|
-
astroConfig.integrations.push(
|
|
69
|
-
tailwind({ applyBaseStyles: false }),
|
|
70
|
-
react(),
|
|
71
|
-
)
|
|
72
|
-
|
|
73
74
|
updateConfig({
|
|
75
|
+
integrations: [tailwind(), react()],
|
|
74
76
|
vite: {
|
|
75
77
|
plugins: [vitePluginLightnetConfig(config, astroConfig, logger)],
|
|
76
78
|
},
|
|
77
|
-
i18n: {
|
|
78
|
-
defaultLocale: resolveDefaultLocale(config),
|
|
79
|
-
locales: resolveLocales(config),
|
|
80
|
-
routing: {
|
|
81
|
-
redirectToDefaultLocale: false,
|
|
82
|
-
// We need to set this to false to allow for
|
|
83
|
-
// admin paths without locale. But actually
|
|
84
|
-
// the default locale will be prefixed for regular
|
|
85
|
-
// LightNet pages.
|
|
86
|
-
prefixDefaultLocale: false,
|
|
87
|
-
fallbackType: "rewrite",
|
|
88
|
-
},
|
|
89
|
-
},
|
|
90
79
|
})
|
|
91
80
|
},
|
|
92
81
|
},
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { fileURLToPath } from "node:url"
|
|
2
|
+
|
|
3
|
+
import type { AstroIntegration } from "astro"
|
|
4
|
+
import autoprefixerPlugin from "autoprefixer"
|
|
5
|
+
import type { AcceptedPlugin, ProcessOptions } from "postcss"
|
|
6
|
+
import postcssrc from "postcss-load-config"
|
|
7
|
+
import tailwindPlugin from "tailwindcss"
|
|
8
|
+
|
|
9
|
+
type PostcssInlineOptions =
|
|
10
|
+
| undefined
|
|
11
|
+
| string
|
|
12
|
+
| (ProcessOptions & {
|
|
13
|
+
plugins?: AcceptedPlugin[]
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
function isInlinePostCssOptions(
|
|
17
|
+
postcssInlineOptions: PostcssInlineOptions,
|
|
18
|
+
): postcssInlineOptions is Exclude<PostcssInlineOptions, undefined | string> {
|
|
19
|
+
return (
|
|
20
|
+
typeof postcssInlineOptions === "object" && postcssInlineOptions !== null
|
|
21
|
+
)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function getPostCssConfig(
|
|
25
|
+
root: string,
|
|
26
|
+
postcssInlineOptions: PostcssInlineOptions,
|
|
27
|
+
) {
|
|
28
|
+
let postcssConfigResult
|
|
29
|
+
if (!isInlinePostCssOptions(postcssInlineOptions)) {
|
|
30
|
+
const searchPath =
|
|
31
|
+
typeof postcssInlineOptions === "string" ? postcssInlineOptions : root
|
|
32
|
+
try {
|
|
33
|
+
postcssConfigResult = await postcssrc({}, searchPath)
|
|
34
|
+
} catch {
|
|
35
|
+
postcssConfigResult = null
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return postcssConfigResult
|
|
39
|
+
}
|
|
40
|
+
async function getViteConfiguration(
|
|
41
|
+
root: string,
|
|
42
|
+
postcssInlineOptions: PostcssInlineOptions,
|
|
43
|
+
) {
|
|
44
|
+
const postcssConfigResult = await getPostCssConfig(root, postcssInlineOptions)
|
|
45
|
+
const inlinePostcssOptions = isInlinePostCssOptions(postcssInlineOptions)
|
|
46
|
+
? postcssInlineOptions
|
|
47
|
+
: null
|
|
48
|
+
const postcssOptions = inlinePostcssOptions
|
|
49
|
+
? { ...inlinePostcssOptions }
|
|
50
|
+
: (postcssConfigResult?.options ?? {})
|
|
51
|
+
const postcssPlugins =
|
|
52
|
+
inlinePostcssOptions?.plugins?.slice() ??
|
|
53
|
+
postcssConfigResult?.plugins?.slice() ??
|
|
54
|
+
[]
|
|
55
|
+
postcssPlugins.push(tailwindPlugin())
|
|
56
|
+
postcssPlugins.push(autoprefixerPlugin())
|
|
57
|
+
return {
|
|
58
|
+
css: {
|
|
59
|
+
postcss: {
|
|
60
|
+
...postcssOptions,
|
|
61
|
+
plugins: postcssPlugins,
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Astro v6 no longer supports Tailwind CSS v3 through `@astrojs/tailwind`,
|
|
68
|
+
// so LightNet provides its own minimal integration for now.
|
|
69
|
+
// Remove this in the next major release once Tailwind v4 is the baseline,
|
|
70
|
+
// and then also remove `autoprefixer`, `postcss`, and `postcss-load-config`.
|
|
71
|
+
function tailwindIntegration(): AstroIntegration {
|
|
72
|
+
return {
|
|
73
|
+
name: "@lightnet/tailwind",
|
|
74
|
+
hooks: {
|
|
75
|
+
"astro:config:setup": async ({ config, updateConfig }) => {
|
|
76
|
+
updateConfig({
|
|
77
|
+
vite: await getViteConfiguration(
|
|
78
|
+
fileURLToPath(config.root),
|
|
79
|
+
config.vite.css?.postcss,
|
|
80
|
+
),
|
|
81
|
+
})
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
export { tailwindIntegration as default }
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { z } from "astro/zod"
|
|
2
|
+
|
|
3
|
+
export const validateInlineTranslations = (
|
|
4
|
+
config: {
|
|
5
|
+
title: Record<string, string>
|
|
6
|
+
languages: { label: Record<string, string> }[]
|
|
7
|
+
mainMenu?: { label: Record<string, string> }[]
|
|
8
|
+
logo?: { alt?: Record<string, string> }
|
|
9
|
+
},
|
|
10
|
+
locales: string[],
|
|
11
|
+
defaultLocale: string,
|
|
12
|
+
ctx: z.RefinementCtx,
|
|
13
|
+
) => {
|
|
14
|
+
const validateInlineTranslation = (
|
|
15
|
+
inlineTranslation: Record<string, string> | undefined,
|
|
16
|
+
path: (string | number)[],
|
|
17
|
+
) => {
|
|
18
|
+
if (!inlineTranslation) {
|
|
19
|
+
return
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (!(defaultLocale in inlineTranslation)) {
|
|
23
|
+
ctx.addIssue({
|
|
24
|
+
code: "custom",
|
|
25
|
+
message: `Missing translation for default locale "${defaultLocale}"`,
|
|
26
|
+
path: [...path, defaultLocale],
|
|
27
|
+
})
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
for (const locale of Object.keys(inlineTranslation)) {
|
|
31
|
+
if (locales.includes(locale)) {
|
|
32
|
+
continue
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
ctx.addIssue({
|
|
36
|
+
code: "custom",
|
|
37
|
+
message: `Invalid locale "${locale}". Inline translations only support configured site locales: ${locales.join(", ")}`,
|
|
38
|
+
path: [...path, locale],
|
|
39
|
+
})
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
validateInlineTranslation(config.title, ["title"])
|
|
44
|
+
for (const [index, language] of config.languages.entries()) {
|
|
45
|
+
validateInlineTranslation(language.label, ["languages", index, "label"])
|
|
46
|
+
}
|
|
47
|
+
for (const [index, link] of (config.mainMenu ?? []).entries()) {
|
|
48
|
+
validateInlineTranslation(link.label, ["mainMenu", index, "label"])
|
|
49
|
+
}
|
|
50
|
+
validateInlineTranslation(config.logo?.alt, ["logo", "alt"])
|
|
51
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { z } from "astro/zod"
|
|
2
|
+
|
|
3
|
+
type Language = {
|
|
4
|
+
code: string
|
|
5
|
+
isDefaultSiteLanguage?: boolean
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const validateLanguages = (
|
|
9
|
+
languages: Language[],
|
|
10
|
+
ctx: z.RefinementCtx,
|
|
11
|
+
) => {
|
|
12
|
+
const seen = new Set<string>()
|
|
13
|
+
let defaultCount = 0
|
|
14
|
+
|
|
15
|
+
for (const [index, language] of languages.entries()) {
|
|
16
|
+
const { code, isDefaultSiteLanguage } = language
|
|
17
|
+
|
|
18
|
+
if (!seen.has(code)) {
|
|
19
|
+
seen.add(code)
|
|
20
|
+
} else {
|
|
21
|
+
ctx.addIssue({
|
|
22
|
+
code: "custom",
|
|
23
|
+
message: `Duplicate language code "${code}"`,
|
|
24
|
+
path: [index, "code"],
|
|
25
|
+
})
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (isDefaultSiteLanguage) {
|
|
29
|
+
defaultCount += 1
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (defaultCount !== 1) {
|
|
34
|
+
ctx.addIssue({
|
|
35
|
+
code: "custom",
|
|
36
|
+
message: "Exactly one language must define isDefaultSiteLanguage: true",
|
|
37
|
+
})
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
declare module "virtual:lightnet/config" {
|
|
2
|
-
const config: import("./config").
|
|
2
|
+
const config: import("./config").ExtendedLightnetConfig
|
|
3
3
|
export default config
|
|
4
4
|
}
|
|
5
5
|
|
|
@@ -8,11 +8,6 @@ declare module "virtual:lightnet/logo" {
|
|
|
8
8
|
export default logo
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
declare module "virtual:lightnet/project-context" {
|
|
12
|
-
const context: import("./project-context").ProjectContext
|
|
13
|
-
export default context
|
|
14
|
-
}
|
|
15
|
-
|
|
16
11
|
declare module "virtual:lightnet/components/CustomHead" {
|
|
17
12
|
const CustomHead: ((props: Record<string, any>) => any) | undefined
|
|
18
13
|
export default CustomHead
|
|
@@ -22,3 +17,10 @@ declare module "virtual:lightnet/components/CustomFooter" {
|
|
|
22
17
|
const CustomFooter: ((props: Record<string, any>) => any) | undefined
|
|
23
18
|
export default CustomFooter
|
|
24
19
|
}
|
|
20
|
+
|
|
21
|
+
declare module "virtual:lightnet/components/media-item-edit-button-controller" {
|
|
22
|
+
const mediaItemEditButtonController:
|
|
23
|
+
| { shouldShow: () => boolean; createHref: (mediaId: string) => string }
|
|
24
|
+
| undefined
|
|
25
|
+
export default mediaItemEditButtonController
|
|
26
|
+
}
|