nuxt-users 1.37.3 → 1.39.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/README.md +5 -1
- package/dist/cli.mjs +10 -6
- package/dist/module.d.mts +1 -1
- package/dist/module.d.ts +1 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +16 -3
- package/dist/runtime/components/NUsersEmailConfirmation.d.vue.ts +5 -3
- package/dist/runtime/components/NUsersEmailConfirmation.vue +22 -15
- package/dist/runtime/components/NUsersEmailConfirmation.vue.d.ts +5 -3
- package/dist/runtime/components/NUsersGoogleLoginButton.d.vue.ts +4 -2
- package/dist/runtime/components/NUsersGoogleLoginButton.vue +5 -2
- package/dist/runtime/components/NUsersGoogleLoginButton.vue.d.ts +4 -2
- package/dist/runtime/components/NUsersList.vue +25 -11
- package/dist/runtime/components/NUsersLoginForm.d.vue.ts +13 -2
- package/dist/runtime/components/NUsersLoginForm.vue +35 -13
- package/dist/runtime/components/NUsersLoginForm.vue.d.ts +13 -2
- package/dist/runtime/components/NUsersLogoutLink.d.vue.ts +3 -4
- package/dist/runtime/components/NUsersLogoutLink.vue +8 -5
- package/dist/runtime/components/NUsersLogoutLink.vue.d.ts +3 -4
- package/dist/runtime/components/NUsersPasswordStrengthIndicator.d.vue.ts +6 -0
- package/dist/runtime/components/NUsersPasswordStrengthIndicator.vue +17 -9
- package/dist/runtime/components/NUsersPasswordStrengthIndicator.vue.d.ts +6 -0
- package/dist/runtime/components/NUsersProfileInfo.d.vue.ts +13 -1
- package/dist/runtime/components/NUsersProfileInfo.vue +23 -9
- package/dist/runtime/components/NUsersProfileInfo.vue.d.ts +13 -1
- package/dist/runtime/components/NUsersRegisterForm.d.vue.ts +26 -11
- package/dist/runtime/components/NUsersRegisterForm.vue +34 -16
- package/dist/runtime/components/NUsersRegisterForm.vue.d.ts +26 -11
- package/dist/runtime/components/NUsersResetPasswordForm.d.vue.ts +11 -2
- package/dist/runtime/components/NUsersResetPasswordForm.vue +25 -12
- package/dist/runtime/components/NUsersResetPasswordForm.vue.d.ts +11 -2
- package/dist/runtime/components/NUsersUserCard.d.vue.ts +2 -0
- package/dist/runtime/components/NUsersUserCard.vue +8 -4
- package/dist/runtime/components/NUsersUserCard.vue.d.ts +2 -0
- package/dist/runtime/components/NUsersUserForm.d.vue.ts +2 -2
- package/dist/runtime/components/NUsersUserForm.vue +5 -4
- package/dist/runtime/components/NUsersUserForm.vue.d.ts +2 -2
- package/dist/runtime/composables/useNuxtUsersLocale.d.ts +15 -0
- package/dist/runtime/composables/useNuxtUsersLocale.js +27 -0
- package/dist/runtime/composables/usePasswordValidation.d.ts +1 -1
- package/dist/runtime/composables/usePublicPaths.js +2 -1
- package/dist/runtime/locales/en.d.ts +2 -0
- package/dist/runtime/locales/en.js +130 -0
- package/dist/runtime/locales/hu.d.ts +3 -0
- package/dist/runtime/locales/hu.js +188 -0
- package/dist/runtime/locales/index.d.ts +5 -0
- package/dist/runtime/locales/index.js +8 -0
- package/dist/runtime/middleware/authorization.client.js +4 -1
- package/dist/runtime/plugin.auth-init.d.ts +9 -0
- package/dist/runtime/plugin.auth-init.js +16 -0
- package/dist/runtime/server/api/nuxt-users/confirm-email.get.js +3 -3
- package/dist/runtime/server/api/nuxt-users/password/forgot.post.js +2 -1
- package/dist/runtime/server/api/nuxt-users/password/reset.post.d.ts +1 -1
- package/dist/runtime/server/api/nuxt-users/password/reset.post.js +4 -2
- package/dist/runtime/server/api/nuxt-users/session/index.post.js +4 -2
- package/dist/runtime/server/middleware/authorization.server.js +6 -3
- package/dist/runtime/server/utils/google-oauth.js +2 -1
- package/dist/runtime/utils/deep-merge.d.ts +8 -0
- package/dist/runtime/utils/deep-merge.js +17 -0
- package/dist/runtime/utils/locale.d.ts +14 -0
- package/dist/runtime/utils/locale.js +38 -0
- package/dist/shared/{nuxt-users.BnOQs2LI.mjs → nuxt-users.DfZ7bc_P.mjs} +16 -8
- package/dist/shared/{nuxt-users.SXaMnaEL.d.ts → nuxt-users.FiQjvlZl.d.mts} +40 -2
- package/dist/shared/{nuxt-users.SXaMnaEL.d.mts → nuxt-users.FiQjvlZl.d.ts} +40 -2
- package/dist/utils.d.mts +4 -1
- package/dist/utils.d.ts +4 -1
- package/dist/utils.mjs +4 -1
- package/package.json +5 -3
- package/scripts/check-locale-consistency.ts +156 -0
- package/scripts/check-peer-dependencies.js +5 -5
- package/scripts/check-translations.ts +255 -0
- package/scripts/post-build.js +1 -1
- package/dist/runtime/plugin.client.d.ts +0 -2
- package/dist/runtime/plugin.client.js +0 -6
package/dist/utils.d.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { defaultLocaleMessages, en, hu, huFormal } from '../dist/runtime/locales/index.js';
|
|
2
|
+
export { getNestedValue, getTranslation } from '../dist/runtime/utils/locale.js';
|
|
3
|
+
export { deepMerge } from '../dist/runtime/utils/deep-merge.js';
|
|
4
|
+
export { g as DatabaseConfig, f as DatabaseType, D as DisplayFieldsProps, G as GoogleOAuthOptions, i as LocaleMessages, h as LocaleOptions, L as LoginFormData, c as LoginFormProps, M as ModuleOptions, b as PasswordResetToken, e as Permission, P as PersonalAccessToken, d as ResetPasswordFormProps, R as RuntimeModuleOptions, U as User, a as UserWithoutPassword, j as defaultDisplayFields, k as defaultFieldLabels } from './shared/nuxt-users.FiQjvlZl.js';
|
|
2
5
|
|
|
3
6
|
interface PasswordValidationResult {
|
|
4
7
|
isValid: boolean;
|
package/dist/utils.mjs
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
export { a as getPasswordStrengthColor, b as getPasswordStrengthText, g as getPasswordValidationOptions, v as validatePassword } from './shared/nuxt-users.
|
|
1
|
+
export { a as getPasswordStrengthColor, b as getPasswordStrengthText, g as getPasswordValidationOptions, v as validatePassword } from './shared/nuxt-users.DfZ7bc_P.mjs';
|
|
2
|
+
export { defaultLocaleMessages, en, hu, huFormal } from '../dist/runtime/locales/index.js';
|
|
3
|
+
export { getNestedValue, getTranslation } from '../dist/runtime/utils/locale.js';
|
|
4
|
+
export { deepMerge } from '../dist/runtime/utils/deep-merge.js';
|
|
2
5
|
|
|
3
6
|
const defaultDisplayFields = ["id", "name", "email", "role", "active", "created_at", "updated_at", "last_login_at"];
|
|
4
7
|
const defaultFieldLabels = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nuxt-users",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.39.0",
|
|
4
4
|
"description": "A comprehensive user management module for Nuxt 3 and Nuxt 4 applications with authentication, authorization, database support, and CLI tools",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"nuxt",
|
|
@@ -62,8 +62,10 @@
|
|
|
62
62
|
"dev:prepare": "yarn nuxt-module-build build --stub && yarn nuxt-module-build prepare && nuxi prepare playground",
|
|
63
63
|
"build": "nuxt-module-build build",
|
|
64
64
|
"postbuild": "node scripts/post-build.js",
|
|
65
|
+
"check:translations": "tsx scripts/check-translations.ts",
|
|
66
|
+
"check:locale-consistency": "tsx scripts/check-locale-consistency.ts",
|
|
65
67
|
"prerelease": "node -v | grep -q 'v22' || (echo 'Please use Node.js v22 for deployment' && exit 1)",
|
|
66
|
-
"release": "yarn lint && yarn test && yarn prepack && changelogen --release &&
|
|
68
|
+
"release": "yarn lint && yarn check:translations && yarn check:locale-consistency && yarn test && yarn prepack && changelogen --release && npm publish && git push --follow-tags",
|
|
67
69
|
"lint": "eslint .",
|
|
68
70
|
"lint:fix": "eslint . --fix",
|
|
69
71
|
"pretest": "node -v | grep -q 'v22' || (echo 'Please use Node.js v22 for testing' && exit 1)",
|
|
@@ -121,7 +123,7 @@
|
|
|
121
123
|
"@nuxt/test-utils": "^3.19.2",
|
|
122
124
|
"@nuxt/vite-builder": "^4.2.0",
|
|
123
125
|
"@types/bcrypt": "^6.0.0",
|
|
124
|
-
"@types/node": "
|
|
126
|
+
"@types/node": "^25.0.3",
|
|
125
127
|
"@types/nodemailer": "^6.4.17",
|
|
126
128
|
"@types/pg": "^8.15.4",
|
|
127
129
|
"changelogen": "^0.6.2",
|
|
@@ -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
|
-
|
|
61
|
+
if (existsSync(join(cwd, 'yarn.lock'))) {
|
|
62
62
|
return 'yarn add'
|
|
63
63
|
}
|
|
64
|
-
|
|
64
|
+
if (existsSync(join(cwd, 'package-lock.json'))) {
|
|
65
65
|
return 'npm install'
|
|
66
66
|
}
|
|
67
|
-
|
|
67
|
+
if (process.env.npm_config_user_agent?.includes('pnpm')) {
|
|
68
68
|
return 'pnpm add'
|
|
69
69
|
}
|
|
70
|
-
|
|
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
|
-
|
|
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()
|
package/scripts/post-build.js
CHANGED
|
@@ -44,7 +44,7 @@ function fixImportPaths(dir) {
|
|
|
44
44
|
if (item.isDirectory()) {
|
|
45
45
|
fixImportPaths(fullPath)
|
|
46
46
|
}
|
|
47
|
-
|
|
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
|
|