Skip to content

Commit 4c46c9c

Browse files
Upgrade from Nuxt 2 to Nuxt 3 (#31)
Major changes: - Upgrade nuxt from 2.18.1 to 3.14.159 - Upgrade @nuxt/content from v1 to v2 - Replace @nuxtjs/pwa with @vite-pwa/nuxt - Update @nuxtjs/tailwindcss to v6 - Replace Nuxt 2 build modules with Nuxt 3 modules - Update all Vue components to use Composition API with script setup - Convert pages to use Nuxt 3 composables (useAsyncData, queryContent) - Update nuxt.config.js to use defineNuxtConfig - Rename dynamic route from _slug.vue to [slug].vue - Replace <nuxt-content> with <ContentRenderer> - Replace <Nuxt> with <slot> in layout - Update all nuxt-link references to NuxtLink - Add .output to .gitignore for Nuxt 3 build output - Migrate from .eslintrc.js to eslint.config.js for ESLint 9 Co-authored-by: Claude <noreply@anthropic.com>
1 parent 14a7277 commit 4c46c9c

File tree

13 files changed

+14710
-15674
lines changed

13 files changed

+14710
-15674
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ typings/
6767

6868
# nuxt.js build output
6969
.nuxt
70+
.output
7071

7172
# Nuxt generate
7273
dist

components/ArticleCard.vue

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
<template>
22
<div class="my-4 border-2 rounded-lg border-primary">
3-
<nuxt-link :to="{ name: 'blog-slug', params: { slug: article.slug } }">
3+
<NuxtLink :to="`/blog/${article.slug}`">
44
<div
55
class="cover-img"
66
:style="{ backgroundImage: `url(${article.cover_image})` }"
77
></div>
8-
</nuxt-link>
8+
</NuxtLink>
99
<div class="p-3">
10-
<nuxt-link :to="{ name: 'blog-slug', params: { slug: article.slug } }">
10+
<NuxtLink :to="`/blog/${article.slug}`">
1111
<h3 class="font-bold tracking-wider text-center color-secondary">
1212
{{ article.title }}
1313
</h3>
14-
</nuxt-link>
14+
</NuxtLink>
1515
<p class="py-1 my-2 font-medium border-t-2">
1616
{{ article.description }}
1717
</p>
@@ -20,23 +20,17 @@
2020
</div>
2121
</template>
2222

23-
<script>
24-
import ArticleTag from '@/components/ArticleTag'
25-
26-
export default {
27-
components: { ArticleTag },
28-
props: {
29-
article: {
30-
type: Object,
31-
required: true,
32-
},
33-
},
34-
computed: {
35-
tags() {
36-
return this.article.tags.split(',').map((tag) => tag.trim())
37-
},
23+
<script setup>
24+
const props = defineProps({
25+
article: {
26+
type: Object,
27+
required: true,
3828
},
39-
}
29+
})
30+
31+
const tags = computed(() => {
32+
return props.article.tags.split(',').map((tag) => tag.trim())
33+
})
4034
</script>
4135

4236
<style scoped>

components/ArticleTag.vue

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,16 @@
11
<template>
2-
<nuxt-link :class="tag" :to="{ name: 'index', query: { tag } }">{{
2+
<NuxtLink :class="tag" :to="{ path: '/', query: { tag } }">{{
33
tag
4-
}}</nuxt-link>
4+
}}</NuxtLink>
55
</template>
66

7-
<script>
8-
export default {
9-
props: {
10-
tag: {
11-
type: String,
12-
required: true,
13-
},
7+
<script setup>
8+
defineProps({
9+
tag: {
10+
type: String,
11+
required: true,
1412
},
15-
}
13+
})
1614
</script>
1715

1816
<style scoped>

components/SideBar.vue

Lines changed: 23 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,24 @@
44
>
55
<ul class="flex flex-col items-start font-semibold tracking-wider">
66
<li class="self-center">
7-
<nuxt-link :to="{ name: 'index' }">
7+
<NuxtLink to="/">
88
<img
99
class="w-24 h-24 border-4 border-double rounded-full border-primary"
1010
src="~/assets/images/prof-pic.jpg"
1111
alt="Jeremy Ward"
1212
/>
13-
</nuxt-link>
13+
</NuxtLink>
1414
</li>
1515
<li>
16-
<nuxt-link to="/about" @click="console.log('hey there')">
16+
<NuxtLink to="/about">
1717
About
18-
</nuxt-link>
18+
</NuxtLink>
1919
</li>
2020
<li>
21-
<nuxt-link to="/"> Blog </nuxt-link>
21+
<NuxtLink to="/"> Blog </NuxtLink>
2222
</li>
2323
<!-- <li> -->
24-
<!-- <nuxt-link to="/portfolio"> /Works </nuxt-link> -->
24+
<!-- <NuxtLink to="/portfolio"> /Works </NuxtLink> -->
2525
<!-- </li> -->
2626
<li v-for="social in socials" :key="social.id" class="md:hidden">
2727
<a :href="social.href" target="_blank">
@@ -42,33 +42,24 @@
4242
</aside>
4343
</template>
4444

45-
<script>
46-
import SocialIcon from '@/components/SocialIcon'
47-
48-
export default {
49-
components: { SocialIcon },
50-
data() {
51-
return {
52-
socials: [
53-
{
54-
id: 'github',
55-
text: 'GitHub',
56-
href: 'https://github.com/basicbrogrammer',
57-
},
58-
{
59-
id: 'twitter',
60-
text: 'Twitter',
61-
href: 'https://twitter.com/basicbrogrammer',
62-
},
63-
{
64-
id: 'instagram',
65-
text: 'Instagram',
66-
href: 'https://instagram.com/basicbrogrammer',
67-
},
68-
],
69-
}
45+
<script setup>
46+
const socials = [
47+
{
48+
id: 'github',
49+
text: 'GitHub',
50+
href: 'https://github.com/basicbrogrammer',
7051
},
71-
}
52+
{
53+
id: 'twitter',
54+
text: 'Twitter',
55+
href: 'https://twitter.com/basicbrogrammer',
56+
},
57+
{
58+
id: 'instagram',
59+
text: 'Instagram',
60+
href: 'https://instagram.com/basicbrogrammer',
61+
},
62+
]
7263
</script>
7364

7465
<style scoped>

eslint.config.js

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import eslintConfigPrettier from 'eslint-config-prettier'
2+
import pluginVue from 'eslint-plugin-vue'
3+
import vueParser from 'vue-eslint-parser'
4+
5+
export default [
6+
{
7+
ignores: [
8+
'.nuxt',
9+
'.output',
10+
'dist',
11+
'node_modules',
12+
],
13+
},
14+
...pluginVue.configs['flat/recommended'],
15+
{
16+
files: ['**/*.{js,mjs,cjs}'],
17+
languageOptions: {
18+
ecmaVersion: 'latest',
19+
sourceType: 'module',
20+
globals: {
21+
// Browser globals
22+
window: 'readonly',
23+
document: 'readonly',
24+
navigator: 'readonly',
25+
console: 'readonly',
26+
// Node globals
27+
process: 'readonly',
28+
__dirname: 'readonly',
29+
__filename: 'readonly',
30+
module: 'readonly',
31+
require: 'readonly',
32+
// Nuxt globals
33+
defineNuxtConfig: 'readonly',
34+
},
35+
},
36+
},
37+
{
38+
files: ['**/*.vue'],
39+
languageOptions: {
40+
parser: vueParser,
41+
ecmaVersion: 'latest',
42+
sourceType: 'module',
43+
globals: {
44+
// Browser globals
45+
window: 'readonly',
46+
document: 'readonly',
47+
navigator: 'readonly',
48+
console: 'readonly',
49+
// Node globals
50+
process: 'readonly',
51+
// Nuxt globals
52+
defineNuxtConfig: 'readonly',
53+
defineProps: 'readonly',
54+
defineEmits: 'readonly',
55+
defineExpose: 'readonly',
56+
withDefaults: 'readonly',
57+
useRoute: 'readonly',
58+
useRouter: 'readonly',
59+
useAsyncData: 'readonly',
60+
queryContent: 'readonly',
61+
ref: 'readonly',
62+
computed: 'readonly',
63+
watch: 'readonly',
64+
onMounted: 'readonly',
65+
onUnmounted: 'readonly',
66+
},
67+
},
68+
rules: {
69+
'vue/multi-word-component-names': 'off',
70+
'vue/no-v-html': 'off',
71+
},
72+
},
73+
eslintConfigPrettier,
74+
]

layouts/default.vue

Lines changed: 30 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -16,56 +16,52 @@
1616
</svg>
1717
</button>
1818

19-
<nuxt-link
19+
<NuxtLink
2020
to="/"
2121
class="text-lg font-semibold tracking-widest text-gray-900 uppercase rounded-lg dark-mode:text-white focus:outline-none focus:shadow-outline"
22-
>BasicBrogrammer</nuxt-link
22+
>BasicBrogrammer</NuxtLink
2323
>
2424
</div>
2525
<div class="main-body">
26-
<transition name="sidebar-slide">
26+
<Transition name="sidebar-slide">
2727
<SideBar v-if="showSideBar" />
28-
</transition>
28+
</Transition>
2929
<article class="w-screen p-4 md:w-3/4">
3030
<div class="mx-auto w-full md:w-3/4">
31-
<Nuxt />
31+
<slot />
3232
</div>
3333
</article>
3434
</div>
3535
</main>
3636
</template>
37-
<script>
38-
import SideBar from '@/components/SideBar'
3937

40-
export default {
41-
components: { SideBar },
42-
data() {
43-
return {
44-
sideNavOpen: false,
45-
windowWidth: window.innerWidth,
46-
}
47-
},
48-
computed: {
49-
showSideBar() {
50-
return this.windowWidth > 767 || this.sideNavOpen
51-
},
52-
sidebarToggleIcon() {
53-
return this.sideNavOpen
54-
? 'M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z'
55-
: 'M3 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM9 15a1 1 0 011-1h6a1 1 0 110 2h-6a1 1 0 01-1-1z'
56-
},
57-
},
58-
watch: {
59-
$route() {
60-
this.sideNavOpen = false
61-
},
62-
},
63-
mounted() {
38+
<script setup>
39+
const route = useRoute()
40+
const sideNavOpen = ref(false)
41+
const windowWidth = ref(768)
42+
43+
const showSideBar = computed(() => {
44+
return windowWidth.value > 767 || sideNavOpen.value
45+
})
46+
47+
const sidebarToggleIcon = computed(() => {
48+
return sideNavOpen.value
49+
? 'M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z'
50+
: 'M3 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM9 15a1 1 0 011-1h6a1 1 0 110 2h-6a1 1 0 01-1-1z'
51+
})
52+
53+
watch(() => route.path, () => {
54+
sideNavOpen.value = false
55+
})
56+
57+
onMounted(() => {
58+
if (process.client) {
59+
windowWidth.value = window.innerWidth
6460
window.addEventListener('resize', () => {
65-
this.windowWidth = window.innerWidth
61+
windowWidth.value = window.innerWidth
6662
})
67-
},
68-
}
63+
}
64+
})
6965
</script>
7066

7167
<style>

0 commit comments

Comments
 (0)