renusify 3.0.2 → 3.1.3
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/components/infinite/index.vue +9 -15
- package/components/table/crud/index.vue +1 -3
- package/directive/animate/index.js +1 -1
- package/index.d.ts +73 -0
- package/package.json +38 -30
- package/plugins/auto-loader.mjs +314 -0
- package/plugins/storage/index.js +151 -22
- package/scripts/generate-types.mjs +83 -0
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
v-scroll="{handler:onScroll,target:target}"
|
|
5
5
|
:style="{'max-height': height,'height': height}"
|
|
6
6
|
:class="{'overflow-div':height}" class="infinite-page-container">
|
|
7
|
-
<transition-group :class="{'flex-column-reverse':isChat}"
|
|
7
|
+
<transition-group v-if="!noItem" :class="{'flex-column-reverse':isChat}"
|
|
8
8
|
:name="isChat?'slide-up':'slide-down'"
|
|
9
9
|
class="row"
|
|
10
10
|
tag="div">
|
|
@@ -16,23 +16,17 @@
|
|
|
16
16
|
</r-col>
|
|
17
17
|
</transition-group>
|
|
18
18
|
</div>
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
<!-- noItem slot for empty contents. Provide noItem, noItemMsg props -->
|
|
20
|
+
<slot :noItem="noItem" :noItemMsg="noItemMsg" name="noItem">
|
|
21
|
+
<div v-if="noItem"
|
|
22
|
+
class="text-center title-2"
|
|
23
|
+
>{{ noItemMsg }}
|
|
24
|
+
</div>
|
|
25
|
+
</slot>
|
|
23
26
|
</r-container>
|
|
24
27
|
</template>
|
|
25
28
|
<script setup>
|
|
26
|
-
import {
|
|
27
|
-
ref,
|
|
28
|
-
computed,
|
|
29
|
-
onMounted,
|
|
30
|
-
onUnmounted,
|
|
31
|
-
onActivated,
|
|
32
|
-
onDeactivated,
|
|
33
|
-
watch,
|
|
34
|
-
inject, nextTick
|
|
35
|
-
} from 'vue'
|
|
29
|
+
import {computed, inject, nextTick, onActivated, onDeactivated, onMounted, onUnmounted, ref, watch} from 'vue'
|
|
36
30
|
|
|
37
31
|
const props = defineProps({
|
|
38
32
|
/**
|
|
@@ -259,7 +259,7 @@
|
|
|
259
259
|
</template>
|
|
260
260
|
|
|
261
261
|
<script setup>
|
|
262
|
-
import {
|
|
262
|
+
import {computed, inject, onMounted, ref, watch} from 'vue'
|
|
263
263
|
import ManageFooter from "./footer.vue";
|
|
264
264
|
import ManageHeader from "./header.vue";
|
|
265
265
|
|
|
@@ -543,8 +543,6 @@ const sortSetup = (item) => {
|
|
|
543
543
|
const ok = () => {
|
|
544
544
|
table.value.startTime = false
|
|
545
545
|
page.value = 1
|
|
546
|
-
sortBy.value = null
|
|
547
|
-
sortType.value = 0
|
|
548
546
|
autoSend.value = false
|
|
549
547
|
showForm.value = false
|
|
550
548
|
refresh()
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// Auto-generated Vue component type declarations
|
|
2
|
+
// Do not edit manually - run `npm run build` to update
|
|
3
|
+
declare module 'vue' {
|
|
4
|
+
export interface GlobalComponents {
|
|
5
|
+
rApp: typeof import('./components/app/index.js')['rApp']
|
|
6
|
+
rAvatar: typeof import('./components/avatar/index.js')['rAvatar']
|
|
7
|
+
rBtn: typeof import('./components/button/index.js')['rBtn']
|
|
8
|
+
rBtnConfirm: typeof import('./components/button/buttonConfirm.js')['rBtnConfirm']
|
|
9
|
+
rBtnGroup: typeof import('./components/button/buttonGroup.js')['rBtnGroup']
|
|
10
|
+
rCalendar: typeof import('./components/calendar/index.js')['rCalendar']
|
|
11
|
+
rCard: typeof import('./components/card/index.js')['rCard']
|
|
12
|
+
rCodeEditor: typeof import('./components/codeEditor/index.js')['rCodeEditor']
|
|
13
|
+
rConfirm: typeof import('./components/confirm/index.js')['rConfirm']
|
|
14
|
+
rContainer: typeof import('./components/container/index.js')['rContainer']
|
|
15
|
+
rRow: typeof import('./components/container/row.js')['rRow']
|
|
16
|
+
rCol: typeof import('./components/container/col.js')['rCol']
|
|
17
|
+
rSpacer: typeof import('./components/container/spacer.js')['rSpacer']
|
|
18
|
+
rDivider: typeof import('./components/container/divider.js')['rDivider']
|
|
19
|
+
rContent: typeof import('./components/content/index.js')['rContent']
|
|
20
|
+
rCropper: typeof import('./components/cropper/index.js')['rCropper']
|
|
21
|
+
rFloat: typeof import('./components/float/index.js')['rFloat']
|
|
22
|
+
rForm: typeof import('./components/form/index.js')['rForm']
|
|
23
|
+
rInput: typeof import('./components/form/input/index.js')['rInput']
|
|
24
|
+
rColorInput: typeof import('./components/form/colorInput/index.js')['rColorInput']
|
|
25
|
+
rDateInput: typeof import('./components/form/dateInput/index.js')['rDateInput']
|
|
26
|
+
rFileInput: typeof import('./components/form/fileInput/index.js')['rFileInput']
|
|
27
|
+
rTelInput: typeof import('./components/form/telInput/index.js')['rTelInput']
|
|
28
|
+
rJsonInput: typeof import('./components/form/jsonInput/index.js')['rJsonInput']
|
|
29
|
+
rTextEditor: typeof import('./components/form/textEditor/index.js')['rTextEditor']
|
|
30
|
+
rTextEditorPreview: typeof import('./components/form/textEditor/preview.js')['rTextEditorPreview']
|
|
31
|
+
rTimeInput: typeof import('./components/form/timeInput/index.js')['rTimeInput']
|
|
32
|
+
rTimeRangeInput: typeof import('./components/form/timeInput/range.js')['rTimeRangeInput']
|
|
33
|
+
rUniqueInput: typeof import('./components/form/uniqueInput/index.js')['rUniqueInput']
|
|
34
|
+
rAddressInput: typeof import('./components/form/addressInput/index.js')['rAddressInput']
|
|
35
|
+
rCamInput: typeof import('./components/form/camInput/index.js')['rCamInput']
|
|
36
|
+
rCheckInput: typeof import('./components/form/checkInput/index.js')['rCheckInput']
|
|
37
|
+
rCheckboxInput: typeof import('./components/form/checkboxInput/index.js')['rCheckboxInput']
|
|
38
|
+
rGroupInput: typeof import('./components/form/groupInput/index.js')['rGroupInput']
|
|
39
|
+
rMaskInput: typeof import('./components/form/maskInput/index.js')['rMaskInput']
|
|
40
|
+
rNumberInput: typeof import('./components/form/numberInput/index.js')['rNumberInput']
|
|
41
|
+
rPasswordInput: typeof import('./components/form/passwordInput/index.js')['rPasswordInput']
|
|
42
|
+
rRadioInput: typeof import('./components/form/radioInput/index.js')['rRadioInput']
|
|
43
|
+
rRangeInput: typeof import('./components/form/rangeInput/index.js')['rRangeInput']
|
|
44
|
+
rRatingInput: typeof import('./components/form/ratingInput/index.js')['rRatingInput']
|
|
45
|
+
rSelectInput: typeof import('./components/form/selectInput/index.js')['rSelectInput']
|
|
46
|
+
rSwitchInput: typeof import('./components/form/switchInput/index.js')['rSwitchInput']
|
|
47
|
+
rTextArea: typeof import('./components/form/textArea/index.js')['rTextArea']
|
|
48
|
+
rTextInput: typeof import('./components/form/textInput/index.js')['rTextInput']
|
|
49
|
+
rUnitInput: typeof import('./components/form/unitInput/index.js')['rUnitInput']
|
|
50
|
+
rFormCreator: typeof import('./components/formCreator/index.js')['rFormCreator']
|
|
51
|
+
rIcon: typeof import('./components/icon/index.js')['rIcon']
|
|
52
|
+
rImg: typeof import('./components/img/index.js')['rImg']
|
|
53
|
+
rInfinite: typeof import('./components/infinite/index.js')['rInfinite']
|
|
54
|
+
rMap: typeof import('./components/map/index.js')['rMap']
|
|
55
|
+
rMapSelect: typeof import('./components/map/select.js')['rMapSelect']
|
|
56
|
+
rMapRoute: typeof import('./components/map/route.js')['rMapRoute']
|
|
57
|
+
rMenu: typeof import('./components/menu/index.js')['rMenu']
|
|
58
|
+
rMeta: typeof import('./components/meta/index.js')['rMeta']
|
|
59
|
+
rModal: typeof import('./components/modal/index.js')['rModal']
|
|
60
|
+
rNotify: typeof import('./components/notify/index.js')['rNotify']
|
|
61
|
+
rProgressCircle: typeof import('./components/progress/circle.js')['rProgressCircle']
|
|
62
|
+
rProgressLine: typeof import('./components/progress/line.js')['rProgressLine']
|
|
63
|
+
rSearchBox: typeof import('./components/searchBox/index.js')['rSearchBox']
|
|
64
|
+
rSlider: typeof import('./components/slider/index.js')['rSlider']
|
|
65
|
+
rSwiper: typeof import('./components/swiper/index.js')['rSwiper']
|
|
66
|
+
rTable: typeof import('./components/table/index.js')['rTable']
|
|
67
|
+
rTableCrud: typeof import('./components/table/crud/index.js')['rTableCrud']
|
|
68
|
+
rTimeAgo: typeof import('./components/timeAgo/index.js')['rTimeAgo']
|
|
69
|
+
rTour: typeof import('./components/tour/index.js')['rTour']
|
|
70
|
+
rTree: typeof import('./components/tree/index.js')['rTree']
|
|
71
|
+
rHighlight: typeof import('./components/highlight/index.js')['rHighlight']
|
|
72
|
+
}
|
|
73
|
+
}
|
package/package.json
CHANGED
|
@@ -1,30 +1,38 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "renusify",
|
|
3
|
-
"version": "3.
|
|
4
|
-
"description": "Vue3 Framework",
|
|
5
|
-
"keywords": [
|
|
6
|
-
"vuejs",
|
|
7
|
-
"vue framework",
|
|
8
|
-
"ui framework",
|
|
9
|
-
"component framework",
|
|
10
|
-
"ui library",
|
|
11
|
-
"component library",
|
|
12
|
-
"material components",
|
|
13
|
-
"renusify"
|
|
14
|
-
],
|
|
15
|
-
"homepage": "https://github.com/smkoBa/renusify",
|
|
16
|
-
"main": "index.js",
|
|
17
|
-
"
|
|
18
|
-
|
|
19
|
-
"
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
"
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "renusify",
|
|
3
|
+
"version": "3.1.3",
|
|
4
|
+
"description": "Vue3 Framework",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"vuejs",
|
|
7
|
+
"vue framework",
|
|
8
|
+
"ui framework",
|
|
9
|
+
"component framework",
|
|
10
|
+
"ui library",
|
|
11
|
+
"component library",
|
|
12
|
+
"material components",
|
|
13
|
+
"renusify"
|
|
14
|
+
],
|
|
15
|
+
"homepage": "https://github.com/smkoBa/renusify",
|
|
16
|
+
"main": "index.js",
|
|
17
|
+
"types": "index.d.ts",
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "https://github.com/smkoBa/renusify.git"
|
|
21
|
+
},
|
|
22
|
+
"author": {
|
|
23
|
+
"name": "Smko Bayazidi",
|
|
24
|
+
"email": "ba.smko@gmail.com"
|
|
25
|
+
},
|
|
26
|
+
"license": "BSD",
|
|
27
|
+
"private": false,
|
|
28
|
+
"scripts": {
|
|
29
|
+
"docs:generate": "node scripts/generate-docs.mjs",
|
|
30
|
+
"build:types": "node scripts/generate-types.mjs",
|
|
31
|
+
"build": "npm run docs:generate && npm run build:types"
|
|
32
|
+
},
|
|
33
|
+
"peerDependencies": {
|
|
34
|
+
"sass": "^1.69.0",
|
|
35
|
+
"vue": "^3.3.0",
|
|
36
|
+
"vue-router": "^4.5.0"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
import fs from 'fs'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
|
|
4
|
+
export default function renusifyAutoPlugin(options = {}) {
|
|
5
|
+
const { prefix = 'r', debug = false } = options
|
|
6
|
+
|
|
7
|
+
const state = {
|
|
8
|
+
usedComponents: new Set(),
|
|
9
|
+
usedDirectives: new Set(),
|
|
10
|
+
componentsMap: new Map(),
|
|
11
|
+
directivesMap: new Map(),
|
|
12
|
+
rootDir: '',
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
function parseExports(filePath, exclude = []) {
|
|
17
|
+
try {
|
|
18
|
+
const content = fs.readFileSync(filePath, 'utf-8')
|
|
19
|
+
const clean = content
|
|
20
|
+
.replace(/\/\*[\s\S]*?\*\//g, '')
|
|
21
|
+
.replace(/\/\/.*$/gm, '')
|
|
22
|
+
|
|
23
|
+
const regex = /export\s+\*\s+as\s+([a-zA-Z_]\w*)\s+from\s+['"]([^'"]+)['"]/g
|
|
24
|
+
const map = new Map()
|
|
25
|
+
let m
|
|
26
|
+
while ((m = regex.exec(clean)) !== null) {
|
|
27
|
+
const [, name, relPath] = m
|
|
28
|
+
if (exclude.includes(name)) continue
|
|
29
|
+
let p = relPath.replace(/^\.\//, '')
|
|
30
|
+
if (!p.match(/\.(js|vue|ts|mjs)$/)) p = `${p}/index.js`
|
|
31
|
+
map.set(name, p)
|
|
32
|
+
}
|
|
33
|
+
return map
|
|
34
|
+
} catch (err) {
|
|
35
|
+
console.warn(`[renusify-auto] Could not parse ${filePath}:`, err.message)
|
|
36
|
+
return new Map()
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
function kebab2camel(s) {
|
|
42
|
+
return s.replace(/-([a-z0-9])/g, (_, c) => c.toUpperCase())
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
function scanFile(filePath) {
|
|
47
|
+
try {
|
|
48
|
+
const content = fs.readFileSync(filePath, 'utf-8')
|
|
49
|
+
const tmplMatch = content.match(/<template(?:\s[^>]*)?>([\s\S]*?)<\/template>/)
|
|
50
|
+
if (!tmplMatch) return
|
|
51
|
+
|
|
52
|
+
const tmpl = tmplMatch[1]
|
|
53
|
+
|
|
54
|
+
const tagRe = /<\s*\/?([a-zA-Z][a-zA-Z0-9-]*)/g
|
|
55
|
+
let m
|
|
56
|
+
while ((m = tagRe.exec(tmpl)) !== null) {
|
|
57
|
+
const tag = m[1]
|
|
58
|
+
if (!tag.startsWith(prefix)) continue
|
|
59
|
+
const camel = kebab2camel(tag)
|
|
60
|
+
if (state.componentsMap.has(camel)) state.usedComponents.add(camel)
|
|
61
|
+
else if (state.componentsMap.has(tag)) state.usedComponents.add(tag)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const dirRe = /\bv-([a-zA-Z][a-zA-Z0-9-]*)/g
|
|
65
|
+
const builtins = new Set([
|
|
66
|
+
'model', 'if', 'else', 'else-if', 'for', 'show', 'bind', 'on',
|
|
67
|
+
'slot', 'text', 'html', 'cloak', 'once', 'memo', 'pre'
|
|
68
|
+
])
|
|
69
|
+
|
|
70
|
+
while ((m = dirRe.exec(tmpl)) !== null) {
|
|
71
|
+
const d = m[1]
|
|
72
|
+
if (builtins.has(d)) continue
|
|
73
|
+
const camel = kebab2camel(d)
|
|
74
|
+
const candidates = [d, camel, `v${camel.charAt(0).toUpperCase()}${camel.slice(1)}`]
|
|
75
|
+
for (const c of candidates) {
|
|
76
|
+
if (state.directivesMap.has(c)) {
|
|
77
|
+
state.usedDirectives.add(c)
|
|
78
|
+
break
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
} catch {
|
|
83
|
+
// ignore
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
function scanDirSync(dir) {
|
|
89
|
+
let entries
|
|
90
|
+
try {
|
|
91
|
+
entries = fs.readdirSync(dir, { withFileTypes: true })
|
|
92
|
+
} catch {
|
|
93
|
+
return
|
|
94
|
+
}
|
|
95
|
+
for (const entry of entries) {
|
|
96
|
+
const full = path.join(dir, entry.name)
|
|
97
|
+
if (entry.isDirectory()) {
|
|
98
|
+
if (['node_modules', 'dist', '.git', '.nuxt', '.output'].includes(entry.name)) continue
|
|
99
|
+
scanDirSync(full)
|
|
100
|
+
} else if (entry.name.endsWith('.vue')) {
|
|
101
|
+
scanFile(full)
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
function generateImportStatements() {
|
|
108
|
+
const comps = [...state.usedComponents].sort()
|
|
109
|
+
const dirs = [...state.usedDirectives].sort()
|
|
110
|
+
|
|
111
|
+
if (comps.length === 0 && dirs.length === 0) return ''
|
|
112
|
+
|
|
113
|
+
let code = '\n'
|
|
114
|
+
if (comps.length > 0) {
|
|
115
|
+
code += `import {\n ${comps.join(',\n ')}\n} from 'renusify/components/index.js'\n`
|
|
116
|
+
}
|
|
117
|
+
if (dirs.length > 0) {
|
|
118
|
+
const dirNames = dirs.map(name => {
|
|
119
|
+
if (name.startsWith('v') || name.startsWith('V')) {
|
|
120
|
+
return name.charAt(1).toLowerCase() + name.slice(2)
|
|
121
|
+
}
|
|
122
|
+
return name
|
|
123
|
+
})
|
|
124
|
+
code += `import { ${dirNames.join(', ')} } from 'renusify/directive/index.js'\n`
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return code
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
function generateComponentsObject() {
|
|
132
|
+
const comps = [...state.usedComponents].sort()
|
|
133
|
+
if (comps.length === 0) return null
|
|
134
|
+
return `components: {\n ${comps.join(',\n ')}\n }`
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
function generateDirectivesObject() {
|
|
139
|
+
const dirs = [...state.usedDirectives].sort()
|
|
140
|
+
if (dirs.length === 0) return null
|
|
141
|
+
const dirNames = dirs.map(name => {
|
|
142
|
+
if (name.startsWith('v') || name.startsWith('V')) {
|
|
143
|
+
return name.charAt(1).toLowerCase() + name.slice(2)
|
|
144
|
+
}
|
|
145
|
+
return name
|
|
146
|
+
})
|
|
147
|
+
return `directives: { ${dirNames.join(', ')} }`
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return {
|
|
151
|
+
name: 'vite-plugin-renusify-auto',
|
|
152
|
+
enforce: 'pre',
|
|
153
|
+
|
|
154
|
+
configResolved(config) {
|
|
155
|
+
state.rootDir = config.root
|
|
156
|
+
const renusifyPath = path.join(config.root, 'node_modules/renusify')
|
|
157
|
+
|
|
158
|
+
state.componentsMap = parseExports(
|
|
159
|
+
path.join(renusifyPath, 'components/index.js'),
|
|
160
|
+
['_register']
|
|
161
|
+
)
|
|
162
|
+
state.directivesMap = parseExports(
|
|
163
|
+
path.join(renusifyPath, 'directive/index.js'),
|
|
164
|
+
['_registers']
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
if (config.command === 'serve') {
|
|
168
|
+
state.usedComponents = new Set(state.componentsMap.keys())
|
|
169
|
+
state.usedDirectives = new Set(state.directivesMap.keys())
|
|
170
|
+
if (debug) {
|
|
171
|
+
console.log(`[renusify-auto] Dev mode: ALL ${state.componentsMap.size} components, ${state.directivesMap.size} directives`)
|
|
172
|
+
}
|
|
173
|
+
} else {
|
|
174
|
+
scanDirSync(path.join(config.root, 'src'))
|
|
175
|
+
if (debug) {
|
|
176
|
+
console.log(`[renusify-auto] Build mode: ${state.usedComponents.size} components, ${state.usedDirectives.size} directives`)
|
|
177
|
+
console.log(' Components:', [...state.usedComponents].join(', '))
|
|
178
|
+
console.log(' Directives:', [...state.usedDirectives].join(', '))
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
|
|
183
|
+
transform(code, id) {
|
|
184
|
+
if (!/main\.(js|ts)$/.test(id)) return null
|
|
185
|
+
if (id.includes('node_modules')) return null
|
|
186
|
+
|
|
187
|
+
let newCode = code
|
|
188
|
+
let modified = false
|
|
189
|
+
|
|
190
|
+
const importStmts = generateImportStatements()
|
|
191
|
+
if (importStmts) {
|
|
192
|
+
const importRegex = /^import\s+[^;]+;?\s*$/gm
|
|
193
|
+
const allImports = code.match(importRegex)
|
|
194
|
+
|
|
195
|
+
if (allImports && allImports.length > 0) {
|
|
196
|
+
const lastImport = allImports[allImports.length - 1]
|
|
197
|
+
const lastImportIndex = code.lastIndexOf(lastImport)
|
|
198
|
+
const insertPos = lastImportIndex + lastImport.length
|
|
199
|
+
newCode = newCode.slice(0, insertPos) + importStmts + newCode.slice(insertPos)
|
|
200
|
+
} else {
|
|
201
|
+
newCode = importStmts + '\n' + newCode
|
|
202
|
+
}
|
|
203
|
+
modified = true
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const renusifyRegex = /\.use\s*\(\s*renusify\s*,\s*\{/
|
|
207
|
+
const useMatch = newCode.match(renusifyRegex)
|
|
208
|
+
|
|
209
|
+
if (useMatch) {
|
|
210
|
+
const matchIndex = useMatch.index
|
|
211
|
+
const startOfOptions = matchIndex + useMatch[0].length
|
|
212
|
+
|
|
213
|
+
let braceCount = 1
|
|
214
|
+
let i = startOfOptions
|
|
215
|
+
while (i < newCode.length && braceCount > 0) {
|
|
216
|
+
if (newCode[i] === '{') braceCount++
|
|
217
|
+
else if (newCode[i] === '}') braceCount--
|
|
218
|
+
if (braceCount > 0) i++
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (braceCount === 0) {
|
|
222
|
+
const endOfOptions = i
|
|
223
|
+
const optionsContent = newCode.slice(startOfOptions, endOfOptions)
|
|
224
|
+
let cleanedOptions = optionsContent
|
|
225
|
+
|
|
226
|
+
cleanedOptions = removeProperty(cleanedOptions, 'components')
|
|
227
|
+
cleanedOptions = removeProperty(cleanedOptions, 'directives')
|
|
228
|
+
|
|
229
|
+
cleanedOptions = cleanedOptions.replace(/,(\s*)}/g, '$1}')
|
|
230
|
+
cleanedOptions = cleanedOptions.trim()
|
|
231
|
+
|
|
232
|
+
const componentsObj = generateComponentsObject()
|
|
233
|
+
const directivesObj = generateDirectivesObject()
|
|
234
|
+
|
|
235
|
+
let newOptionsContent = cleanedOptions
|
|
236
|
+
|
|
237
|
+
if (componentsObj || directivesObj) {
|
|
238
|
+
const needsComma = newOptionsContent.trim().length > 0 && !newOptionsContent.trim().endsWith(',')
|
|
239
|
+
const comma = needsComma ? ',' : ''
|
|
240
|
+
|
|
241
|
+
let additions = ''
|
|
242
|
+
if (componentsObj) additions += '\n ' + componentsObj
|
|
243
|
+
if (directivesObj) additions += (additions ? ',' : '') + '\n ' + directivesObj
|
|
244
|
+
|
|
245
|
+
if (newOptionsContent.trim().length === 0) {
|
|
246
|
+
newOptionsContent = additions + '\n '
|
|
247
|
+
} else {
|
|
248
|
+
newOptionsContent = newOptionsContent + comma + additions + '\n '
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
newCode = newCode.slice(0, startOfOptions) + newOptionsContent + newCode.slice(endOfOptions)
|
|
253
|
+
modified = true
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (modified) {
|
|
258
|
+
if (debug) {
|
|
259
|
+
console.log(`[renusify-auto] Transformed ${path.basename(id)}`)
|
|
260
|
+
}
|
|
261
|
+
return { code: newCode, map: null }
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return null
|
|
265
|
+
},
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function removeProperty(optionsStr, propName) {
|
|
270
|
+
const propRegex = new RegExp(`,?\\s*${propName}\\s*:\\s*`)
|
|
271
|
+
const match = propRegex.exec(optionsStr)
|
|
272
|
+
|
|
273
|
+
if (!match) return optionsStr
|
|
274
|
+
|
|
275
|
+
const startIndex = match.index
|
|
276
|
+
const valueStart = startIndex + match[0].length
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
let endIndex = valueStart
|
|
280
|
+
const firstChar = optionsStr[valueStart]
|
|
281
|
+
|
|
282
|
+
if (firstChar === '{') {
|
|
283
|
+
|
|
284
|
+
let braceCount = 1
|
|
285
|
+
let i = valueStart + 1
|
|
286
|
+
while (i < optionsStr.length && braceCount > 0) {
|
|
287
|
+
if (optionsStr[i] === '{') braceCount++
|
|
288
|
+
else if (optionsStr[i] === '}') braceCount--
|
|
289
|
+
i++
|
|
290
|
+
}
|
|
291
|
+
endIndex = i
|
|
292
|
+
} else if (firstChar === '[') {
|
|
293
|
+
let bracketCount = 1
|
|
294
|
+
let i = valueStart + 1
|
|
295
|
+
while (i < optionsStr.length && bracketCount > 0) {
|
|
296
|
+
if (optionsStr[i] === '[') bracketCount++
|
|
297
|
+
else if (optionsStr[i] === ']') bracketCount--
|
|
298
|
+
i++
|
|
299
|
+
}
|
|
300
|
+
endIndex = i
|
|
301
|
+
} else {
|
|
302
|
+
let i = valueStart
|
|
303
|
+
while (i < optionsStr.length && optionsStr[i] !== ',' && optionsStr[i] !== '}') {
|
|
304
|
+
i++
|
|
305
|
+
}
|
|
306
|
+
endIndex = i
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
if (endIndex < optionsStr.length && optionsStr[endIndex] === ',') {
|
|
310
|
+
endIndex++
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
return optionsStr.slice(0, startIndex) + optionsStr.slice(endIndex)
|
|
314
|
+
}
|
package/plugins/storage/index.js
CHANGED
|
@@ -1,35 +1,164 @@
|
|
|
1
1
|
class Storage {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
constructor(encryptionKey) {
|
|
3
|
+
this._key = encryptionKey || this._loadKeyFromEnv();
|
|
4
|
+
this._available = this._checkAvailability();
|
|
5
|
+
if (!this._key) {
|
|
6
|
+
console.warn('[Storage] ⚠️ No encryption key provided. Safe methods will throw.');
|
|
4
7
|
}
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
_loadKeyFromEnv() {
|
|
11
|
+
if (typeof import.meta !== 'undefined' && import.meta.env?.VITE_STORAGE_KEY)
|
|
12
|
+
return import.meta.env.VITE_STORAGE_KEY;
|
|
13
|
+
if (typeof process !== 'undefined' && process.env?.STORAGE_KEY)
|
|
14
|
+
return process.env.STORAGE_KEY;
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
_checkAvailability() {
|
|
19
|
+
try {
|
|
20
|
+
const test = '__storage_test__';
|
|
21
|
+
localStorage.setItem(test, '1');
|
|
22
|
+
localStorage.removeItem(test);
|
|
23
|
+
return true;
|
|
24
|
+
} catch {
|
|
25
|
+
return false;
|
|
10
26
|
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
_arrayBufferToBase64(buffer) {
|
|
30
|
+
const bytes = new Uint8Array(buffer);
|
|
31
|
+
let binary = '';
|
|
32
|
+
for (let i = 0; i < bytes.byteLength; i++) binary += String.fromCharCode(bytes[i]);
|
|
33
|
+
return btoa(binary);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
_base64ToArrayBuffer(base64) {
|
|
37
|
+
const binary = atob(base64);
|
|
38
|
+
const bytes = new Uint8Array(binary.length);
|
|
39
|
+
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
|
|
40
|
+
return bytes.buffer;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async _deriveKey(password, salt) {
|
|
44
|
+
const enc = new TextEncoder();
|
|
45
|
+
const keyMaterial = await crypto.subtle.importKey(
|
|
46
|
+
'raw', enc.encode(password), 'PBKDF2', false, ['deriveKey']
|
|
47
|
+
);
|
|
48
|
+
return crypto.subtle.deriveKey(
|
|
49
|
+
{name: 'PBKDF2', salt, iterations: 100000, hash: 'SHA-256'},
|
|
50
|
+
keyMaterial,
|
|
51
|
+
{name: 'AES-GCM', length: 256},
|
|
52
|
+
false,
|
|
53
|
+
['encrypt', 'decrypt']
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async _encrypt(plaintext) {
|
|
58
|
+
const salt = crypto.getRandomValues(new Uint8Array(16));
|
|
59
|
+
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
60
|
+
const key = await this._deriveKey(this._key, salt);
|
|
61
|
+
const cipher = await crypto.subtle.encrypt(
|
|
62
|
+
{name: 'AES-GCM', iv}, key, new TextEncoder().encode(plaintext)
|
|
63
|
+
);
|
|
64
|
+
return JSON.stringify({
|
|
65
|
+
salt: this._arrayBufferToBase64(salt),
|
|
66
|
+
iv: this._arrayBufferToBase64(iv),
|
|
67
|
+
data: this._arrayBufferToBase64(cipher)
|
|
68
|
+
});
|
|
69
|
+
}
|
|
11
70
|
|
|
12
|
-
|
|
13
|
-
|
|
71
|
+
async _decrypt(payloadString) {
|
|
72
|
+
const {salt, iv, data} = JSON.parse(payloadString);
|
|
73
|
+
const key = await this._deriveKey(this._key, this._base64ToArrayBuffer(salt));
|
|
74
|
+
const decrypted = await crypto.subtle.decrypt(
|
|
75
|
+
{name: 'AES-GCM', iv: this._base64ToArrayBuffer(iv)},
|
|
76
|
+
key,
|
|
77
|
+
this._base64ToArrayBuffer(data)
|
|
78
|
+
);
|
|
79
|
+
return new TextDecoder().decode(decrypted);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
stringify(value) {
|
|
83
|
+
return JSON.stringify(value);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
parse(value) {
|
|
87
|
+
return JSON.parse(value);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
set(key, value) {
|
|
91
|
+
if (!this._available) return false;
|
|
92
|
+
try {
|
|
93
|
+
localStorage.setItem(String(key), this.stringify(value));
|
|
94
|
+
return true;
|
|
95
|
+
} catch (err) {
|
|
96
|
+
console.error(`[Storage] set failed:`, err);
|
|
97
|
+
return false;
|
|
14
98
|
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
has(key) {
|
|
102
|
+
if (!this._available) return false;
|
|
103
|
+
return localStorage.getItem(String(key)) !== null;
|
|
104
|
+
}
|
|
15
105
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
106
|
+
get(key, def = null) {
|
|
107
|
+
if (!this._available) return def;
|
|
108
|
+
const raw = localStorage.getItem(String(key));
|
|
109
|
+
if (raw === null) return def;
|
|
110
|
+
try {
|
|
111
|
+
return this.parse(raw);
|
|
112
|
+
} catch {
|
|
113
|
+
return def;
|
|
22
114
|
}
|
|
115
|
+
}
|
|
23
116
|
|
|
24
|
-
|
|
25
|
-
|
|
117
|
+
remove(key) {
|
|
118
|
+
if (!this._available) return false;
|
|
119
|
+
localStorage.removeItem(String(key));
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
pull(key, def = null) {
|
|
124
|
+
const data = this.get(key, def);
|
|
125
|
+
this.remove(key);
|
|
126
|
+
return data;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async setSafe(key, value) {
|
|
130
|
+
if (!this._key) throw new Error('[Storage] Encryption key not configured.');
|
|
131
|
+
if (!this._available) return false;
|
|
132
|
+
try {
|
|
133
|
+
const plaintext = this.stringify(value);
|
|
134
|
+
const encrypted = await this._encrypt(plaintext);
|
|
135
|
+
localStorage.setItem(String(key), encrypted);
|
|
136
|
+
return true;
|
|
137
|
+
} catch (err) {
|
|
138
|
+
console.error(`[Storage] setSafe failed:`, err);
|
|
139
|
+
return false;
|
|
26
140
|
}
|
|
141
|
+
}
|
|
27
142
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
143
|
+
async getSafe(key, def = null) {
|
|
144
|
+
if (!this._key) throw new Error('[Storage] Encryption key not configured.');
|
|
145
|
+
if (!this._available) return def;
|
|
146
|
+
const raw = localStorage.getItem(String(key));
|
|
147
|
+
if (raw === null) return def;
|
|
148
|
+
try {
|
|
149
|
+
const decrypted = await this._decrypt(raw);
|
|
150
|
+
return this.parse(decrypted);
|
|
151
|
+
} catch (err) {
|
|
152
|
+
console.warn(`[Storage] getSafe failed (tampered/invalid):`, err);
|
|
153
|
+
return def;
|
|
32
154
|
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async pullSafe(key, def = null) {
|
|
158
|
+
const data = await this.getSafe(key, def);
|
|
159
|
+
this.remove(key);
|
|
160
|
+
return data;
|
|
161
|
+
}
|
|
33
162
|
}
|
|
34
163
|
|
|
35
|
-
export default new Storage();
|
|
164
|
+
export default new Storage();
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
|
|
5
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
6
|
+
const __dirname = path.dirname(__filename);
|
|
7
|
+
|
|
8
|
+
const CONFIG = {
|
|
9
|
+
inputFile: path.resolve(__dirname, '../components/index.js'),
|
|
10
|
+
outputFile: path.resolve(__dirname, '../index.d.ts'),
|
|
11
|
+
importPathPrefix: './components',
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
function parseExports(content) {
|
|
16
|
+
return content
|
|
17
|
+
.split('\n')
|
|
18
|
+
.map(line => line.trim())
|
|
19
|
+
.filter(line => line && !line.startsWith('//'))
|
|
20
|
+
.map(line => {
|
|
21
|
+
const match = line.match(/^export\s+\*\s+as\s+(\w+)\s+from\s+['"](.+)['"]\s*;?$/);
|
|
22
|
+
return match ? { componentName: match[1], relativePath: match[2] } : null;
|
|
23
|
+
})
|
|
24
|
+
.filter(item => item !== null);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
function transformImportPath(relativePath, prefix) {
|
|
29
|
+
if (relativePath.startsWith('./')) {
|
|
30
|
+
return `${prefix}${relativePath.slice(1)}`;
|
|
31
|
+
}
|
|
32
|
+
return relativePath;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
function generateDeclaration(components, prefix) {
|
|
37
|
+
const lines = [
|
|
38
|
+
'// Auto-generated Vue component type declarations',
|
|
39
|
+
'// Do not edit manually - run `npm run build` to update',
|
|
40
|
+
`declare module 'vue' {`,
|
|
41
|
+
` export interface GlobalComponents {`,
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
for (const { componentName, relativePath } of components) {
|
|
45
|
+
const importPath = transformImportPath(relativePath, prefix);
|
|
46
|
+
lines.push(` ${componentName}: typeof import('${importPath}')['${componentName}']`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
lines.push(` }`, `}`, '');
|
|
50
|
+
return lines.join('\n');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
function main() {
|
|
55
|
+
try {
|
|
56
|
+
console.log(`Reading exports from: ${CONFIG.inputFile}`);
|
|
57
|
+
|
|
58
|
+
if (!fs.existsSync(CONFIG.inputFile)) {
|
|
59
|
+
throw new Error(`Input file not found: ${CONFIG.inputFile}`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const content = fs.readFileSync(CONFIG.inputFile, 'utf-8');
|
|
63
|
+
const components = parseExports(content);
|
|
64
|
+
|
|
65
|
+
if (components.length === 0) {
|
|
66
|
+
console.warn('No component exports found. Check your input file format.');
|
|
67
|
+
} else {
|
|
68
|
+
console.log(`Found ${components.length} component exports`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const declaration = generateDeclaration(components, CONFIG.importPathPrefix);
|
|
72
|
+
|
|
73
|
+
fs.writeFileSync(CONFIG.outputFile, declaration, 'utf-8');
|
|
74
|
+
console.log(`Generated: ${CONFIG.outputFile}`);
|
|
75
|
+
console.log(`Total components: ${components.length}`);
|
|
76
|
+
|
|
77
|
+
} catch (error) {
|
|
78
|
+
console.error('Error:', error.message);
|
|
79
|
+
process.exit(1);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
main();
|