Skip to content

Commit

Permalink
Move Perseus JSON parsers to perseus-core (#2155)
Browse files Browse the repository at this point in the history
## Summary:
These parsers will need to run in a NodeJS service for server-side
scoring. The server needs to be able to import them without also pulling
in JSX.

Moving them to perseus-core means we can't validate
dynamically-registered widgets as strictly, but that seems like a small
sacrifice given that a) dynamic widgets are not currently used in
production, and b) the validation we can do on them would not be
particularly strict in any case.

Issue: LEMS-2774

Test plan:

`yarn test`

Author: benchristel

Reviewers: handeyeco, jeremywiebe, benchristel

Required Reviewers:

Approved By: handeyeco, jeremywiebe

Checks: ✅ Publish npm snapshot (ubuntu-latest, 20.x), ✅ Check for .changeset entries for all changed files (ubuntu-latest, 20.x), ✅ Cypress (ubuntu-latest, 20.x), ✅ Lint, Typecheck, Format, and Test (ubuntu-latest, 20.x), ✅ Publish Storybook to Chromatic (ubuntu-latest, 20.x), ✅ Check builds for changes in size (ubuntu-latest, 20.x)

Pull Request URL: #2155
  • Loading branch information
benchristel authored Jan 27, 2025
1 parent 8f89557 commit 0df0b19
Show file tree
Hide file tree
Showing 171 changed files with 127 additions and 92 deletions.
9 changes: 9 additions & 0 deletions .changeset/forty-eels-play.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"@khanacademy/perseus": minor
"@khanacademy/perseus-core": minor
---

Move `parsePerseusItem`, `parseAndMigratePerseusItem`,
`parseAndMigratePerseusArticle`, `isSuccess`, and `isFailure` to the
`perseus-core` package, and deprecate the equivalent exports from the `perseus`
package.
14 changes: 14 additions & 0 deletions packages/perseus-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,20 @@ export {
export {default as deepClone} from "./utils/deep-clone";
export * as GrapherUtil from "./utils/grapher-util";

export {
parsePerseusItem,
parseAndMigratePerseusItem,
parseAndMigratePerseusArticle,
} from "./parse-perseus-json";

export {
isSuccess,
isFailure,
type Result,
type Success,
type Failure,
} from "./parse-perseus-json/result";

export {libVersion} from "./version";

export {Errors} from "./error/errors";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import {isRealJSONParse} from "../is-real-json-parse";
import {isRealJSONParse} from "../utils/is-real-json-parse";

import {parse} from "./parse";
import {parsePerseusArticle as migrateAndTypecheckPerseusArticle} from "./perseus-parsers/perseus-article";
import {parsePerseusItem as migrateAndTypecheckPerseusItem} from "./perseus-parsers/perseus-item";
import {failure, isFailure} from "./result";

import type {Result} from "./result";
import type {PerseusItem, PerseusArticle} from "@khanacademy/perseus-core";
import type {PerseusItem, PerseusArticle} from "../data-schema";

/**
* Helper to parse PerseusItem JSON
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ import {jest} from "@jest/globals";

import {assertFailure, assertSuccess, success} from "./result";

import {parseAndMigratePerseusItem, parseAndMigratePerseusArticle} from ".";
import {
parseAndMigratePerseusItem,
parseAndMigratePerseusArticle,
} from "./index";

describe("parseAndMigratePerseusItem", () => {
it("should parse JSON", () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import {defaulted} from "../general-purpose-parsers/defaulted";

import {parseWidget} from "./widget";

import type {CategorizerWidget} from "../../data-schema";
import type {Parser} from "../parser-types";
import type {CategorizerWidget} from "@khanacademy/perseus-core";

export const parseCategorizerWidget: Parser<CategorizerWidget> = parseWidget(
constant("categorizer"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import {defaulted} from "../general-purpose-parsers/defaulted";

import {parseWidget} from "./widget";

import type {CSProgramWidget} from "../../data-schema";
import type {Parser} from "../parser-types";
import type {CSProgramWidget} from "@khanacademy/perseus-core";

export const parseCSProgramWidget: Parser<CSProgramWidget> = parseWidget(
constant("cs-program"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import {defaulted} from "../general-purpose-parsers/defaulted";

import {parseWidget} from "./widget";

import type {DefinitionWidget} from "../../data-schema";
import type {Parser} from "../parser-types";
import type {DefinitionWidget} from "@khanacademy/perseus-core";

export const parseDefinitionWidget: Parser<DefinitionWidget> = parseWidget(
constant("definition"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import {defaulted} from "../general-purpose-parsers/defaulted";

import {parseWidget} from "./widget";

import type {DropdownWidget} from "../../data-schema";
import type {Parser} from "../parser-types";
import type {DropdownWidget} from "@khanacademy/perseus-core";

export const parseDropdownWidget: Parser<DropdownWidget> = parseWidget(
constant("dropdown"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import {defaulted} from "../general-purpose-parsers/defaulted";
import {parseWidget} from "./widget";
import {parseWidgetsMap} from "./widgets-map";

import type {ExplanationWidget} from "../../data-schema";
import type {Parser} from "../parser-types";
import type {ExplanationWidget} from "@khanacademy/perseus-core";

export const parseExplanationWidget: Parser<ExplanationWidget> = parseWidget(
constant("explanation"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ import {defaulted} from "../general-purpose-parsers/defaulted";
import {versionedWidgetOptions} from "./versioned-widget-options";
import {parseWidgetWithVersion} from "./widget";

import type {ParsedValue, Parser} from "../parser-types";
import type {
ExpressionWidget,
PerseusExpressionAnswerForm,
} from "@khanacademy/perseus-core";
} from "../../data-schema";
import type {ParsedValue, Parser} from "../parser-types";

const stringOrNumberOrNullOrUndefined = union(string)
.or(number)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import {array, constant, object} from "../general-purpose-parsers";
import {parseGradedGroupWidgetOptions} from "./graded-group-widget";
import {parseWidget} from "./widget";

import type {GradedGroupSetWidget} from "../../data-schema";
import type {Parser} from "../parser-types";
import type {GradedGroupSetWidget} from "@khanacademy/perseus-core";

export const parseGradedGroupSetWidget: Parser<GradedGroupSetWidget> =
parseWidget(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ import {parsePerseusRenderer} from "./perseus-renderer";
import {parseWidget} from "./widget";
import {parseWidgetsMap} from "./widgets-map";

import type {GradedGroupWidget} from "../../data-schema";
import type {Parser} from "../parser-types";
import type {GradedGroupWidget} from "@khanacademy/perseus-core";

const falseToNull = pipeParsers(constant(false)).then(
convert(() => null),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ import {discriminatedUnionOn} from "../general-purpose-parsers/discriminated-uni

import {parseWidget} from "./widget";

import type {GrapherWidget} from "../../data-schema";
import type {Parser} from "../parser-types";
import type {GrapherWidget} from "@khanacademy/perseus-core";

const pairOfNumbers = pair(number, number);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import {constant} from "../general-purpose-parsers";
import {parsePerseusRenderer} from "./perseus-renderer";
import {parseWidget} from "./widget";

import type {GroupWidget} from "../../data-schema";
import type {Parser} from "../parser-types";
import type {GroupWidget} from "@khanacademy/perseus-core";

export const parseGroupWidget: Parser<GroupWidget> = parseWidget(
constant("group"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import {defaulted} from "../general-purpose-parsers/defaulted";
import {parseImages} from "./images-map";
import {parseWidgetsMap} from "./widgets-map";

import type {Hint} from "../../data-schema";
import type {Parser} from "../parser-types";
import type {Hint} from "@khanacademy/perseus-core";

export const parseHint: Parser<Hint> = object({
replace: optional(boolean),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import {defaulted} from "../general-purpose-parsers/defaulted";

import {parseWidget} from "./widget";

import type {IFrameWidget} from "../../data-schema";
import type {Parser} from "../parser-types";
import type {IFrameWidget} from "@khanacademy/perseus-core";

export const parseIframeWidget: Parser<IFrameWidget> = parseWidget(
constant("iframe"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import {
import {parsePerseusImageBackground} from "./perseus-image-background";
import {parseWidget} from "./widget";

import type {ImageWidget} from "../../data-schema";
import type {Parser} from "../parser-types";
import type {ImageWidget} from "@khanacademy/perseus-core";

const pairOfNumbers = pair(number, number);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {number, object, record, string} from "../general-purpose-parsers";
import {defaulted} from "../general-purpose-parsers/defaulted";

import type {PerseusImageDetail} from "../../data-schema";
import type {Parser} from "../parser-types";
import type {PerseusImageDetail} from "@khanacademy/perseus-core";

export const parseImages: Parser<{[key: string]: PerseusImageDetail}> =
defaulted(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import {

import {parseWidget} from "./widget";

import type {InputNumberWidget} from "../../data-schema";
import type {Parser} from "../parser-types";
import type {InputNumberWidget} from "@khanacademy/perseus-core";

const booleanToString: Parser<string> = (rawValue, ctx) => {
if (typeof rawValue === "boolean") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ import {discriminatedUnionOn} from "../general-purpose-parsers/discriminated-uni
import {parsePerseusImageBackground} from "./perseus-image-background";
import {parseWidget} from "./widget";

import type {Parser} from "../parser-types";
import type {
InteractionWidget,
PerseusInteractionElement,
} from "@khanacademy/perseus-core";
} from "../../data-schema";
import type {Parser} from "../parser-types";

const pairOfNumbers = pair(number, number);
const stringOrEmpty = defaulted(string, () => "");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {lockedFigureColorNames} from "@khanacademy/perseus-core";

import {lockedFigureColorNames} from "../../data-schema";
import {
array,
boolean,
Expand All @@ -20,7 +19,6 @@ import {discriminatedUnionOn} from "../general-purpose-parsers/discriminated-uni
import {parsePerseusImageBackground} from "./perseus-image-background";
import {parseWidget} from "./widget";

import type {Parser} from "../parser-types";
import type {
InteractiveGraphWidget,
LockedEllipseType,
Expand All @@ -46,7 +44,8 @@ import type {
PerseusGraphTypeRay,
PerseusGraphTypeSegment,
PerseusGraphTypeSinusoid,
} from "@khanacademy/perseus-core";
} from "../../data-schema";
import type {Parser} from "../parser-types";

// Used to represent 2-D points and ranges
const pairOfNumbers = pair(number, number);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import {defaulted} from "../general-purpose-parsers/defaulted";

import {parseWidget} from "./widget";

import type {LabelImageWidget} from "../../data-schema";
import type {Parser} from "../parser-types";
import type {LabelImageWidget} from "@khanacademy/perseus-core";

export const parseLabelImageWidget: Parser<LabelImageWidget> = parseWidget(
constant("label-image"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import {

import {parseWidget} from "./widget";

import type {MatcherWidget} from "../../data-schema";
import type {Parser} from "../parser-types";
import type {MatcherWidget} from "@khanacademy/perseus-core";

export const parseMatcherWidget: Parser<MatcherWidget> = parseWidget(
constant("matcher"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ import {stringToNumber} from "../general-purpose-parsers/string-to-number";

import {parseWidget} from "./widget";

import type {MatrixWidget} from "../../data-schema";
import type {Parser} from "../parser-types";
import type {MatrixWidget} from "@khanacademy/perseus-core";

const numberOrString = union(number).or(string).parser;
const numeric = pipeParsers(defaulted(numberOrString, () => NaN)).then(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import {defaulted} from "../general-purpose-parsers/defaulted";
import {parsePerseusImageBackground} from "./perseus-image-background";
import {parseWidget} from "./widget";

import type {MeasurerWidget} from "../../data-schema";
import type {Parser} from "../parser-types";
import type {MeasurerWidget} from "@khanacademy/perseus-core";

export const parseMeasurerWidget: Parser<MeasurerWidget> = parseWidget(
constant("measurer"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import {

import {parseWidget} from "./widget";

import type {MoleculeRendererWidget} from "../../data-schema";
import type {Parser} from "../parser-types";
import type {MoleculeRendererWidget} from "@khanacademy/perseus-core";

export const parseMoleculeRendererWidget: Parser<MoleculeRendererWidget> =
parseWidget(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ import {defaulted} from "../general-purpose-parsers/defaulted";

import {parseWidget} from "./widget";

import type {NumberLineWidget} from "../../data-schema";
import type {Parser} from "../parser-types";
import type {NumberLineWidget} from "@khanacademy/perseus-core";

const emptyStringToNull = pipeParsers(constant("")).then(
convert(() => null),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ import {defaulted} from "../general-purpose-parsers/defaulted";

import {parseWidget} from "./widget";

import type {NumericInputWidget} from "../../data-schema";
import type {Parser} from "../parser-types";
import type {NumericInputWidget} from "@khanacademy/perseus-core";

const parseMathFormat = enumeration(
"integer",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import {defaulted} from "../general-purpose-parsers/defaulted";
import {parsePerseusRenderer} from "./perseus-renderer";
import {parseWidget} from "./widget";

import type {OrdererWidget} from "../../data-schema";
import type {Parser, PartialParser} from "../parser-types";
import type {OrdererWidget} from "@khanacademy/perseus-core";

// There is an import cycle between orderer-widget.ts and perseus-renderer.ts.
// This wrapper ensures that we don't refer to parsePerseusRenderer before
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import {

import {parseWidget} from "./widget";

import type {PassageRefWidget} from "../../data-schema";
import type {Parser} from "../parser-types";
import type {PassageRefWidget} from "@khanacademy/perseus-core";

export const parsePassageRefWidget: Parser<PassageRefWidget> = parseWidget(
constant("passage-ref"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import {defaulted} from "../general-purpose-parsers/defaulted";

import {parseWidget} from "./widget";

import type {PassageWidget} from "../../data-schema";
import type {Parser} from "../parser-types";
import type {PassageWidget} from "@khanacademy/perseus-core";

export const parsePassageWidget: Parser<PassageWidget> = parseWidget(
constant("passage"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import {array, union} from "../general-purpose-parsers";

import {parsePerseusRenderer} from "./perseus-renderer";

import type {PerseusArticle} from "../../data-schema";
import type {Parser} from "../parser-types";
import type {PerseusArticle} from "@khanacademy/perseus-core";

export const parsePerseusArticle: Parser<PerseusArticle> = union(
parsePerseusRenderer,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import {
import {convert} from "../general-purpose-parsers/convert";
import {stringToNumber} from "../general-purpose-parsers/string-to-number";

import type {PerseusImageBackground} from "../../data-schema";
import type {Parser} from "../parser-types";
import type {PerseusImageBackground} from "@khanacademy/perseus-core";

function emptyToZero(x: string | number): string | number {
return x === "" ? 0 : x;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {ItemExtras} from "@khanacademy/perseus-core";

import {ItemExtras} from "../../data-schema";
import {
any,
array,
Expand All @@ -16,8 +15,8 @@ import {defaulted} from "../general-purpose-parsers/defaulted";
import {parseHint} from "./hint";
import {parsePerseusRenderer} from "./perseus-renderer";

import type {PerseusItem} from "../../data-schema";
import type {ParseContext, Parser, ParseResult} from "../parser-types";
import type {PerseusItem} from "@khanacademy/perseus-core";

export const parsePerseusItem: Parser<PerseusItem> = object({
question: parsePerseusRenderer,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import {defaulted} from "../general-purpose-parsers/defaulted";
import {parseImages} from "./images-map";
import {parseWidgetsMap} from "./widgets-map";

import type {PerseusRenderer} from "../../data-schema";
import type {Parser} from "../parser-types";
import type {PerseusRenderer} from "@khanacademy/perseus-core";

export const parsePerseusRenderer: Parser<PerseusRenderer> = defaulted(
object({
Expand Down
Loading

0 comments on commit 0df0b19

Please sign in to comment.