diff --git a/package.json b/package.json index bb080a8..7a40656 100644 --- a/package.json +++ b/package.json @@ -147,7 +147,7 @@ "test": "vitest --silent", "test-not-silent": "vitest", "new-test": "vitest", - "test-esm": "node ./scripts/test-esm.mjs && ./scripts/checkdeps.mjs", + "test-esm": "node ./scripts/test-esm.mjs && ./scripts/checkdeps.mjs && ./scripts/checkimports.mjs", "pack-internal": "echo TODO maybe set an environment variable" }, "keywords": [ @@ -164,8 +164,7 @@ "dependencies": { "esbuild": "0.23.0", "jwt-decode": "^4.0.0", - "prettier": "3.2.5", - "globals": "~15.9.0" + "prettier": "3.2.5" }, "peerDependencies": { "@auth0/auth0-react": "^2.0.1", @@ -234,6 +233,7 @@ "eslint-plugin-require-extensions": "~0.1.3", "fetch-retry": "~5.0.6", "find-up": "^6.3.0", + "globals": "~15.9.0", "happy-dom": "~14.12.3", "inquirer": "^9.1.4", "inquirer-search-list": "~1.2.6", @@ -247,6 +247,7 @@ "react-dom": "^18.0.0", "semver": "^7.6.0", "shx": "~0.3.4", + "skott": "~0.35.3", "strip-ansi": "^7.0.1", "tsx": "~4.15.6", "typedoc": "^0.24.6", diff --git a/scripts/checkimports.mjs b/scripts/checkimports.mjs new file mode 100755 index 0000000..e36f5c4 --- /dev/null +++ b/scripts/checkimports.mjs @@ -0,0 +1,50 @@ +#!/usr/bin/env node +import { fileURLToPath } from "url"; +import { dirname } from "path"; +import skott from "skott"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); +const __root = dirname(__dirname); + +async function entrypointHasCycles(entrypoint) { + // Note that skott can do a lot of other things too! + const { useGraph } = await skott({ + entrypoint: `./dist/esm/${entrypoint}/index.js`, + incremental: false, + cwd: __root, + includeBaseDir: true, + verbose: false, + }); + const { findCircularDependencies } = useGraph(); + + const circular = findCircularDependencies(); + if (circular.length) { + console.log("Found import cycles by traversing", entrypoint); + console.log(circular); + return false; + } + return true; +} + +let allOk = true; +// These haven't been fixed yet so we don't fail if they have cycles. +for (const entrypoint of [ + "bundler", + "nextjs", + "react", + "react-auth0", + "react-clerk", + "values", + // don't care about cycles in CLI +]) { + const ok = await entrypointHasCycles(entrypoint); + allOk &&= ok; +} + +if (!(await entrypointHasCycles("server"))) { + process.exit(1); +} else { + console.log("No import cycles found in server."); + process.exit(0); +} diff --git a/src/server/api.ts b/src/server/api.ts index 9b36e7f..91f7fba 100644 --- a/src/server/api.ts +++ b/src/server/api.ts @@ -9,6 +9,7 @@ import { import { Expand, UnionToIntersection } from "../type_utils.js"; import { PaginationOptions, PaginationResult } from "./pagination.js"; import { getFunctionAddress } from "./impl/actions_impl.js"; +import { functionName } from "./functionName.js"; /** * The type of a Convex function. @@ -62,11 +63,6 @@ export type FunctionReference< _componentPath: ComponentPath; }; -/** - * A symbol for accessing the name of a {@link FunctionReference} at runtime. - */ -export const functionName = Symbol.for("functionName"); - /** * Get the name of a function from a {@link FunctionReference}. * diff --git a/src/server/components/index.ts b/src/server/components/index.ts index 39896b8..0dc9377 100644 --- a/src/server/components/index.ts +++ b/src/server/components/index.ts @@ -12,16 +12,7 @@ import { ComponentDefinitionAnalysis, ComponentDefinitionType, } from "./definition.js"; - -export const toReferencePath = Symbol.for("toReferencePath"); - -export function extractReferencePath(reference: any): string | null { - return reference[toReferencePath] ?? null; -} - -export function isFunctionHandle(s: string): boolean { - return s.startsWith("function://"); -} +import { toReferencePath } from "./paths.js"; /** * @internal diff --git a/src/server/components/paths.ts b/src/server/components/paths.ts new file mode 100644 index 0000000..4f76e98 --- /dev/null +++ b/src/server/components/paths.ts @@ -0,0 +1,9 @@ +export const toReferencePath = Symbol.for("toReferencePath"); + +export function extractReferencePath(reference: any): string | null { + return reference[toReferencePath] ?? null; +} + +export function isFunctionHandle(s: string): boolean { + return s.startsWith("function://"); +} diff --git a/src/server/functionName.ts b/src/server/functionName.ts new file mode 100644 index 0000000..a0edd65 --- /dev/null +++ b/src/server/functionName.ts @@ -0,0 +1,4 @@ +/** + * A symbol for accessing the name of a {@link FunctionReference} at runtime. + */ +export const functionName = Symbol.for("functionName"); diff --git a/src/server/functions.ts b/src/server/functions.ts new file mode 100644 index 0000000..a0edd65 --- /dev/null +++ b/src/server/functions.ts @@ -0,0 +1,4 @@ +/** + * A symbol for accessing the name of a {@link FunctionReference} at runtime. + */ +export const functionName = Symbol.for("functionName"); diff --git a/src/server/impl/actions_impl.ts b/src/server/impl/actions_impl.ts index 367c9c4..d5aef3d 100644 --- a/src/server/impl/actions_impl.ts +++ b/src/server/impl/actions_impl.ts @@ -2,8 +2,9 @@ import { convexToJson, jsonToConvex, Value } from "../../values/index.js"; import { version } from "../../index.js"; import { performAsyncSyscall } from "./syscall.js"; import { parseArgs } from "../../common/index.js"; -import { functionName, FunctionReference } from "../../server/api.js"; -import { extractReferencePath, isFunctionHandle } from "../components/index.js"; +import { FunctionReference } from "../../server/api.js"; +import { extractReferencePath, isFunctionHandle } from "../components/paths.js"; +import { functionName } from "../functionName.js"; function syscallArgs( requestId: string,