diff --git a/packages/cli/package.json b/packages/cli/package.json index 8c69f3a6..a370449a 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@reflag/cli", - "version": "1.0.2", + "version": "1.0.3", "packageManager": "yarn@4.1.1", "description": "CLI for Reflag service", "main": "./dist/index.js", @@ -31,8 +31,9 @@ "scripts": { "build": "tsc && shx chmod +x dist/index.js", "reflag": "yarn build && ./dist/index.js", - "test": "vitest -c vite.config.js", - "test:ci": "vitest run -c vite.config.js --reporter=default --reporter=junit --outputFile=junit.xml", + "test": "vitest run", + "test:watch": "vitest", + "test:ci": "yarn test --reporter=default --reporter=junit --outputFile=junit.xml", "coverage": "vitest run --coverage", "lint": "eslint .", "lint:ci": "eslint --output-file eslint-report.json --format json .", diff --git a/packages/cli/test/json.test.ts b/packages/cli/test/json.test.ts index bcbc1a6f..600f3ae0 100644 --- a/packages/cli/test/json.test.ts +++ b/packages/cli/test/json.test.ts @@ -3,6 +3,7 @@ import { describe, expect, it } from "vitest"; import { JSONToType, mergeTypeASTs, + quoteKey, stringifyTypeAST, toTypeAST, TypeAST, @@ -300,6 +301,37 @@ describe("JSON utilities", () => { expect(stringifyTypeAST({ kind: "object", properties: [] })).toBe("{}"); }); + it("should quote object keys with special characters", () => { + const ast: TypeAST = { + kind: "object", + properties: [ + { + key: "my-key", + type: { kind: "primitive", type: "string" }, + optional: false, + }, + { + key: "my key", + type: { kind: "primitive", type: "string" }, + optional: false, + }, + { + key: "my.key", + type: { kind: "primitive", type: "string" }, + optional: false, + }, + { + key: "123key", + type: { kind: "primitive", type: "string" }, + optional: false, + }, + ], + }; + + const expected = `{\n "my-key": string,\n "my key": string,\n "my.key": string,\n "123key": string\n}`; + expect(stringifyTypeAST(ast)).toBe(expected); + }); + it("should stringify union types", () => { const ast: TypeAST = { kind: "union", @@ -422,4 +454,14 @@ describe("JSON utilities", () => { ).toBe(expected); }); }); + + describe("quoteKey", () => { + it("should quote keys with special characters", () => { + expect(quoteKey("my-key")).toBe('"my-key"'); + expect(quoteKey("my key")).toBe('"my key"'); + expect(quoteKey("my.key")).toBe('"my.key"'); + expect(quoteKey("123key")).toBe('"123key"'); + expect(quoteKey("key")).toBe("key"); + }); + }); }); diff --git a/packages/cli/utils/gen.ts b/packages/cli/utils/gen.ts index f248f873..2c771286 100644 --- a/packages/cli/utils/gen.ts +++ b/packages/cli/utils/gen.ts @@ -4,7 +4,7 @@ import { dirname, isAbsolute, join } from "node:path"; import { Flag, RemoteConfig } from "../services/flags.js"; -import { JSONToType } from "./json.js"; +import { JSONToType, quoteKey } from "./json.js"; export type GenFormat = "react" | "node"; @@ -118,7 +118,7 @@ ${flags .map(({ key }) => { const config = configDefs.get(key); return indentLines( - `"${key}": ${config?.definition ? `{ config: { payload: ${config.name} } }` : "boolean"};`, + `${quoteKey(key)}: ${config?.definition ? `{ config: { payload: ${config.name} } }` : "boolean"};`, 4, ); }) diff --git a/packages/cli/utils/json.ts b/packages/cli/utils/json.ts index ed79f4be..3c9feba5 100644 --- a/packages/cli/utils/json.ts +++ b/packages/cli/utils/json.ts @@ -179,13 +179,12 @@ export function stringifyTypeAST(ast: TypeAST, nestLevel = 0): string { if (ast.properties.length === 0) return "{}"; return `{\n${ast.properties - .map( - ({ key, optional, type }) => - `${nextIndent}${key}${optional ? "?" : ""}: ${stringifyTypeAST( - type, - nestLevel + 1, - )}`, - ) + .map(({ key, optional, type }) => { + return `${nextIndent}${quoteKey(key)}${optional ? "?" : ""}: ${stringifyTypeAST( + type, + nestLevel + 1, + )}`; + }) .join(",\n")}\n${indent}}`; case "union": @@ -198,6 +197,10 @@ export function stringifyTypeAST(ast: TypeAST, nestLevel = 0): string { } } +export function quoteKey(key: string): string { + return /[^a-zA-Z0-9_]/.test(key) || /^[0-9]/.test(key) ? `"${key}"` : key; +} + // Convert JSON array to TypeScript type export function JSONToType(json: JSONPrimitive[]): string | null { if (!json.length) return null;