Skip to content

Commit

Permalink
updated parseData() to reject top-level design token data
Browse files Browse the repository at this point in the history
  • Loading branch information
c1rrus committed Dec 4, 2024
1 parent 0ff5419 commit fd5462f
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 23 deletions.
5 changes: 5 additions & 0 deletions .changeset/forty-boxes-shave.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@udt/parser-utils": minor
---

BREAKING CHANGE: `parseData()` will now throw an `InvalidStructureError` if the top-level object in the input data is a design token.
5 changes: 5 additions & 0 deletions .changeset/strange-insects-cover.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@udt/parser-utils": minor
---

BREAKING CHANGE: Specified minimum Node version of 18
2 changes: 1 addition & 1 deletion packages/parser-utils/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const fileData = { /* ... */ };
// relevant properties of each group and design token
// object it encounters to the functions you provide in
// the config:
const parsedData = parseData(fileData, {
const parsedGroup = parseData(fileData, {
/* Parser config */

// A function that checks whether an object is
Expand Down
40 changes: 20 additions & 20 deletions packages/parser-utils/src/parseData.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
type ParserConfig,
InvalidDataError,
AddChildFn,
InvalidStructureError,
} from "./parseData.js";

interface TestGroup {
Expand Down Expand Up @@ -108,14 +109,14 @@ describe("parseData()", () => {
});

describe("parsing an empty group object", () => {
let parsedGroupOrToken: TestGroup | TestDesignToken | undefined;
let parsedGroup: TestGroup | undefined;

beforeEach(() => {
parsedGroupOrToken = parseData({}, parserConfig);
parsedGroup = parseData({}, parserConfig);
});

it("returns a group", () => {
expect(parsedGroupOrToken?.type).toBe("group");
expect(parsedGroup?.type).toBe("group");
});

it("calls isDesignTokenData function once", () => {
Expand All @@ -141,33 +142,26 @@ describe("parseData()", () => {

describe("parsing a design token object", () => {
const testTokenData = {
value: "whatever", // <-- this makes it a token
other: "thing",
stuff: 123,
notAGroup: {},
testToken: {
value: "whatever", // <-- this makes it a token
other: "thing",
stuff: 123,
notAGroup: {},
},
};
let parsedGroupOrToken: TestGroup | TestDesignToken | undefined;

beforeEach(() => {
parsedGroupOrToken = parseData(testTokenData, parserConfig);
});

it("returns a design token", () => {
expect(parsedGroupOrToken?.type).toBe("token");
});

it("does not call parseGroupData function", () => {
expect(mockParseGroupData).not.toHaveBeenCalled();
parseData(testTokenData, parserConfig);
});

it("calls parseDesignTokenData function once", () => {
expect(mockParseDesignTokenData).toHaveBeenCalledOnce();
});

it("calls parseDesignTokenData function with complete data and empty path array", () => {
it("calls parseDesignTokenData function with complete data and path array", () => {
expect(mockParseDesignTokenData).toHaveBeenCalledWith(
testTokenData,
[],
testTokenData.testToken,
["testToken"],
undefined
);
});
Expand Down Expand Up @@ -432,4 +426,10 @@ describe("parseData()", () => {
expect((error as InvalidDataError).data).toBe(123);
}
});

it("throws an InvalidStructureError when there is no root group", () => {
expect(() =>
parseData({ value: "naughty anonymous token" }, parserConfig)
).toThrowError(InvalidStructureError);
});
});
31 changes: 29 additions & 2 deletions packages/parser-utils/src/parseData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,25 @@ export class InvalidDataError extends Error {
}
}

/**
* Thrown when the outermost object in the data passed to `parseData()`
* is not a group.
*/
export class InvalidStructureError extends Error {
/**
* The offending value.
*/
public data: unknown;

constructor(data: unknown) {
super(
`Expected a group at the root level, but encountered a design token instead`
);
this.name = "InvalidDataError";
this.data = data;
}
}

/**
* The internal data parsing implementation.
*
Expand Down Expand Up @@ -210,6 +229,10 @@ function parseDataImpl<ParsedDesignToken, ParsedGroup, T>(
let groupOrToken: ParsedGroup | ParsedDesignToken | undefined = undefined;
if (isDesignTokenData(data)) {
// looks like a token
if (path.length === 0) {
throw new InvalidStructureError(data);
}

groupOrToken = parseDesignTokenData(data, path, contextFromParent);
if (addChildToGroup && path.length > 0 && parentGroup !== undefined) {
addChildToGroup(parentGroup, path[path.length - 1], groupOrToken);
Expand Down Expand Up @@ -275,6 +298,10 @@ export function parseData<ParsedDesignToken, ParsedGroup, T>(
data: unknown,
config: ParserConfig<ParsedDesignToken, ParsedGroup, T>,
contextFromParent?: T
): ParsedDesignToken | ParsedGroup | undefined {
return parseDataImpl(data, config, contextFromParent);
): ParsedGroup | undefined {
// parseDataImpl() called with an empty path will never return
// a ParsedDesignToken
return parseDataImpl(data, config, contextFromParent) as
| ParsedGroup
| undefined;
}

0 comments on commit fd5462f

Please sign in to comment.