mirror of
https://github.com/usebruno/bruno.git
synced 2024-11-28 19:03:15 +01:00
feat(#1460): string interpolation
This commit is contained in:
parent
aed1b41da6
commit
9ad265e74f
54
package-lock.json
generated
54
package-lock.json
generated
@ -9,6 +9,7 @@
|
|||||||
"packages/bruno-app",
|
"packages/bruno-app",
|
||||||
"packages/bruno-electron",
|
"packages/bruno-electron",
|
||||||
"packages/bruno-cli",
|
"packages/bruno-cli",
|
||||||
|
"packages/bruno-common",
|
||||||
"packages/bruno-schema",
|
"packages/bruno-schema",
|
||||||
"packages/bruno-query",
|
"packages/bruno-query",
|
||||||
"packages/bruno-js",
|
"packages/bruno-js",
|
||||||
@ -21,6 +22,7 @@
|
|||||||
"@faker-js/faker": "^7.6.0",
|
"@faker-js/faker": "^7.6.0",
|
||||||
"@jest/globals": "^29.2.0",
|
"@jest/globals": "^29.2.0",
|
||||||
"@playwright/test": "^1.27.1",
|
"@playwright/test": "^1.27.1",
|
||||||
|
"@types/jest": "^29.5.11",
|
||||||
"fs-extra": "^11.1.1",
|
"fs-extra": "^11.1.1",
|
||||||
"husky": "^8.0.3",
|
"husky": "^8.0.3",
|
||||||
"jest": "^29.2.0",
|
"jest": "^29.2.0",
|
||||||
@ -5240,6 +5242,16 @@
|
|||||||
"@types/istanbul-lib-report": "*"
|
"@types/istanbul-lib-report": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/jest": {
|
||||||
|
"version": "29.5.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.11.tgz",
|
||||||
|
"integrity": "sha512-S2mHmYIVe13vrm6q4kN6fLYYAka15ALQki/vgDC3mIukEOx8WJlv0kQPM+d4w8Gp6u0uSdKND04IlTXBv0rwnQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"expect": "^29.0.0",
|
||||||
|
"pretty-format": "^29.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/json-schema": {
|
"node_modules/@types/json-schema": {
|
||||||
"version": "7.0.11",
|
"version": "7.0.11",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@ -5381,6 +5393,10 @@
|
|||||||
"resolved": "packages/bruno-cli",
|
"resolved": "packages/bruno-cli",
|
||||||
"link": true
|
"link": true
|
||||||
},
|
},
|
||||||
|
"node_modules/@usebruno/common": {
|
||||||
|
"resolved": "packages/bruno-common",
|
||||||
|
"link": true
|
||||||
|
},
|
||||||
"node_modules/@usebruno/graphql-docs": {
|
"node_modules/@usebruno/graphql-docs": {
|
||||||
"resolved": "packages/bruno-graphql-docs",
|
"resolved": "packages/bruno-graphql-docs",
|
||||||
"link": true
|
"link": true
|
||||||
@ -18144,6 +18160,21 @@
|
|||||||
"node": ">= 14"
|
"node": ">= 14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"packages/bruno-common": {
|
||||||
|
"name": "@usebruno/common",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"license": "MIT",
|
||||||
|
"devDependencies": {
|
||||||
|
"@rollup/plugin-commonjs": "^23.0.2",
|
||||||
|
"@rollup/plugin-node-resolve": "^15.0.1",
|
||||||
|
"@rollup/plugin-typescript": "^9.0.2",
|
||||||
|
"rollup": "3.2.5",
|
||||||
|
"rollup-plugin-dts": "^5.0.0",
|
||||||
|
"rollup-plugin-peer-deps-external": "^2.2.4",
|
||||||
|
"rollup-plugin-terser": "^7.0.2",
|
||||||
|
"typescript": "^4.8.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"packages/bruno-electron": {
|
"packages/bruno-electron": {
|
||||||
"name": "bruno",
|
"name": "bruno",
|
||||||
"version": "v1.6.1",
|
"version": "v1.6.1",
|
||||||
@ -22287,6 +22318,16 @@
|
|||||||
"@types/istanbul-lib-report": "*"
|
"@types/istanbul-lib-report": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/jest": {
|
||||||
|
"version": "29.5.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.11.tgz",
|
||||||
|
"integrity": "sha512-S2mHmYIVe13vrm6q4kN6fLYYAka15ALQki/vgDC3mIukEOx8WJlv0kQPM+d4w8Gp6u0uSdKND04IlTXBv0rwnQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"expect": "^29.0.0",
|
||||||
|
"pretty-format": "^29.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@types/json-schema": {
|
"@types/json-schema": {
|
||||||
"version": "7.0.11",
|
"version": "7.0.11",
|
||||||
"dev": true
|
"dev": true
|
||||||
@ -22628,6 +22669,19 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@usebruno/common": {
|
||||||
|
"version": "file:packages/bruno-common",
|
||||||
|
"requires": {
|
||||||
|
"@rollup/plugin-commonjs": "^23.0.2",
|
||||||
|
"@rollup/plugin-node-resolve": "^15.0.1",
|
||||||
|
"@rollup/plugin-typescript": "^9.0.2",
|
||||||
|
"rollup": "3.2.5",
|
||||||
|
"rollup-plugin-dts": "^5.0.0",
|
||||||
|
"rollup-plugin-peer-deps-external": "^2.2.4",
|
||||||
|
"rollup-plugin-terser": "^7.0.2",
|
||||||
|
"typescript": "^4.8.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@usebruno/graphql-docs": {
|
"@usebruno/graphql-docs": {
|
||||||
"version": "file:packages/bruno-graphql-docs",
|
"version": "file:packages/bruno-graphql-docs",
|
||||||
"requires": {
|
"requires": {
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
"packages/bruno-app",
|
"packages/bruno-app",
|
||||||
"packages/bruno-electron",
|
"packages/bruno-electron",
|
||||||
"packages/bruno-cli",
|
"packages/bruno-cli",
|
||||||
|
"packages/bruno-common",
|
||||||
"packages/bruno-schema",
|
"packages/bruno-schema",
|
||||||
"packages/bruno-query",
|
"packages/bruno-query",
|
||||||
"packages/bruno-js",
|
"packages/bruno-js",
|
||||||
@ -18,18 +19,20 @@
|
|||||||
"@faker-js/faker": "^7.6.0",
|
"@faker-js/faker": "^7.6.0",
|
||||||
"@jest/globals": "^29.2.0",
|
"@jest/globals": "^29.2.0",
|
||||||
"@playwright/test": "^1.27.1",
|
"@playwright/test": "^1.27.1",
|
||||||
|
"@types/jest": "^29.5.11",
|
||||||
|
"fs-extra": "^11.1.1",
|
||||||
"husky": "^8.0.3",
|
"husky": "^8.0.3",
|
||||||
"jest": "^29.2.0",
|
"jest": "^29.2.0",
|
||||||
"pretty-quick": "^3.1.3",
|
"pretty-quick": "^3.1.3",
|
||||||
"randomstring": "^1.2.2",
|
"randomstring": "^1.2.2",
|
||||||
"ts-jest": "^29.0.5",
|
"ts-jest": "^29.0.5"
|
||||||
"fs-extra": "^11.1.1"
|
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev:web": "npm run dev --workspace=packages/bruno-app",
|
"dev:web": "npm run dev --workspace=packages/bruno-app",
|
||||||
"build:web": "npm run build --workspace=packages/bruno-app",
|
"build:web": "npm run build --workspace=packages/bruno-app",
|
||||||
"prettier:web": "npm run prettier --workspace=packages/bruno-app",
|
"prettier:web": "npm run prettier --workspace=packages/bruno-app",
|
||||||
"dev:electron": "npm run dev --workspace=packages/bruno-electron",
|
"dev:electron": "npm run dev --workspace=packages/bruno-electron",
|
||||||
|
"build:bruno-common": "npm run build --workspace=packages/bruno-common",
|
||||||
"build:bruno-query": "npm run build --workspace=packages/bruno-query",
|
"build:bruno-query": "npm run build --workspace=packages/bruno-query",
|
||||||
"build:graphql-docs": "npm run build --workspace=packages/bruno-graphql-docs",
|
"build:graphql-docs": "npm run build --workspace=packages/bruno-graphql-docs",
|
||||||
"build:electron": "node ./scripts/build-electron.js",
|
"build:electron": "node ./scripts/build-electron.js",
|
||||||
|
22
packages/bruno-common/.gitignore
vendored
Normal file
22
packages/bruno-common/.gitignore
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
# dependencies
|
||||||
|
node_modules
|
||||||
|
yarn.lock
|
||||||
|
pnpm-lock.yaml
|
||||||
|
package-lock.json
|
||||||
|
.pnp
|
||||||
|
.pnp.js
|
||||||
|
|
||||||
|
# testing
|
||||||
|
coverage
|
||||||
|
|
||||||
|
# production
|
||||||
|
dist
|
||||||
|
|
||||||
|
# misc
|
||||||
|
.DS_Store
|
||||||
|
*.pem
|
||||||
|
|
||||||
|
# debug
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
5
packages/bruno-common/jest.config.js
Normal file
5
packages/bruno-common/jest.config.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
/** @type {import('ts-jest').JestConfigWithTsJest} */
|
||||||
|
module.exports = {
|
||||||
|
preset: 'ts-jest',
|
||||||
|
testEnvironment: 'node'
|
||||||
|
};
|
21
packages/bruno-common/license.md
Normal file
21
packages/bruno-common/license.md
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2024 Anoop M D, Anusree P S and Contributors
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
33
packages/bruno-common/package.json
Normal file
33
packages/bruno-common/package.json
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"name": "@usebruno/common",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"license": "MIT",
|
||||||
|
"main": "dist/cjs/index.js",
|
||||||
|
"module": "dist/esm/index.js",
|
||||||
|
"types": "dist/index.d.ts",
|
||||||
|
"files": [
|
||||||
|
"dist",
|
||||||
|
"src",
|
||||||
|
"package.json"
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"clean": "rimraf dist",
|
||||||
|
"test": "jest",
|
||||||
|
"prebuild": "npm run clean",
|
||||||
|
"build": "rollup -c",
|
||||||
|
"prepack": "npm run test && npm run build"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@rollup/plugin-commonjs": "^23.0.2",
|
||||||
|
"@rollup/plugin-node-resolve": "^15.0.1",
|
||||||
|
"@rollup/plugin-typescript": "^9.0.2",
|
||||||
|
"rollup": "3.2.5",
|
||||||
|
"rollup-plugin-dts": "^5.0.0",
|
||||||
|
"rollup-plugin-peer-deps-external": "^2.2.4",
|
||||||
|
"rollup-plugin-terser": "^7.0.2",
|
||||||
|
"typescript": "^4.8.4"
|
||||||
|
},
|
||||||
|
"overrides": {
|
||||||
|
"rollup": "3.2.5"
|
||||||
|
}
|
||||||
|
}
|
9
packages/bruno-common/readme.md
Normal file
9
packages/bruno-common/readme.md
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
# bruno-common
|
||||||
|
|
||||||
|
A collection of common utilities used across Bruno App, Electron and CLI packages.
|
||||||
|
|
||||||
|
### Publish to Npm Registry
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm publish --access=public
|
||||||
|
```
|
40
packages/bruno-common/rollup.config.js
Normal file
40
packages/bruno-common/rollup.config.js
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
const { nodeResolve } = require('@rollup/plugin-node-resolve');
|
||||||
|
const commonjs = require('@rollup/plugin-commonjs');
|
||||||
|
const typescript = require('@rollup/plugin-typescript');
|
||||||
|
const dts = require('rollup-plugin-dts');
|
||||||
|
const { terser } = require('rollup-plugin-terser');
|
||||||
|
const peerDepsExternal = require('rollup-plugin-peer-deps-external');
|
||||||
|
|
||||||
|
const packageJson = require('./package.json');
|
||||||
|
|
||||||
|
module.exports = [
|
||||||
|
{
|
||||||
|
input: 'src/index.ts',
|
||||||
|
output: [
|
||||||
|
{
|
||||||
|
file: packageJson.main,
|
||||||
|
format: 'cjs',
|
||||||
|
sourcemap: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
file: packageJson.module,
|
||||||
|
format: 'esm',
|
||||||
|
sourcemap: true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
plugins: [
|
||||||
|
peerDepsExternal(),
|
||||||
|
nodeResolve({
|
||||||
|
extensions: ['.css']
|
||||||
|
}),
|
||||||
|
commonjs(),
|
||||||
|
typescript({ tsconfig: './tsconfig.json' }),
|
||||||
|
terser()
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: 'dist/esm/index.d.ts',
|
||||||
|
output: [{ file: 'dist/index.d.ts', format: 'esm' }],
|
||||||
|
plugins: [dts.default()]
|
||||||
|
}
|
||||||
|
];
|
5
packages/bruno-common/src/index.ts
Normal file
5
packages/bruno-common/src/index.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import interpolate from './interpolate';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
interpolate
|
||||||
|
};
|
@ -0,0 +1,16 @@
|
|||||||
|
String Interpolation
|
||||||
|
|
||||||
|
### Goal
|
||||||
|
|
||||||
|
Today our interlation logic is duplicated across multiple packages.
|
||||||
|
The goal is to centralize a single source of truth for all interpolation logic.
|
||||||
|
|
||||||
|
### Considerations
|
||||||
|
|
||||||
|
- We want to be flexible in terms of key naming conventions.
|
||||||
|
- We plan to support Nested environments in the future.
|
||||||
|
|
||||||
|
### Moving away from handlebars
|
||||||
|
|
||||||
|
I think its time to move away from handlebars.
|
||||||
|
We don't need the full power of handlebars and write a custom interpolation function that meets our needs.
|
84
packages/bruno-common/src/interpolate/index.spec.ts
Normal file
84
packages/bruno-common/src/interpolate/index.spec.ts
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
import interpolate from './index';
|
||||||
|
|
||||||
|
describe('interpolate', () => {
|
||||||
|
it('should replace placeholders with values from the object', () => {
|
||||||
|
const inputString = 'Hello, my name is {{user.name}} and I am {{user.age}} years old';
|
||||||
|
const inputObject = {
|
||||||
|
'user.name': 'Bruno',
|
||||||
|
user: {
|
||||||
|
age: 4
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = interpolate(inputString, inputObject);
|
||||||
|
|
||||||
|
expect(result).toBe('Hello, my name is Bruno and I am 4 years old');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle missing values by leaving the placeholders unchanged using {{}} as delimiters', () => {
|
||||||
|
const inputString = 'Hello, my name is {{user.name}} and I am {{user.age}} years old';
|
||||||
|
const inputObject = {
|
||||||
|
user: {
|
||||||
|
name: 'Bruno'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = interpolate(inputString, inputObject);
|
||||||
|
|
||||||
|
expect(result).toBe('Hello, my name is Bruno and I am {{user.age}} years old');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle all valid keys', () => {
|
||||||
|
const inputObject = {
|
||||||
|
user: {
|
||||||
|
full_name: 'Bruno',
|
||||||
|
age: 4,
|
||||||
|
'fav-food': ['egg', 'meat'],
|
||||||
|
'want.attention': true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const inputStr = `
|
||||||
|
Hi, I am {{user.full_name}},
|
||||||
|
I am {{user.age}} years old.
|
||||||
|
My favorite food is {{user.fav-food[0]}} and {{user.fav-food[1]}}.
|
||||||
|
I like attention: {{user.want.attention}}
|
||||||
|
`;
|
||||||
|
const expectedStr = `
|
||||||
|
Hi, I am Bruno,
|
||||||
|
I am 4 years old.
|
||||||
|
My favorite food is egg and meat.
|
||||||
|
I like attention: true
|
||||||
|
`;
|
||||||
|
const result = interpolate(inputStr, inputObject);
|
||||||
|
expect(result).toBe(expectedStr);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should strictly match the keys (whitespace matters)', () => {
|
||||||
|
const inputString = 'Hello, my name is {{ user.name }} and I am {{user.age}} years old';
|
||||||
|
const inputObject = {
|
||||||
|
'user.name': 'Bruno',
|
||||||
|
user: {
|
||||||
|
age: 4
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = interpolate(inputString, inputObject);
|
||||||
|
|
||||||
|
expect(result).toBe('Hello, my name is {{ user.name }} and I am 4 years old');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should give precedence to the last key in case of duplicates', () => {
|
||||||
|
const inputString = 'Hello, my name is {{user.name}} and I am {{user.age}} years old';
|
||||||
|
const inputObject = {
|
||||||
|
'user.name': 'Bruno',
|
||||||
|
user: {
|
||||||
|
name: 'Not Bruno',
|
||||||
|
age: 4
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = interpolate(inputString, inputObject);
|
||||||
|
|
||||||
|
expect(result).toBe('Hello, my name is Not Bruno and I am 4 years old');
|
||||||
|
});
|
||||||
|
});
|
31
packages/bruno-common/src/interpolate/index.ts
Normal file
31
packages/bruno-common/src/interpolate/index.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/**
|
||||||
|
* The interpolation function expects a string with placeholders and an object with the values to replace the placeholders.
|
||||||
|
* The keys passed can have dot notation too.
|
||||||
|
*
|
||||||
|
* Ex: interpolate('Hello, my name is ${user.name} and I am ${user.age} years old', {
|
||||||
|
* "user.name": "Bruno",
|
||||||
|
* "user": {
|
||||||
|
* "age": 4
|
||||||
|
* }
|
||||||
|
* });
|
||||||
|
* Output: Hello, my name is Bruno and I am 4 years old
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { flattenObject } from '../utils';
|
||||||
|
|
||||||
|
const interpolate = (str: string, obj: Record<string, any>): string => {
|
||||||
|
if (!str || typeof str !== 'string' || !obj || typeof obj !== 'object') {
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
const patternRegex = /\{\{([^}]+)\}\}/g;
|
||||||
|
const flattenedObj = flattenObject(obj);
|
||||||
|
const result = str.replace(patternRegex, (match, placeholder) => {
|
||||||
|
const replacement = flattenedObj[placeholder];
|
||||||
|
return replacement !== undefined ? replacement : match;
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default interpolate;
|
39
packages/bruno-common/src/utils/index.spec.ts
Normal file
39
packages/bruno-common/src/utils/index.spec.ts
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import { flattenObject } from './index';
|
||||||
|
|
||||||
|
describe('flattenObject', () => {
|
||||||
|
it('should flatten a simple object', () => {
|
||||||
|
const input = { a: 1, b: { c: 2, d: { e: 3 } } };
|
||||||
|
const output = flattenObject(input);
|
||||||
|
expect(output).toEqual({ a: 1, 'b.c': 2, 'b.d.e': 3 });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should flatten an object with arrays', () => {
|
||||||
|
const input = { a: 1, b: { c: [2, 3, 4], d: { e: 5 } } };
|
||||||
|
const output = flattenObject(input);
|
||||||
|
expect(output).toEqual({ a: 1, 'b.c[0]': 2, 'b.c[1]': 3, 'b.c[2]': 4, 'b.d.e': 5 });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should flatten an object with arrays having objects', () => {
|
||||||
|
const input = { a: 1, b: { c: [{ d: 2 }, { e: 3 }], f: { g: 4 } } };
|
||||||
|
const output = flattenObject(input);
|
||||||
|
expect(output).toEqual({ a: 1, 'b.c[0].d': 2, 'b.c[1].e': 3, 'b.f.g': 4 });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle null values', () => {
|
||||||
|
const input = { a: 1, b: { c: null, d: { e: 3 } } };
|
||||||
|
const output = flattenObject(input);
|
||||||
|
expect(output).toEqual({ a: 1, 'b.c': null, 'b.d.e': 3 });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle an empty object', () => {
|
||||||
|
const input = {};
|
||||||
|
const output = flattenObject(input);
|
||||||
|
expect(output).toEqual({});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle an object with nested empty objects', () => {
|
||||||
|
const input = { a: { b: {}, c: { d: {} } } };
|
||||||
|
const output = flattenObject(input);
|
||||||
|
expect(output).toEqual({});
|
||||||
|
});
|
||||||
|
});
|
11
packages/bruno-common/src/utils/index.ts
Normal file
11
packages/bruno-common/src/utils/index.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
export const flattenObject = (obj: Record<string, any>, parentKey: string = ''): Record<string, any> => {
|
||||||
|
return Object.entries(obj).reduce((acc: Record<string, any>, [key, value]: [string, any]) => {
|
||||||
|
const newKey = parentKey ? (Array.isArray(obj) ? `${parentKey}[${key}]` : `${parentKey}.${key}`) : key;
|
||||||
|
if (typeof value === 'object' && value !== null) {
|
||||||
|
Object.assign(acc, flattenObject(value, newKey));
|
||||||
|
} else {
|
||||||
|
acc[newKey] = value;
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
};
|
19
packages/bruno-common/tsconfig.json
Normal file
19
packages/bruno-common/tsconfig.json
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES6",
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"strict": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"jsx": "react",
|
||||||
|
"module": "ESNext",
|
||||||
|
"declaration": true,
|
||||||
|
"declarationDir": "types",
|
||||||
|
"sourceMap": true,
|
||||||
|
"outDir": "dist",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"emitDeclarationOnly": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"forceConsistentCasingInFileNames": true
|
||||||
|
},
|
||||||
|
"exclude": ["dist", "node_modules", "tests"]
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user