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.
@@ -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.Site as { filteredLocales: LocaleObject<string>[] })?.filteredLocales || []
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 prefix = `/${locale.value}`
25
- if (path !== prefix && !path.startsWith(`${prefix}/`)) {
26
- // we need to inject a virtual path to find the page in the collection
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
- return await findByPath(route.path)
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 { resolve } = createResolver(import.meta.url)
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
- // @ts-expect-error This is not typed.
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
- ** I18N
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: resolveRoot } = createResolver(dir)
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
- console.warn(`[Site] Locale file not found: ${localeCode}.json - skipping locale "${localeCode}"`)
91
+ log.warn(`Locale file not found: ${localeCode}.json - skipping locale "${localeCode}"`)
82
92
  }
83
93
 
84
94
  if (!hasContentFolder) {
85
- console.warn(`[Site] Content folder not found: content/${localeCode}/ - skipping locale "${localeCode}"`)
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.Site = {
102
+ nuxt.options.runtimeConfig.public.scs = {
104
103
  filteredLocales,
105
104
  }
106
105
 
107
- // ensure we redirect from index if the strategy requires.
108
- if (nuxt.options.i18n.strategy && !['prefix_except_default', 'no_prefix'].includes(nuxt.options.i18n.strategy)) {
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
- files,
115
+ file: `${locale}.json`,
131
116
  }
132
117
  : {
133
118
  code: locale.code,
134
119
  name: locale.name || locale.code,
135
- files,
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: 'website.css',
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
- nuxt.options.css.unshift(cssTemplate.dst)
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.0.0",
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)