simple-content-site 3.0.0 → 3.1.0
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/app/composables/useSiteI18n.ts +1 -2
- package/app/composables/useSitePage.ts +6 -13
- package/app/pages/[[lang]]/[...slug].vue +3 -1
- package/modules/config.ts +36 -50
- package/modules/content.ts +13 -0
- package/modules/css.ts +9 -5
- package/nuxt.config.ts +0 -7
- package/package.json +2 -1
- package/README.md +0 -103
|
@@ -5,7 +5,6 @@ export const useSiteI18n = () => {
|
|
|
5
5
|
const config = useRuntimeConfig().public
|
|
6
6
|
const isEnabled = ref(!!config.i18n && config.i18n.locales?.length > 0)
|
|
7
7
|
// todo: reading the strategy like this might cause issues in the future.
|
|
8
|
-
// @ts-expect-error Due to the above comment
|
|
9
8
|
const strategy = ref(config.i18n?.strategy || 'prefix_except_default')
|
|
10
9
|
|
|
11
10
|
if (!isEnabled.value) {
|
|
@@ -25,7 +24,7 @@ export const useSiteI18n = () => {
|
|
|
25
24
|
}
|
|
26
25
|
|
|
27
26
|
const { locale, t } = useI18n()
|
|
28
|
-
const filteredLocales = (config.
|
|
27
|
+
const filteredLocales = (config.scs as { filteredLocales: LocaleObject<string>[] })?.filteredLocales || []
|
|
29
28
|
|
|
30
29
|
return {
|
|
31
30
|
isEnabled,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Collections, PagesCollectionItem } from '@nuxt/content'
|
|
2
2
|
import { kebabCase } from 'scule'
|
|
3
|
+
import { joinURL, withLeadingSlash } from 'ufo'
|
|
3
4
|
|
|
4
5
|
export const useSitePage = () => {
|
|
5
6
|
const { locale, isEnabled, defaultLocale, strategy } = useSiteI18n()
|
|
@@ -13,28 +14,20 @@ export const useSitePage = () => {
|
|
|
13
14
|
|
|
14
15
|
const getKeyForPath = (path: string) => {
|
|
15
16
|
const prefix = toValue(collectionName.value).replaceAll('_', '-')
|
|
16
|
-
const suffix = kebabCase(path.replaceAll('/', '--'))
|
|
17
|
+
const suffix = kebabCase(withLeadingSlash(path).replaceAll('/', '--'))
|
|
17
18
|
return `${prefix}:${suffix}`
|
|
18
19
|
}
|
|
19
20
|
|
|
20
|
-
// const page = ref<PagesCollectionItem | undefined>()
|
|
21
|
-
|
|
22
21
|
const findByPath = async (path: string) => {
|
|
23
22
|
if (isEnabled.value && strategy.value === 'prefix_except_default' && locale.value === defaultLocale.value) {
|
|
24
|
-
const
|
|
25
|
-
if (path !==
|
|
26
|
-
|
|
27
|
-
path = `${prefix}${path}`
|
|
23
|
+
const localePrefix = withLeadingSlash(locale.value)
|
|
24
|
+
if (path !== localePrefix && !path.startsWith(`${localePrefix}/`)) {
|
|
25
|
+
path = joinURL(localePrefix, path)
|
|
28
26
|
}
|
|
29
27
|
}
|
|
30
|
-
return await queryCollection(collectionName.value).path(path).first() as PagesCollectionItem
|
|
28
|
+
return await queryCollection(collectionName.value).path(withLeadingSlash(path)).first() as PagesCollectionItem
|
|
31
29
|
}
|
|
32
30
|
|
|
33
|
-
// watch(() => route.path, async (path) => {
|
|
34
|
-
// const match = await findByPath(path)
|
|
35
|
-
// page.value = match ? match : undefined
|
|
36
|
-
// })
|
|
37
|
-
|
|
38
31
|
return {
|
|
39
32
|
collectionName,
|
|
40
33
|
findByPath,
|
|
@@ -3,6 +3,7 @@ import type { ContentNavigationItem } from '@nuxt/content'
|
|
|
3
3
|
import { findPageHeadline } from '@nuxt/content/utils'
|
|
4
4
|
// import { addPrerenderPath } from '../../utils/prerender'
|
|
5
5
|
import { useSitePage } from '#imports'
|
|
6
|
+
import { withLeadingSlash } from 'ufo'
|
|
6
7
|
|
|
7
8
|
definePageMeta({
|
|
8
9
|
layout: 'page',
|
|
@@ -12,7 +13,8 @@ const route = useRoute()
|
|
|
12
13
|
const { findByPath, getKeyForPath } = useSitePage()
|
|
13
14
|
|
|
14
15
|
const { data: page } = await useAsyncData(() => getKeyForPath(route.path), async () => {
|
|
15
|
-
|
|
16
|
+
console.log('fetching page', route.path)
|
|
17
|
+
return await findByPath(withLeadingSlash(route.path) || '/')
|
|
16
18
|
}, {
|
|
17
19
|
immediate: true,
|
|
18
20
|
})
|
package/modules/config.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createResolver, defineNuxtModule, addPlugin } from '@nuxt/kit'
|
|
1
|
+
import { createResolver, defineNuxtModule, addPlugin, logger } from '@nuxt/kit'
|
|
2
2
|
import { defu } from 'defu'
|
|
3
3
|
import { existsSync } from 'node:fs'
|
|
4
4
|
import { join } from 'node:path'
|
|
@@ -9,7 +9,7 @@ interface SimpleContentSiteOptions {
|
|
|
9
9
|
excludeContent?: string[]
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
const
|
|
12
|
+
const log = logger.withTag('SimpleContentSite')
|
|
13
13
|
|
|
14
14
|
export default defineNuxtModule<SimpleContentSiteOptions>({
|
|
15
15
|
meta: {
|
|
@@ -19,22 +19,12 @@ export default defineNuxtModule<SimpleContentSiteOptions>({
|
|
|
19
19
|
excludeContent: [],
|
|
20
20
|
},
|
|
21
21
|
async setup(_options, nuxt) {
|
|
22
|
+
const { resolve } = createResolver(import.meta.url)
|
|
22
23
|
const dir = nuxt.options.rootDir
|
|
23
24
|
const url = inferSiteURL()
|
|
24
25
|
const meta = await getPackageJsonMetadata(dir)
|
|
25
26
|
const gitInfo = await getLocalGitInfo(dir) || getGitEnv()
|
|
26
|
-
|
|
27
|
-
const siteName = nuxt.options?.site?.name || meta.name || gitInfo?.name || ''
|
|
28
|
-
|
|
29
|
-
// nuxt.options.llms = defu(nuxt.options.llms, {
|
|
30
|
-
// domain: url,
|
|
31
|
-
// title: siteName,
|
|
32
|
-
// description: meta.description || '',
|
|
33
|
-
// full: {
|
|
34
|
-
// title: siteName,
|
|
35
|
-
// description: meta.description || '',
|
|
36
|
-
// },
|
|
37
|
-
// })
|
|
27
|
+
const siteName = (typeof nuxt.options.site === 'object' && nuxt.options.site?.name) || meta.name || gitInfo?.name || ''
|
|
38
28
|
|
|
39
29
|
nuxt.options.site = defu(nuxt.options.site, {
|
|
40
30
|
url,
|
|
@@ -59,11 +49,31 @@ export default defineNuxtModule<SimpleContentSiteOptions>({
|
|
|
59
49
|
branch: getGitBranch(),
|
|
60
50
|
})
|
|
61
51
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
52
|
+
// ...
|
|
53
|
+
nuxt.options.i18n = defu(nuxt.options.i18n, {
|
|
54
|
+
strategy: 'prefix_except_default',
|
|
55
|
+
}) as typeof nuxt.options.i18n
|
|
56
|
+
|
|
57
|
+
// ensure we redirect from index if the strategy requires.
|
|
58
|
+
|
|
59
|
+
// todo: exposing the strategy like this might cause issues in the future.
|
|
60
|
+
// So it will be better to expose the i18n redirect plugin instead from a module.
|
|
61
|
+
nuxt.options.runtimeConfig.public.i18n = defu(nuxt.options.runtimeConfig.public.i18n, {
|
|
62
|
+
strategy: (nuxt.options.i18n ? nuxt.options.i18n.strategy : undefined) || 'prefix_except_default',
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
const i18nStrategy = nuxt.options.runtimeConfig.public.i18n.strategy as string
|
|
66
|
+
|
|
67
|
+
if (i18nStrategy && !['prefix_except_default', 'no_prefix'].includes(i18nStrategy)) {
|
|
68
|
+
console.log(`[I18n] Adding redirect plugin for root since strategy is: ${i18nStrategy}`)
|
|
69
|
+
addPlugin({
|
|
70
|
+
src: resolve('../runtime/plugins/i18n-redirect'),
|
|
71
|
+
mode: 'client',
|
|
72
|
+
})
|
|
73
|
+
}
|
|
74
|
+
|
|
65
75
|
if (nuxt.options.i18n && nuxt.options.i18n.locales) {
|
|
66
|
-
const { resolve
|
|
76
|
+
const { resolve } = createResolver(import.meta.url)
|
|
67
77
|
|
|
68
78
|
// Filter locales to only include existing ones
|
|
69
79
|
const filteredLocales = nuxt.options.i18n.locales.filter((locale) => {
|
|
@@ -78,64 +88,40 @@ export default defineNuxtModule<SimpleContentSiteOptions>({
|
|
|
78
88
|
const hasContentFolder = existsSync(contentPath)
|
|
79
89
|
|
|
80
90
|
if (!hasLocaleFile) {
|
|
81
|
-
|
|
91
|
+
log.warn(`Locale file not found: ${localeCode}.json - skipping locale "${localeCode}"`)
|
|
82
92
|
}
|
|
83
93
|
|
|
84
94
|
if (!hasContentFolder) {
|
|
85
|
-
|
|
95
|
+
log.warn(`Content folder not found: content/${localeCode}/ - skipping locale "${localeCode}"`)
|
|
86
96
|
}
|
|
87
97
|
|
|
88
98
|
return hasLocaleFile && hasContentFolder
|
|
89
99
|
})
|
|
90
100
|
|
|
91
|
-
//
|
|
92
|
-
nuxt.options.i18n = defu(nuxt.options.i18n, {
|
|
93
|
-
strategy: 'prefix_except_default',
|
|
94
|
-
}) as typeof nuxt.options.i18n
|
|
95
|
-
|
|
96
|
-
// todo: exposing the strategy like this might cause issues in the future.
|
|
97
|
-
// So it will be better to expose the i18n redirect plugin instead from a module.
|
|
98
|
-
nuxt.options.runtimeConfig.public.i18n = defu(nuxt.options.runtimeConfig.public.i18n, {
|
|
99
|
-
strategy: nuxt.options.i18n.strategy,
|
|
100
|
-
})
|
|
101
|
-
|
|
102
101
|
// Expose filtered locales
|
|
103
|
-
nuxt.options.runtimeConfig.public.
|
|
102
|
+
nuxt.options.runtimeConfig.public.scs = {
|
|
104
103
|
filteredLocales,
|
|
105
104
|
}
|
|
106
105
|
|
|
107
|
-
//
|
|
108
|
-
|
|
109
|
-
console.log(`[I18n] Adding redirect plugin for root since strategy is: ${nuxt.options.i18n.strategy}`)
|
|
110
|
-
addPlugin({
|
|
111
|
-
src: resolve('../runtime/plugins/i18n-redirect'),
|
|
112
|
-
mode: 'client',
|
|
113
|
-
})
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// @ts-expect-error This is not properly typed after updates.
|
|
117
|
-
nuxt.hook('i18n:registerModule', (register) => {
|
|
106
|
+
// @ts-expect-error This is messed up...
|
|
107
|
+
nuxt.hook('i18n:registerModule', (register: never) => {
|
|
118
108
|
const langDir = resolve('../i18n/locales')
|
|
119
109
|
|
|
120
110
|
const locales = filteredLocales?.map((locale) => {
|
|
121
|
-
// Possibly load custom translations.
|
|
122
|
-
const localeCode = typeof locale === 'string' ? locale : locale.code
|
|
123
|
-
const customLocalePath = resolveRoot('i18n/locales', `${localeCode}.json`)
|
|
124
|
-
const hasCustomLocale = existsSync(customLocalePath)
|
|
125
|
-
const files = hasCustomLocale ? [customLocalePath, `${localeCode}.json`] : [`${localeCode}.json`]
|
|
126
111
|
return typeof locale === 'string'
|
|
127
112
|
? {
|
|
128
113
|
code: locale,
|
|
129
114
|
name: locale,
|
|
130
|
-
|
|
115
|
+
file: `${locale}.json`,
|
|
131
116
|
}
|
|
132
117
|
: {
|
|
133
118
|
code: locale.code,
|
|
134
119
|
name: locale.name || locale.code,
|
|
135
|
-
|
|
120
|
+
file: `${locale.code}.json`,
|
|
136
121
|
}
|
|
137
122
|
})
|
|
138
123
|
|
|
124
|
+
// @ts-expect-error This is amessed up too
|
|
139
125
|
register({
|
|
140
126
|
langDir,
|
|
141
127
|
locales,
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { defineNuxtModule } from '@nuxt/kit'
|
|
2
|
+
|
|
3
|
+
// const { resolve } = createResolver(import.meta.url)
|
|
4
|
+
|
|
5
|
+
export default defineNuxtModule({
|
|
6
|
+
meta: {
|
|
7
|
+
// todo: rename this module to fit it's purpose
|
|
8
|
+
name: 'routing',
|
|
9
|
+
},
|
|
10
|
+
async setup(_options) {
|
|
11
|
+
console.log('content module setup -> Should bootstrap content?! or can i make it work from a separate content.config.ts?')
|
|
12
|
+
},
|
|
13
|
+
})
|
package/modules/css.ts
CHANGED
|
@@ -2,32 +2,36 @@ import { defineNuxtModule, addTemplate, createResolver } from '@nuxt/kit'
|
|
|
2
2
|
import { joinURL } from 'ufo'
|
|
3
3
|
import { resolveModulePath } from 'exsolve'
|
|
4
4
|
|
|
5
|
-
const resolver = createResolver(import.meta.url)
|
|
6
5
|
export default defineNuxtModule({
|
|
7
6
|
meta: {
|
|
8
7
|
name: 'css',
|
|
9
8
|
},
|
|
10
9
|
async setup(_options, nuxt) {
|
|
11
10
|
const dir = nuxt.options.rootDir
|
|
11
|
+
const resolver = createResolver(import.meta.url)
|
|
12
12
|
|
|
13
13
|
const contentDir = joinURL(dir, 'content')
|
|
14
14
|
const uiPath = resolveModulePath('@nuxt/ui', { from: import.meta.url, conditions: ['style'] })
|
|
15
15
|
const tailwindPath = resolveModulePath('tailwindcss', { from: import.meta.url, conditions: ['style'] })
|
|
16
16
|
const layerDir = resolver.resolve('../app')
|
|
17
|
+
// const assistantDir = resolver.resolve('../modules/assistant')
|
|
17
18
|
|
|
18
19
|
const cssTemplate = addTemplate({
|
|
19
|
-
filename: '
|
|
20
|
+
filename: 'simple-content-site.css',
|
|
20
21
|
getContents: () => {
|
|
21
22
|
return `@import ${JSON.stringify(tailwindPath)};
|
|
22
23
|
@import ${JSON.stringify(uiPath)};
|
|
23
24
|
|
|
24
25
|
@source "${contentDir.replace(/\\/g, '/')}/**/*";
|
|
25
26
|
@source "${layerDir.replace(/\\/g, '/')}/**/*";
|
|
26
|
-
@source "../../app.config.ts"
|
|
27
|
+
@source "../../app.config.ts";
|
|
28
|
+
`
|
|
29
|
+
// @source "${assistantDir.replace(/\\/g, '/')}/**/*";
|
|
27
30
|
},
|
|
28
31
|
})
|
|
29
32
|
|
|
30
|
-
nuxt.options.css
|
|
31
|
-
|
|
33
|
+
if (Array.isArray(nuxt.options.css)) {
|
|
34
|
+
nuxt.options.css.unshift(cssTemplate.dst)
|
|
35
|
+
}
|
|
32
36
|
},
|
|
33
37
|
})
|
package/nuxt.config.ts
CHANGED
|
@@ -52,13 +52,6 @@ export default defineNuxtConfig({
|
|
|
52
52
|
// compatibilityVersion: 5,
|
|
53
53
|
// },
|
|
54
54
|
compatibilityDate: '2025-07-22',
|
|
55
|
-
nitro: {
|
|
56
|
-
prerender: {
|
|
57
|
-
crawlLinks: true,
|
|
58
|
-
failOnError: false,
|
|
59
|
-
autoSubfolderIndex: false,
|
|
60
|
-
},
|
|
61
|
-
},
|
|
62
55
|
hooks: {
|
|
63
56
|
'nitro:config'(nitroConfig) {
|
|
64
57
|
const nuxt = useNuxt()
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "simple-content-site",
|
|
3
3
|
"description": "Nuxt layer for simple website with basic functions.",
|
|
4
|
-
"version": "3.
|
|
4
|
+
"version": "3.1.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./nuxt.config.ts",
|
|
7
7
|
"repository": {
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
"@nuxtjs/mdc": "^0.20.0",
|
|
34
34
|
"@nuxtjs/robots": "^5.6.7",
|
|
35
35
|
"@vueuse/core": "^14.1.0",
|
|
36
|
+
"@nuxt/fonts": "^0.14.0",
|
|
36
37
|
"defu": "^6.1.4",
|
|
37
38
|
"exsolve": "^1.0.8",
|
|
38
39
|
"git-url-parse": "^16.1.0",
|
package/README.md
DELETED
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
# simple-content-site layer
|
|
2
|
-
|
|
3
|
-
A simple layer for setting up a simple website using Nuxt, Nuxt UI and Nuxt Content.
|
|
4
|
-
|
|
5
|
-
## Installation
|
|
6
|
-
> You can add the layer to an existing project, or create a fresh one following the methods below.
|
|
7
|
-
|
|
8
|
-
### Create a new project
|
|
9
|
-
|
|
10
|
-
Minimal version
|
|
11
|
-
|
|
12
|
-
```bash
|
|
13
|
-
npx nuxi init -t gh:hareland/simple-content-site/.starters/minimal
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
I18n version
|
|
17
|
-
```bash
|
|
18
|
-
npx nuxi init -t gh:hareland/simple-content-site/.starters/i18n
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
### Add to existing nuxt app
|
|
22
|
-
> This should be done inside a nuxt app.
|
|
23
|
-
|
|
24
|
-
```bash
|
|
25
|
-
npm i simple-content-site
|
|
26
|
-
```
|
|
27
|
-
or
|
|
28
|
-
|
|
29
|
-
```bash
|
|
30
|
-
pnpm add simple-content-site
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
### Extend the layer
|
|
34
|
-
|
|
35
|
-
```ts nuxt.config.ts
|
|
36
|
-
export default defineNuxtConfig({
|
|
37
|
-
extends: ['simple-content-site'],
|
|
38
|
-
})
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
### Running with the layer
|
|
42
|
-
|
|
43
|
-
```bash
|
|
44
|
-
pnpm dev
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
### Adding pages
|
|
48
|
-
|
|
49
|
-
There are two types of pages, `landing` and `page`, in a future version they will be the same.
|
|
50
|
-
|
|
51
|
-
#### Landing page
|
|
52
|
-
|
|
53
|
-
This only covers the front page, either with i18n or without, see examples in the [./playground](./playground) folder.
|
|
54
|
-
This is always the `content/index.md` or `content/[lang]/index.md`.
|
|
55
|
-
|
|
56
|
-
Supports full MDC syntax, and you can implement custom components.
|
|
57
|
-
|
|
58
|
-
#### Page
|
|
59
|
-
|
|
60
|
-
Can more or less do exactly the same as a landing page.
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
## 🛠️ Development
|
|
64
|
-
|
|
65
|
-
### Local Development
|
|
66
|
-
|
|
67
|
-
To contribute to the CLI tool:
|
|
68
|
-
|
|
69
|
-
```bash
|
|
70
|
-
# Clone this repository...
|
|
71
|
-
|
|
72
|
-
# Install dependencies
|
|
73
|
-
pnpm install
|
|
74
|
-
|
|
75
|
-
# Run the dev server
|
|
76
|
-
pnpm run dev
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
### Package Structure
|
|
80
|
-
|
|
81
|
-
This is a monorepo containing:
|
|
82
|
-
|
|
83
|
-
- [**`/layer`**](./layer) - Content Site Nuxt layer (`simple-content-site`)
|
|
84
|
-
- [**`/playground/minimal`**](./playground/minimal) - Minimal example project.
|
|
85
|
-
- [**`/playground/i18n`**](./playground/i18n) - I18n example project
|
|
86
|
-
|
|
87
|
-
## TODO's
|
|
88
|
-
|
|
89
|
-
- [X] Base structure and functionality
|
|
90
|
-
- [X] Support i18n
|
|
91
|
-
- [ ] Find a shared setup for landing and page
|
|
92
|
-
|
|
93
|
-
## Contributions
|
|
94
|
-
|
|
95
|
-
Contributions are welcome.
|
|
96
|
-
|
|
97
|
-
## 📄 License
|
|
98
|
-
|
|
99
|
-
Published under the [MIT](LICENSE) license.
|
|
100
|
-
|
|
101
|
-
---
|
|
102
|
-
|
|
103
|
-
Heavily inspired by [Docus](https://docus.dev)
|