nuxt-users 1.38.0 → 1.40.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.
Files changed (94) hide show
  1. package/README.md +13 -1
  2. package/dist/cli.mjs +57 -27
  3. package/dist/module.d.mts +2 -2
  4. package/dist/module.d.ts +2 -2
  5. package/dist/module.json +4 -1
  6. package/dist/module.mjs +29 -7
  7. package/dist/runtime/assets/nuxt-users.css +1 -1
  8. package/dist/runtime/components/NUsersEmailConfirmation.d.vue.ts +5 -3
  9. package/dist/runtime/components/NUsersEmailConfirmation.vue +22 -15
  10. package/dist/runtime/components/NUsersEmailConfirmation.vue.d.ts +5 -3
  11. package/dist/runtime/components/NUsersGoogleLoginButton.d.vue.ts +4 -2
  12. package/dist/runtime/components/NUsersGoogleLoginButton.vue +5 -2
  13. package/dist/runtime/components/NUsersGoogleLoginButton.vue.d.ts +4 -2
  14. package/dist/runtime/components/NUsersList.vue +25 -11
  15. package/dist/runtime/components/NUsersLoginForm.d.vue.ts +13 -2
  16. package/dist/runtime/components/NUsersLoginForm.vue +35 -13
  17. package/dist/runtime/components/NUsersLoginForm.vue.d.ts +13 -2
  18. package/dist/runtime/components/NUsersLogoutLink.d.vue.ts +3 -4
  19. package/dist/runtime/components/NUsersLogoutLink.vue +8 -5
  20. package/dist/runtime/components/NUsersLogoutLink.vue.d.ts +3 -4
  21. package/dist/runtime/components/NUsersPasswordStrengthIndicator.d.vue.ts +6 -0
  22. package/dist/runtime/components/NUsersPasswordStrengthIndicator.vue +17 -9
  23. package/dist/runtime/components/NUsersPasswordStrengthIndicator.vue.d.ts +6 -0
  24. package/dist/runtime/components/NUsersProfileInfo.d.vue.ts +13 -1
  25. package/dist/runtime/components/NUsersProfileInfo.vue +23 -9
  26. package/dist/runtime/components/NUsersProfileInfo.vue.d.ts +13 -1
  27. package/dist/runtime/components/NUsersRegisterForm.d.vue.ts +26 -11
  28. package/dist/runtime/components/NUsersRegisterForm.vue +34 -16
  29. package/dist/runtime/components/NUsersRegisterForm.vue.d.ts +26 -11
  30. package/dist/runtime/components/NUsersResetPasswordForm.d.vue.ts +11 -2
  31. package/dist/runtime/components/NUsersResetPasswordForm.vue +25 -12
  32. package/dist/runtime/components/NUsersResetPasswordForm.vue.d.ts +11 -2
  33. package/dist/runtime/components/NUsersUserCard.d.vue.ts +2 -0
  34. package/dist/runtime/components/NUsersUserCard.vue +8 -4
  35. package/dist/runtime/components/NUsersUserCard.vue.d.ts +2 -0
  36. package/dist/runtime/components/NUsersUserForm.d.vue.ts +2 -2
  37. package/dist/runtime/components/NUsersUserForm.vue +5 -4
  38. package/dist/runtime/components/NUsersUserForm.vue.d.ts +2 -2
  39. package/dist/runtime/composables/useAuthentication.js +14 -0
  40. package/dist/runtime/composables/useNuxtUsersLocale.d.ts +15 -0
  41. package/dist/runtime/composables/useNuxtUsersLocale.js +27 -0
  42. package/dist/runtime/composables/usePasswordValidation.d.ts +1 -1
  43. package/dist/runtime/composables/usePublicPaths.js +2 -1
  44. package/dist/runtime/locales/en.d.ts +2 -0
  45. package/dist/runtime/locales/en.js +130 -0
  46. package/dist/runtime/locales/hu.d.ts +3 -0
  47. package/dist/runtime/locales/hu.js +188 -0
  48. package/dist/runtime/locales/index.d.ts +5 -0
  49. package/dist/runtime/locales/index.js +8 -0
  50. package/dist/runtime/server/api/nuxt-users/[id].delete.js +1 -1
  51. package/dist/runtime/server/api/nuxt-users/[id].get.js +1 -1
  52. package/dist/runtime/server/api/nuxt-users/[id].patch.js +1 -1
  53. package/dist/runtime/server/api/nuxt-users/auth/google/callback.get.js +1 -1
  54. package/dist/runtime/server/api/nuxt-users/auth/google/redirect.get.js +1 -1
  55. package/dist/runtime/server/api/nuxt-users/confirm-email.get.js +3 -3
  56. package/dist/runtime/server/api/nuxt-users/inactive.get.js +1 -2
  57. package/dist/runtime/server/api/nuxt-users/index.get.js +1 -2
  58. package/dist/runtime/server/api/nuxt-users/index.post.js +1 -1
  59. package/dist/runtime/server/api/nuxt-users/me.get.js +20 -13
  60. package/dist/runtime/server/api/nuxt-users/me.patch.js +1 -1
  61. package/dist/runtime/server/api/nuxt-users/password/forgot.post.js +2 -1
  62. package/dist/runtime/server/api/nuxt-users/password/index.patch.js +1 -1
  63. package/dist/runtime/server/api/nuxt-users/password/reset.post.d.ts +1 -1
  64. package/dist/runtime/server/api/nuxt-users/password/reset.post.js +4 -2
  65. package/dist/runtime/server/api/nuxt-users/session/index.delete.js +1 -1
  66. package/dist/runtime/server/api/nuxt-users/session/index.post.js +5 -3
  67. package/dist/runtime/server/composables/useServerAuth.js +1 -1
  68. package/dist/runtime/server/internal/index.d.ts +1 -7
  69. package/dist/runtime/server/internal/index.js +23 -7
  70. package/dist/runtime/server/middleware/authorization.server.js +22 -5
  71. package/dist/runtime/server/tasks/cleanup-tokens.d.ts +1 -1
  72. package/dist/runtime/server/utils/add-indexes.d.ts +3 -0
  73. package/dist/runtime/server/utils/add-indexes.js +17 -0
  74. package/dist/runtime/server/utils/google-oauth.js +2 -1
  75. package/dist/runtime/server/utils/index.d.ts +2 -2
  76. package/dist/runtime/server/utils/index.js +2 -2
  77. package/dist/runtime/server/utils/migrate.d.ts +6 -0
  78. package/dist/runtime/server/utils/migrate.js +10 -1
  79. package/dist/runtime/utils/deep-merge.d.ts +8 -0
  80. package/dist/runtime/utils/deep-merge.js +17 -0
  81. package/dist/runtime/utils/locale.d.ts +14 -0
  82. package/dist/runtime/utils/locale.js +38 -0
  83. package/dist/shared/{nuxt-users.BnOQs2LI.mjs → nuxt-users.DfZ7bc_P.mjs} +16 -8
  84. package/dist/shared/{nuxt-users.SXaMnaEL.d.mts → nuxt-users.XW6DYM7t.d.mts} +46 -2
  85. package/dist/shared/{nuxt-users.SXaMnaEL.d.ts → nuxt-users.XW6DYM7t.d.ts} +46 -2
  86. package/dist/utils.d.mts +4 -1
  87. package/dist/utils.d.ts +4 -1
  88. package/dist/utils.mjs +4 -1
  89. package/package.json +11 -6
  90. package/scripts/check-locale-consistency.ts +156 -0
  91. package/scripts/check-peer-dependencies.js +5 -5
  92. package/scripts/check-translations.ts +255 -0
  93. package/scripts/post-build.js +1 -1
  94. package/scripts/test-nuxt3.sh +78 -0
@@ -0,0 +1,156 @@
1
+ #!/usr/bin/env node
2
+ import { readFileSync } from 'node:fs'
3
+ import { join, dirname } from 'node:path'
4
+ import { fileURLToPath } from 'node:url'
5
+
6
+ const __filename = fileURLToPath(import.meta.url)
7
+ const __dirname = dirname(__filename)
8
+ const rootDir = join(__dirname, '..')
9
+
10
+ /**
11
+ * Get all translation keys from the Hungarian locale file
12
+ */
13
+ const getHungarianKeys = (): { base: Set<string>, informal: Set<string>, formal: Set<string> } => {
14
+ const huFilePath = join(rootDir, 'src/runtime/locales/hu.ts')
15
+ const content = readFileSync(huFilePath, 'utf8')
16
+
17
+ // Extract huBase object
18
+ const baseMatch = content.match(/const huBase: LocaleMessages = \{([\s\S]*?)\n\}/)
19
+ if (!baseMatch) {
20
+ throw new Error('Could not find huBase in hu.ts')
21
+ }
22
+
23
+ // Extract huInformalOverrides object
24
+ const informalMatch = content.match(/const huInformalOverrides: LocaleMessages = \{([\s\S]*?)\n\}/)
25
+ if (!informalMatch) {
26
+ throw new Error('Could not find huInformalOverrides in hu.ts')
27
+ }
28
+
29
+ // Extract huFormalOverrides object
30
+ const formalMatch = content.match(/const huFormalOverrides: LocaleMessages = \{([\s\S]*?)\n\}/)
31
+ if (!formalMatch) {
32
+ throw new Error('Could not find huFormalOverrides in hu.ts')
33
+ }
34
+
35
+ // Parse the objects (simple parsing for nested structures)
36
+ const parseKeys = (objString: string): Set<string> => {
37
+ const keys = new Set<string>()
38
+ const lines = objString.split('\n')
39
+ const stack: string[] = []
40
+
41
+ for (const line of lines) {
42
+ const trimmed = line.trim()
43
+
44
+ // Skip empty lines and comments
45
+ if (!trimmed || trimmed.startsWith('//')) {
46
+ continue
47
+ }
48
+
49
+ // Check for opening brace (nested object)
50
+ if (trimmed.match(/^(\w+):\s*\{/)) {
51
+ const match = trimmed.match(/^(\w+):\s*\{/)
52
+ if (match && match[1]) {
53
+ stack.push(match[1])
54
+ }
55
+ continue
56
+ }
57
+
58
+ // Check for closing brace
59
+ if (trimmed === '},') {
60
+ stack.pop()
61
+ continue
62
+ }
63
+
64
+ // Check for key-value pair
65
+ if (trimmed.match(/^(\w+):/)) {
66
+ const match = trimmed.match(/^(\w+):/)
67
+ if (match) {
68
+ const key = match[1]
69
+ const fullKey = stack.length > 0 ? `${stack.join('.')}.${key}` : key
70
+ if (fullKey) {
71
+ keys.add(fullKey)
72
+ }
73
+ }
74
+ }
75
+ }
76
+
77
+ return keys
78
+ }
79
+
80
+ const baseKeys = parseKeys(baseMatch[1] || '')
81
+ const informalKeys = parseKeys(informalMatch[1] || '')
82
+ const formalKeys = parseKeys(formalMatch[1] || '')
83
+
84
+ return { base: baseKeys, informal: informalKeys, formal: formalKeys }
85
+ }
86
+
87
+ /**
88
+ * Check for consistency issues
89
+ */
90
+ const checkConsistency = () => {
91
+ console.log('[Nuxt Users] 🔍 Checking Hungarian locale formal/informal consistency...\n')
92
+
93
+ const { base, informal, formal } = getHungarianKeys()
94
+
95
+ console.log('[Nuxt Users] 📊 Statistics:')
96
+ console.log(` Base keys: ${base.size}`)
97
+ console.log(` Informal override keys: ${informal.size}`)
98
+ console.log(` Formal override keys: ${formal.size}`)
99
+ console.log()
100
+
101
+ // Check 1: Formal keys that don't exist in informal
102
+ const formalOnlyKeys: string[] = []
103
+ for (const key of formal) {
104
+ if (!informal.has(key)) {
105
+ formalOnlyKeys.push(key)
106
+ }
107
+ }
108
+
109
+ // Check 2: Informal keys that don't exist in formal
110
+ const informalOnlyKeys: string[] = []
111
+ for (const key of informal) {
112
+ if (!formal.has(key)) {
113
+ informalOnlyKeys.push(key)
114
+ }
115
+ }
116
+
117
+ let hasErrors = false
118
+
119
+ if (formalOnlyKeys.length > 0) {
120
+ hasErrors = true
121
+ console.log('❌ Formal keys without informal equivalent:\n')
122
+ for (const key of formalOnlyKeys.sort()) {
123
+ console.log(` - ${key}`)
124
+ }
125
+ console.log()
126
+ }
127
+
128
+ if (informalOnlyKeys.length > 0) {
129
+ hasErrors = true
130
+ console.log('❌ Informal keys without formal equivalent:\n')
131
+ for (const key of informalOnlyKeys.sort()) {
132
+ console.log(` - ${key}`)
133
+ }
134
+ console.log()
135
+ }
136
+
137
+ if (hasErrors) {
138
+ console.log('[Nuxt Users] ⚠️ Inconsistencies found!')
139
+ console.log('[Nuxt Users] 💡 Tip: All formal variants must have an informal equivalent and vice versa.')
140
+ console.log('[Nuxt Users] This ensures consistent translations across both Hungarian variants.')
141
+ console.log()
142
+ process.exit(1)
143
+ }
144
+
145
+ console.log('[Nuxt Users] ✅ All formal/informal translations are consistent!')
146
+ console.log()
147
+ }
148
+
149
+ // Main execution
150
+ try {
151
+ checkConsistency()
152
+ }
153
+ catch (error) {
154
+ console.error('[Nuxt Users] ❌ Error:', error instanceof Error ? error.message : 'Unknown error')
155
+ process.exit(1)
156
+ }
@@ -58,16 +58,16 @@ function getPackageManagerCommand() {
58
58
  if (existsSync(join(cwd, 'pnpm-lock.yaml'))) {
59
59
  return 'pnpm add'
60
60
  }
61
- else if (existsSync(join(cwd, 'yarn.lock'))) {
61
+ if (existsSync(join(cwd, 'yarn.lock'))) {
62
62
  return 'yarn add'
63
63
  }
64
- else if (existsSync(join(cwd, 'package-lock.json'))) {
64
+ if (existsSync(join(cwd, 'package-lock.json'))) {
65
65
  return 'npm install'
66
66
  }
67
- else if (process.env.npm_config_user_agent?.includes('pnpm')) {
67
+ if (process.env.npm_config_user_agent?.includes('pnpm')) {
68
68
  return 'pnpm add'
69
69
  }
70
- else if (process.env.npm_config_user_agent?.includes('yarn')) {
70
+ if (process.env.npm_config_user_agent?.includes('yarn')) {
71
71
  return 'yarn add'
72
72
  }
73
73
 
@@ -123,7 +123,7 @@ function displayResults({ missing, optional, packageName }) {
123
123
  if (missing.length === 0 && optional.length === 0) {
124
124
  console.log(`✅ nuxt-users: All dependencies found in ${packageName}!`)
125
125
  }
126
- else if (missing.length === 0) {
126
+ if (missing.length === 0) {
127
127
  console.log(`✅ nuxt-users: All required dependencies found in ${packageName}!`)
128
128
  }
129
129
  }
@@ -0,0 +1,255 @@
1
+ #!/usr/bin/env node
2
+ import { readFileSync, readdirSync, statSync } from 'node:fs'
3
+ import { join, relative, dirname } from 'node:path'
4
+ import { fileURLToPath } from 'node:url'
5
+
6
+ const __filename = fileURLToPath(import.meta.url)
7
+ const __dirname = dirname(__filename)
8
+ const rootDir = join(__dirname, '..')
9
+
10
+ interface StringMatch {
11
+ file: string
12
+ line: number
13
+ column: number
14
+ content: string
15
+ context: string
16
+ }
17
+
18
+ /**
19
+ * Recursively find all Vue files in a directory
20
+ */
21
+ const findVueFiles = (dir: string, fileList: string[] = []): string[] => {
22
+ const files = readdirSync(dir)
23
+
24
+ for (const file of files) {
25
+ const filePath = join(dir, file)
26
+ const stat = statSync(filePath)
27
+
28
+ if (stat.isDirectory()) {
29
+ findVueFiles(filePath, fileList)
30
+ continue
31
+ }
32
+
33
+ if (file.endsWith('.vue')) {
34
+ fileList.push(filePath)
35
+ }
36
+ }
37
+
38
+ return fileList
39
+ }
40
+
41
+ /**
42
+ * Check if a string should be ignored (technical strings, not user-facing)
43
+ */
44
+ const shouldIgnoreString = (str: string): boolean => {
45
+ const ignoredPatterns = [
46
+ /^\s*$/, // Empty or whitespace only
47
+ /^[a-z-]+$/, // CSS classes, HTML tags, etc. (lowercase with dashes)
48
+ /^\d+$/, // Numbers only
49
+ /^\*+$/, // Asterisks for password placeholders
50
+ /^[./#@]+$/, // Special chars only
51
+ /^\$/, // Vue/JS variables
52
+ /^v-/, // Vue directives
53
+ /^:/, // Vue bindings
54
+ /^@/, // Vue events
55
+ /^#/, // Template refs
56
+ /^%/, // Placeholders
57
+ /^\{/, // Object/template literals
58
+ /^\[/, // Arrays
59
+ /^\//, // Paths/URLs start
60
+ /^https?:\/\//, // URLs
61
+ /^\.\.?\//, // Relative paths
62
+ /^data-/, // Data attributes
63
+ /^aria-/, // ARIA attributes
64
+ /^role$/i, // ARIA role
65
+ /^id$/i, // HTML id
66
+ /^class$/i, // HTML class
67
+ /^src$/i, // HTML src
68
+ /^alt$/i, // HTML alt
69
+ /^type$/i, // HTML type
70
+ /^name$/i, // HTML name
71
+ /^for$/i, // HTML for
72
+ /^t$/i, // Translation function itself
73
+ /^useNuxtUsersLocale$/i, // Composable name
74
+ ]
75
+
76
+ return ignoredPatterns.some(pattern => pattern.test(str.trim()))
77
+ }
78
+
79
+ /**
80
+ * Extract template content from Vue file
81
+ */
82
+ const extractTemplate = (content: string): string | null => {
83
+ const templateMatch = content.match(/<template>([\s\S]*?)<\/template>/)
84
+ return templateMatch ? templateMatch[1] || null : null
85
+ }
86
+
87
+ /**
88
+ * Find hardcoded strings in template
89
+ */
90
+ const findHardcodedStrings = (template: string, filePath: string): StringMatch[] => {
91
+ const matches: StringMatch[] = []
92
+ const lines = template.split('\n')
93
+
94
+ for (let i = 0; i < lines.length; i++) {
95
+ const line = lines[i]
96
+ if (!line) {
97
+ continue
98
+ }
99
+ const lineNum = i + 1
100
+
101
+ // Skip lines that already use t() function or have Vue directives/bindings
102
+ if (line.includes('t(') || line.includes('{{ t(') || line.includes('v-if')
103
+ || line.includes('v-else') || line.includes('v-for') || line.includes('v-show')
104
+ || line.includes('@click') || line.includes('@') || line.includes('viewBox')
105
+ || line.includes('<path') || line.includes('<svg')) {
106
+ continue
107
+ }
108
+
109
+ // Find strings in template interpolations {{ "string" }} or {{ 'string' }}
110
+ const interpolationRegex = /\{\{\s*["']([^"']+)["']\s*\}\}/g
111
+ let match
112
+ while ((match = interpolationRegex.exec(line)) !== null) {
113
+ const str = match[1]
114
+ if (str && !shouldIgnoreString(str)) {
115
+ matches.push({
116
+ file: filePath,
117
+ line: lineNum,
118
+ column: match.index + 1,
119
+ content: str,
120
+ context: line.trim()
121
+ })
122
+ }
123
+ }
124
+
125
+ // Find strings in attributes (but not v-bind:, :, v-on:, @)
126
+ // Match patterns like: attribute="string" but not :attribute="string" or @event="handler"
127
+ // Skip this check entirely for lines with : prefix (Vue bindings)
128
+ if (line.includes(':')) {
129
+ continue
130
+ }
131
+ const attrRegex = /(\w+)=["']([^"']+)["']/g
132
+ while ((match = attrRegex.exec(line)) !== null) {
133
+ const attrName = match[1]
134
+ const attrValue = match[2]
135
+
136
+ // Skip certain attributes that are typically technical
137
+ const technicalAttrs = ['id', 'class', 'data-', 'aria-', 'role', 'type', 'name', 'for', 'src', 'href', 'alt',
138
+ 'viewBox', 'd', 'stroke', 'fill', 'xmlns', 'width', 'height', 'to', 'method']
139
+ const isTechnical = technicalAttrs.some(attr => attrName && attrName.startsWith(attr))
140
+
141
+ // Skip if value looks like a variable name, function call, or expression
142
+ if (/^[a-z][a-zA-Z0-9]*$/.test(attrValue || '') || attrValue?.includes('(') || attrValue?.includes('.')) {
143
+ continue
144
+ }
145
+
146
+ if (!isTechnical && attrValue && !shouldIgnoreString(attrValue) && attrValue.length > 1) {
147
+ matches.push({
148
+ file: filePath,
149
+ line: lineNum,
150
+ column: match.index + 1,
151
+ content: attrValue,
152
+ context: line.trim()
153
+ })
154
+ }
155
+ }
156
+
157
+ // Find text content between tags (but not inside {{ }})
158
+ // This is a simplified check - a full parser would be better but more complex
159
+ const textContentRegex = />([^<{]+)</g
160
+ while ((match = textContentRegex.exec(line)) !== null) {
161
+ const text = match[1]?.trim()
162
+
163
+ // Check if it's actual text content (contains letters and is meaningful)
164
+ if (text && /[a-z]{2,}/i.test(text) && !shouldIgnoreString(text)) {
165
+ // Skip if it's inside a comment
166
+ if (!line.includes('<!--') || line.indexOf('-->') < match.index) {
167
+ matches.push({
168
+ file: filePath,
169
+ line: lineNum,
170
+ column: match.index + 1,
171
+ content: text,
172
+ context: line.trim()
173
+ })
174
+ }
175
+ }
176
+ }
177
+ }
178
+
179
+ return matches
180
+ }
181
+
182
+ // Main execution
183
+ const main = () => {
184
+ try {
185
+ const args = process.argv.slice(2)
186
+ const pathArg = args.find(arg => arg.startsWith('--path='))
187
+ const scanPathRelative = pathArg ? pathArg.split('=')[1] : 'src/runtime/components'
188
+ const scanPath = join(rootDir, scanPathRelative || '')
189
+
190
+ console.log(`[Nuxt Users] 🔍 Scanning Vue files in: ${scanPath}`)
191
+ console.log()
192
+
193
+ const vueFiles = findVueFiles(scanPath)
194
+
195
+ if (vueFiles.length === 0) {
196
+ console.log('[Nuxt Users] ⚠️ No Vue files found in the specified path')
197
+ return
198
+ }
199
+
200
+ console.log(`[Nuxt Users] 📄 Found ${vueFiles.length} Vue file(s)`)
201
+ console.log()
202
+
203
+ let totalMatches = 0
204
+ const fileResults: Record<string, StringMatch[]> = {}
205
+
206
+ for (const filePath of vueFiles) {
207
+ const content = readFileSync(filePath, 'utf8')
208
+ const template = extractTemplate(content)
209
+
210
+ if (!template) {
211
+ continue
212
+ }
213
+
214
+ const matches = findHardcodedStrings(template, filePath)
215
+
216
+ if (matches.length > 0) {
217
+ const relativePath = relative(rootDir, filePath)
218
+ fileResults[relativePath] = matches
219
+ totalMatches += matches.length
220
+ }
221
+ }
222
+
223
+ if (totalMatches === 0) {
224
+ console.log('[Nuxt Users] ✅ No hardcoded strings found! All strings appear to use t()')
225
+ console.log()
226
+ return
227
+ }
228
+
229
+ console.log(`[Nuxt Users] ⚠️ Found ${totalMatches} potential hardcoded string(s):\n`)
230
+
231
+ for (const [file, matches] of Object.entries(fileResults)) {
232
+ console.log(`📁 ${file}`)
233
+
234
+ for (const match of matches) {
235
+ console.log(` Line ${match.line}:${match.column}`)
236
+ console.log(` String: "${match.content}"`)
237
+ console.log(` Context: ${match.context}`)
238
+ console.log()
239
+ }
240
+ }
241
+
242
+ console.log('[Nuxt Users] 💡 Tip: Replace hardcoded strings with t() function from useNuxtUsersLocale()')
243
+ console.log('[Nuxt Users] Example: "Login" → {{ t(\'login.submit\') }}')
244
+ console.log()
245
+
246
+ // Exit with error code if hardcoded strings found
247
+ process.exit(1)
248
+ }
249
+ catch (error) {
250
+ console.error('[Nuxt Users] ❌ Error:', error instanceof Error ? error.message : 'Unknown error')
251
+ process.exit(1)
252
+ }
253
+ }
254
+
255
+ main()
@@ -44,7 +44,7 @@ function fixImportPaths(dir) {
44
44
  if (item.isDirectory()) {
45
45
  fixImportPaths(fullPath)
46
46
  }
47
- else if (item.isFile() && (item.name.endsWith('.js') || item.name.endsWith('.mjs') || item.name.endsWith('.vue') || item.name.endsWith('.ts'))) {
47
+ if (item.isFile() && (item.name.endsWith('.js') || item.name.endsWith('.mjs') || item.name.endsWith('.vue') || item.name.endsWith('.ts'))) {
48
48
  let content = readFileSync(fullPath, 'utf8')
49
49
  let modified = false
50
50
 
@@ -0,0 +1,78 @@
1
+ #!/bin/bash
2
+
3
+ # Nuxt 3 Local Test Runner
4
+ # This script temporarily installs Nuxt 3, runs the specified test,
5
+ # and then restores the original Nuxt 4 dependencies.
6
+
7
+ set -e
8
+
9
+ # --- Configuration ---
10
+ NUXT3_VERSION="^3.17.6"
11
+ TEST_TYPE=$1
12
+
13
+ # --- Colors for output ---
14
+ RED='\033[0;31m'
15
+ GREEN='\033[0;32m'
16
+ BLUE='\033[0;34m'
17
+ NC='\033[0m'
18
+
19
+ # --- Helper functions ---
20
+ print_status() {
21
+ echo -e "${BLUE}$1${NC}"
22
+ }
23
+
24
+ print_error() {
25
+ echo -e "${RED}❌ $1${NC}"
26
+ }
27
+
28
+ print_success() {
29
+ echo -e "${GREEN}✅ $1${NC}"
30
+ }
31
+
32
+ # --- Cleanup function ---
33
+ cleanup() {
34
+ print_status "🧹 Restoring original dependencies..."
35
+ if [ -f "package.json.bak" ]; then
36
+ mv package.json.bak package.json
37
+ fi
38
+ if [ -f "yarn.lock.bak" ]; then
39
+ mv yarn.lock.bak yarn.lock
40
+ fi
41
+ npx nypm@latest i
42
+ print_success "Dependencies restored to Nuxt 4."
43
+ }
44
+
45
+ # --- Main script ---
46
+
47
+ # 1. Validate input
48
+ if [ -z "$TEST_TYPE" ]; then
49
+ print_error "Usage: ./scripts/test-nuxt3.sh [sqlite|mysql|postgresql|unit|integration]"
50
+ exit 1
51
+ fi
52
+
53
+ # 2. Set up cleanup trap
54
+ trap cleanup EXIT
55
+
56
+ # 3. Backup original package files
57
+ print_status "📦 Backing up package files..."
58
+ cp package.json package.json.bak
59
+ cp yarn.lock yarn.lock.bak
60
+
61
+ # 4. Install Nuxt 3
62
+ print_status "🚀 Installing Nuxt v3 (version: ${NUXT3_VERSION})..."
63
+ npx nypm@latest i nuxt@${NUXT3_VERSION}
64
+ print_success "Nuxt 3 installed."
65
+
66
+ # 5. Run the specified test
67
+ print_status "🧪 Running tests for '$TEST_TYPE' against Nuxt 3..."
68
+ if [ -f "scripts/test-${TEST_TYPE}.sh" ]; then
69
+ "./scripts/test-${TEST_TYPE}.sh"
70
+ else
71
+ print_error "Invalid test type '$TEST_TYPE'. No script found at scripts/test-${TEST_TYPE}.sh"
72
+ exit 1
73
+ fi
74
+
75
+ print_success "Nuxt 3 tests completed successfully!"
76
+
77
+ # 6. Cleanup will be handled by the trap
78
+ exit 0