diff --git a/.changeset/brave-tips-protect.md b/.changeset/brave-tips-protect.md new file mode 100644 index 00000000000..4998bf9b3aa --- /dev/null +++ b/.changeset/brave-tips-protect.md @@ -0,0 +1,5 @@ +--- +"@smithy/shared-ini-file-loader": patch +--- + +Treat absence of prefix whitespace as section keys when reading ini files diff --git a/packages/shared-ini-file-loader/src/parseIni.spec.ts b/packages/shared-ini-file-loader/src/parseIni.spec.ts index db21290cc93..273044af7d0 100644 --- a/packages/shared-ini-file-loader/src/parseIni.spec.ts +++ b/packages/shared-ini-file-loader/src/parseIni.spec.ts @@ -90,34 +90,63 @@ describe(parseIni.name, () => { }); }); - it("returns data from main section, and not subsection", () => { - const mockProfileDataWithSubSettings = { - key: "value", - subSection: { subKey: "subValue" }, - }; - const mockInput = getMockProfileContent(mockProfileName, mockProfileDataWithSubSettings); - expect(parseIni(mockInput)).toStrictEqual({ - [mockProfileName]: { - key: "value", - [["subSection", "subKey"].join(CONFIG_PREFIX_SEPARATOR)]: "subValue", - }, + describe("returns data from main section, and not subsection", () => { + it("if subsection comes after section", () => { + const mockProfileDataWithSubSettings = { + key: "keyValue", + subSection: { + key: "keyValueInSubSection", + subKey: "subKeyValue", + }, + }; + const mockInput = getMockProfileContent(mockProfileName, mockProfileDataWithSubSettings); + expect(parseIni(mockInput)).toStrictEqual({ + [mockProfileName]: { + key: "keyValue", + [["subSection", "key"].join(CONFIG_PREFIX_SEPARATOR)]: "keyValueInSubSection", + [["subSection", "subKey"].join(CONFIG_PREFIX_SEPARATOR)]: "subKeyValue", + }, + }); + + const mockProfileName2 = "mock_profile_name_2"; + const mockProfileDataWithSubSettings2 = { + key: "keyValue2", + subSection: { + key: "keyValue2InSubSection", + subKey: "subKeyValue2", + }, + }; + const mockInput2 = getMockProfileContent(mockProfileName2, mockProfileDataWithSubSettings2); + expect(parseIni(`${mockInput}${mockInput2}`)).toStrictEqual({ + [mockProfileName]: { + key: "keyValue", + [["subSection", "key"].join(CONFIG_PREFIX_SEPARATOR)]: "keyValueInSubSection", + [["subSection", "subKey"].join(CONFIG_PREFIX_SEPARATOR)]: "subKeyValue", + }, + [mockProfileName2]: { + key: "keyValue2", + [["subSection", "key"].join(CONFIG_PREFIX_SEPARATOR)]: "keyValue2InSubSection", + [["subSection", "subKey"].join(CONFIG_PREFIX_SEPARATOR)]: "subKeyValue2", + }, + }); }); - const mockProfileName2 = "mock_profile_name_2"; - const mockProfileDataWithSubSettings2 = { - key: "value2", - subSection: { subKey: "subValue2" }, - }; - const mockInput2 = getMockProfileContent(mockProfileName2, mockProfileDataWithSubSettings2); - expect(parseIni(`${mockInput}${mockInput2}`)).toStrictEqual({ - [mockProfileName]: { - key: "value", - [["subSection", "subKey"].join(CONFIG_PREFIX_SEPARATOR)]: "subValue", - }, - [mockProfileName2]: { - key: "value2", - [["subSection", "subKey"].join(CONFIG_PREFIX_SEPARATOR)]: "subValue2", - }, + it("if subsection comes before section", () => { + const mockProfileDataWithSubSettings = { + subSection: { + key: "keyValueInSubSection", + subKey: "subKeyValue", + }, + key: "keyValue", + }; + const mockInput = getMockProfileContent(mockProfileName, mockProfileDataWithSubSettings); + expect(parseIni(mockInput)).toStrictEqual({ + [mockProfileName]: { + [["subSection", "key"].join(CONFIG_PREFIX_SEPARATOR)]: "keyValueInSubSection", + [["subSection", "subKey"].join(CONFIG_PREFIX_SEPARATOR)]: "subKeyValue", + key: "keyValue", + }, + }); }); }); }); diff --git a/packages/shared-ini-file-loader/src/parseIni.ts b/packages/shared-ini-file-loader/src/parseIni.ts index 1e5fa8c1a53..1a63a31c052 100644 --- a/packages/shared-ini-file-loader/src/parseIni.ts +++ b/packages/shared-ini-file-loader/src/parseIni.ts @@ -11,15 +11,15 @@ export const parseIni = (iniData: string): ParsedIniData => { let currentSection: string | undefined; let currentSubSection: string | undefined; - for (let line of iniData.split(/\r?\n/)) { - line = line.split(/(^|\s)[;#]/)[0].trim(); // remove comments and trim - const isSection: boolean = line[0] === "[" && line[line.length - 1] === "]"; + for (const iniLine of iniData.split(/\r?\n/)) { + const trimmedLine = iniLine.split(/(^|\s)[;#]/)[0].trim(); // remove comments and trim + const isSection: boolean = trimmedLine[0] === "[" && trimmedLine[trimmedLine.length - 1] === "]"; if (isSection) { // New section found. Reset currentSection and currentSubSection. currentSection = undefined; currentSubSection = undefined; - const sectionName = line.substring(1, line.length - 1); + const sectionName = trimmedLine.substring(1, trimmedLine.length - 1); const matches = prefixKeyRegex.exec(sectionName); if (matches) { const [, prefix, , name] = matches; @@ -36,15 +36,19 @@ export const parseIni = (iniData: string): ParsedIniData => { throw new Error(`Found invalid profile name "${sectionName}"`); } } else if (currentSection) { - const indexOfEqualsSign = line.indexOf("="); + const indexOfEqualsSign = trimmedLine.indexOf("="); if (![0, -1].includes(indexOfEqualsSign)) { const [name, value]: [string, string] = [ - line.substring(0, indexOfEqualsSign).trim(), - line.substring(indexOfEqualsSign + 1).trim(), + trimmedLine.substring(0, indexOfEqualsSign).trim(), + trimmedLine.substring(indexOfEqualsSign + 1).trim(), ]; if (value === "") { currentSubSection = name; } else { + if (currentSubSection && iniLine.trimStart() === iniLine) { + // Reset currentSubSection if there is no whitespace + currentSubSection = undefined; + } map[currentSection] = map[currentSection] || {}; const key = currentSubSection ? [currentSubSection, name].join(CONFIG_PREFIX_SEPARATOR) : name; map[currentSection][key] = value;