History

Build the Ajv instance on first use and mark codemode-core side-effect free

Rhys Sullivan committed 2 months ago

Commit 3bba5fe1 parent dc1b5aa2 files · +28 −8Browse files

Unified · Split

2 files
  1. packages/kernel/core/package.json +1 −0
  2. packages/kernel/core/src/json-schema.ts +27 −8
packages/kernel/core/package.json+1−0
@@ -15,6 +15,7 @@ · 14 unmodified lines
1515 "dist"
1616 ],
1717 "type": "module",
18+ "sideEffects": false,
1819 "exports": {
1920 ".": "./src/index.ts"
2021 },
packages/kernel/core/src/json-schema.ts+27−8
@@ -5,14 +5,33 @@ import addFormats from "ajv-formats"; · 4 unmodified lines
55 import type { StandardSchema } from "./types";
66 import { unknownInputSchema } from "./types";
77
8−const ajv = new Ajv2020({
9− allErrors: true,
10− strict: false,
11− validateSchema: false,
12− allowUnionTypes: true,
13−});
8+// Built on first use, not at import. Two reasons, and the second is the one
9+// that matters beyond this file:
10+//
11+// 1. Constructing an Ajv instance and registering every format is real work,
12+// and it ran on each cold isolate that so much as imported this package —
13+// including the many that never validate a schema.
14+// 2. `new Ajv2020(...)` + `addFormats(...)` at module scope are import-time
15+// side effects, so a bundler must keep this module (and therefore ajv)
16+// whenever anything touches the package barrel. `packages/core/execution`
17+// imports exactly one runtime value from it — `CodeExecutionError` — and
18+// the rest as erased types, yet still dragged ajv AND sucrase into the
19+// server graph. With no module-scope effects left here, `sideEffects:
20+// false` in package.json is honest and the unused modules tree-shake out.
21+let ajvInstance: Ajv2020 | undefined;
1422
15−addFormats(ajv);
23+const getAjv = (): Ajv2020 => {
24+ if (ajvInstance === undefined) {
25+ ajvInstance = new Ajv2020({
26+ allErrors: true,
27+ strict: false,
28+ validateSchema: false,
29+ allowUnionTypes: true,
30+ });
31+ addFormats(ajvInstance);
32+ }
33+ return ajvInstance;
34+};
1635
1736 const decodePointerSegment = (segment: string): PropertyKey => {
1837 const decoded = segment.replaceAll("~1", "/").replaceAll("~0", "~");
@@ -47,7 +66,7 @@ export const standardSchemaFromJsonSchema = ( · 28 unmodified lines
4766 ): StandardSchema => {
4867 // oxlint-disable-next-line executor/no-try-catch-or-throw -- boundary: AJV compile throws for invalid schemas and this adapter preserves fallback behavior
4968 try {
50− const validate = ajv.compile(schema as Record<string, unknown>);
69+ const validate = getAjv().compile(schema as Record<string, unknown>);
5170
5271 return {
5372 "~standard": {