From 13248adaf46429a2e895f7fb7e7795dc3bc6078f Mon Sep 17 00:00:00 2001 From: Masoud Motahari Date: Wed, 29 Jan 2025 14:39:51 +0330 Subject: [PATCH 1/3] feat(query.js): add stringifier and parser method --- src/utils/query.js | 136 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 src/utils/query.js diff --git a/src/utils/query.js b/src/utils/query.js new file mode 100644 index 0000000..c340a6e --- /dev/null +++ b/src/utils/query.js @@ -0,0 +1,136 @@ +// Utils +import { isObject, normalizeData } from '../utils/index'; + +function parseKey(keyString) { + const keyParts = []; + + const mainKeyPattern = /(\w+)(?=\[)/; + const bracketContentsPattern = /\[(.*?)\]/g; + + const mainKeyMatch = keyString.match(mainKeyPattern); + + if (mainKeyMatch) { + const mainKey = mainKeyMatch[1]; + keyParts.push(mainKey); + + const bracketMatches = [...keyString.matchAll(bracketContentsPattern)]; + const nestedKeys = bracketMatches.map(match => match[1]); + keyParts.push(...nestedKeys); + } else { + keyParts.push(keyString); + } + + return keyParts; +} + +function isNumericString(string) { + return /^\d+$/.test(string); +} + +function setValue(obj, path, value) { + let current = obj; + + for (let i = 0; i < path.length; i++) { + const part = path[i]; + const isNumeric = isNumericString(part); + const isLast = i === path.length - 1; + + if (isLast) { + current[part] = value; + break; + } + if (!current[part]) { + current[part] = isNumeric && Array.isArray(current) ? [] : {}; + } + + if (isNumeric && Array.isArray(current)) { + const index = parseInt(part, 10); + if (index >= current.length) { + current.length = index + 1; + } + current = current[index] || (current[index] = {}); + } else { + current = current[part]; + } + } +} + +function convertObjectsToArrays(obj) { + if (!isObject(obj)) { + return obj; + } + + if (Array.isArray(obj)) { + return obj.map(convertObjectsToArrays); + } + + const keys = Object.keys(obj); + const isArrayLike = keys.every((key, index) => String(index) === key); + + if (isArrayLike) { + return keys.map(key => convertObjectsToArrays(obj[key])); + } else { + const newObj = {}; + for (const key of keys) { + newObj[key] = convertObjectsToArrays(obj[key]); + } + return newObj; + } +} + +const QueryString = { + /** + * + * + * @param {String | Object} input + * @returns {Object} + */ + parse(input) { + const result = {}; + + let obj = input; + + if (typeof input === 'string') { + obj = Object.fromEntries(new URLSearchParams(input).entries()); + } + + for (const key in obj) { + const value = obj[key]; + const path = parseKey(key); + + const convertedValue = Array.isArray(value) ? value : normalizeData(value); + + setValue(result, path, convertedValue); + } + + return convertObjectsToArrays(result); + }, + + /** + * + * @param object + * @returns {string} + */ + stringify(object) { + const params = new URLSearchParams(); + + function traverse(value, path = '') { + if (Array.isArray(value)) { + value.forEach((item, index) => { + traverse(item, `${path}[${index}]`); + }); + } else if (isObject(value)) { + for (const key in value) { + traverse(value[key], path ? `${path}[${key}]` : key); + } + } else if (value !== undefined) { + params.append(path, value); + } + } + + traverse(object); + return params.toString(); + } +}; + +export default QueryString; From 1188b481881760c07fe345144e7ad376ee44a087 Mon Sep 17 00:00:00 2001 From: Masoud Motahari Date: Wed, 29 Jan 2025 14:40:22 +0330 Subject: [PATCH 2/3] feat(router): use stringifier and parser method for query --- src/router/index.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/router/index.js b/src/router/index.js index 452ceea..e69aa9d 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -1,11 +1,16 @@ import { createRouter, createWebHistory } from 'vue-router'; +// Middlewares import AuthenticateRoute from './middleware/AuthenticateRoute'; import AuthorizeRoute from './middleware/AuthorizeRoute'; import RedirectIfAuthenticated from './middleware/RedirectIfAuthenticated'; +// Views import PanelView from '@/views/PanelView.vue'; +// Utils +import QueryString from '@/utils/query'; + const router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), routes: [ @@ -44,8 +49,10 @@ const router = createRouter({ // which is lazy-loaded when the route is visited. component: () => import('@/views/LoginView.vue'), beforeEnter: [RedirectIfAuthenticated] - } - ] + }, + ], + parseQuery: (query) => QueryString.parse(query), + stringifyQuery: (query) => QueryString.stringify(query) }); export default router; From a44d62228d8e4d028e017859d34d9a1806d55300 Mon Sep 17 00:00:00 2001 From: Masoud Motahari Date: Wed, 29 Jan 2025 16:16:56 +0330 Subject: [PATCH 3/3] fix(query):stringify null value --- src/utils/query.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/query.js b/src/utils/query.js index c340a6e..c1ff660 100644 --- a/src/utils/query.js +++ b/src/utils/query.js @@ -123,7 +123,7 @@ const QueryString = { for (const key in value) { traverse(value[key], path ? `${path}[${key}]` : key); } - } else if (value !== undefined) { + } else if (value !== undefined && value !== null) { params.append(path, value); } }