@oprt/cli 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // src/index.ts
2
2
  import { existsSync as existsSync5, readFileSync as readFileSync6 } from "fs";
3
- import { dirname as dirname5, join as join5 } from "path";
3
+ import { dirname as dirname5, join as join4 } from "path";
4
4
  import { fileURLToPath as fileURLToPath3 } from "url";
5
5
 
6
6
  // src/contract.ts
@@ -504,7 +504,7 @@ import { createHash } from "crypto";
504
504
  // package.json
505
505
  var package_default = {
506
506
  name: "@oprt/cli",
507
- version: "0.3.0",
507
+ version: "0.4.0",
508
508
  description: "The OPERATOR command line: copy components and recipes into your app as source, give coding agents the contract, and check their code against it. Includes an MCP server.",
509
509
  keywords: [
510
510
  "design-system",
@@ -560,6 +560,8 @@ var package_default = {
560
560
  lint: "eslint src scripts"
561
561
  },
562
562
  devDependencies: {
563
+ "@babel/parser": "8.0.8",
564
+ "@babel/types": "8.0.8",
563
565
  "@modelcontextprotocol/sdk": "1.32.1",
564
566
  tsup: "^8.3.0",
565
567
  typescript: "^5.6.3",
@@ -573,7 +575,7 @@ var package_default = {
573
575
 
574
576
  // src/identity.ts
575
577
  var VERSION = package_default.version;
576
- var RESULT_SCHEMA_VERSION = 1;
578
+ var RESULT_SCHEMA_VERSION = 2;
577
579
  var digests = /* @__PURE__ */ new WeakMap();
578
580
  function contractDigest(contract2) {
579
581
  let digest = digests.get(contract2);
@@ -594,77 +596,241 @@ function identityOf(contract2) {
594
596
 
595
597
  // src/check.ts
596
598
  import { existsSync as existsSync2, readdirSync, readFileSync as readFileSync3, statSync } from "fs";
597
- import { basename, extname, join as join3, relative as relative2, resolve as resolve2, sep as sep2 } from "path";
599
+ import { basename, extname, join as join2, relative as relative2, resolve as resolve2, sep as sep2 } from "path";
598
600
 
599
601
  // src/parser.ts
600
602
  import { createRequire } from "module";
601
- import { join as join2 } from "path";
602
603
  import { fileURLToPath as fileURLToPath2 } from "url";
603
- var SUPPORTED_TYPESCRIPT = "TypeScript 5 or 6";
604
- var INSTALL_TYPESCRIPT = "npm install -D typescript@6";
605
- function usable(typescript) {
606
- const ts = typescript;
607
- if (typeof ts.createSourceFile !== "function" || ts.ScriptKind === void 0)
608
- return `TypeScript ${ts.version ?? "(unknown version)"} has no JavaScript syntax API (createSourceFile), which oprt reads code with; ${SUPPORTED_TYPESCRIPT} has it`;
604
+
605
+ // ../../node_modules/.pnpm/@babel+parser@8.0.8/node_modules/@babel/parser/package.json
606
+ var version = "8.0.8";
607
+
608
+ // src/parser.ts
609
+ var PARSER = {
610
+ name: "@babel/parser",
611
+ version,
612
+ source: "bundled"
613
+ };
614
+ function optionsFor(file) {
615
+ const typed = /\.tsx$/.test(file);
616
+ const plugins = [
617
+ "jsx",
618
+ ...typed ? ["typescript"] : [],
619
+ "decorators",
620
+ "decoratorAutoAccessors",
621
+ "deferredImportEvaluation",
622
+ "sourcePhaseImports"
623
+ ];
624
+ return {
625
+ sourceType: "module",
626
+ plugins,
627
+ // Recoverable errors are returned beside the tree, instead of the first one being thrown.
628
+ errorRecovery: true,
629
+ attachComment: false,
630
+ allowReturnOutsideFunction: true,
631
+ allowUndeclaredExports: true,
632
+ allowSuperOutsideMethod: true,
633
+ allowNewTargetOutsideFunction: true
634
+ };
635
+ }
636
+ var FILE = "babel-parser.cjs";
637
+ var REINSTALL = "Install @oprt/cli again: the parser ships beside the executable in dist.";
638
+ var loaded;
639
+ function loadBabel() {
640
+ if (loaded !== void 0) return loaded;
641
+ const fromSource = /\.[cm]?tsx?$/.test(fileURLToPath2(import.meta.url));
609
642
  try {
610
- const probe = ts.createSourceFile(
611
- "probe.tsx",
612
- "<a>",
613
- ts.ScriptTarget?.Latest ?? 99,
614
- true,
615
- ts.ScriptKind.TSX
616
- );
617
- const diagnostics = probe.parseDiagnostics;
618
- if (!Array.isArray(diagnostics)) return "it does not report parse errors on a source file";
619
- if (diagnostics.length === 0) return "it did not report a syntax error in code that has one";
643
+ const require2 = createRequire(import.meta.url);
644
+ loaded = fromSource ? require2("@babel/parser") : require2("./babel-parser.cjs");
620
645
  } catch (error) {
621
- return `it failed on a probe parse: ${error instanceof Error ? error.message : String(error)}`;
646
+ throw new CliError(
647
+ `Code checking cannot load its parser (${FILE}): ${error instanceof Error ? error.message : String(error)} ${REINSTALL}`,
648
+ 1,
649
+ { code: "ERR_PARSER_MISSING" }
650
+ );
622
651
  }
623
- return null;
652
+ return loaded;
624
653
  }
625
- var packageFolder = (file) => /^(.*[\\/]node_modules[\\/]typescript)[\\/]/.exec(file)?.[1] ?? file;
626
- function probeTypeScript(cwd, options = {}) {
627
- const places = [
628
- { from: join2(cwd, "package.json"), source: "project" },
629
- ...options.installer === false ? [] : [{ from: fileURLToPath2(import.meta.url), source: "installer" }]
630
- ];
631
- let unusable;
632
- for (const { from, source } of places) {
633
- let file;
634
- let loaded2;
635
- try {
636
- const require2 = createRequire(from);
637
- file = require2.resolve("typescript");
638
- loaded2 = require2("typescript");
639
- } catch (error) {
640
- if (error.code !== "MODULE_NOT_FOUND")
641
- unusable ??= `typescript was found but failed to load: ${error instanceof Error ? error.message : String(error)}`;
642
- continue;
643
- }
644
- const typescript = loaded2.default ?? loaded2;
645
- const problem = usable(typescript);
646
- if (problem !== null) {
647
- unusable ??= `${problem} (at ${packageFolder(file)}).`;
648
- continue;
649
- }
654
+ var rejects = (babel, code) => {
655
+ try {
656
+ return babel.parse(code, optionsFor("probe.tsx")).errors.length > 0;
657
+ } catch {
658
+ return true;
659
+ }
660
+ };
661
+ function probeParser(load = loadBabel) {
662
+ let babel;
663
+ try {
664
+ babel = load();
665
+ } catch (error) {
650
666
  return {
651
- ready: true,
652
- typescript,
653
- resolution: {
654
- name: "typescript",
655
- version: typescript.version,
656
- source,
657
- resolved_from: packageFolder(file)
658
- }
667
+ ready: false,
668
+ reason: "parser_missing",
669
+ detail: error instanceof Error ? error.message : String(error)
670
+ };
671
+ }
672
+ if (rejects(babel, "export const a = <a b={1 as number} />;"))
673
+ return {
674
+ ready: false,
675
+ reason: "parser_unusable",
676
+ detail: "it rejected valid code in a probe parse"
677
+ };
678
+ if (!rejects(babel, "export const a = <a>;"))
679
+ return {
680
+ ready: false,
681
+ reason: "parser_unusable",
682
+ detail: "it did not report a syntax error in code that has one"
659
683
  };
684
+ return { ready: true, parser: PARSER };
685
+ }
686
+
687
+ // src/syntax.ts
688
+ var attempt = (babel, text2, file) => {
689
+ try {
690
+ const tree = babel.parse(text2, optionsFor(file));
691
+ return { tree, errors: tree.errors };
692
+ } catch (error) {
693
+ return { tree: null, fatal: error };
694
+ }
695
+ };
696
+ var sentence = (message) => message.replace(/\s*\(\d+:\d+\)$/, "");
697
+ var problemOf = (failure2) => ({
698
+ line: failure2.loc.line,
699
+ column: failure2.loc.column + 1,
700
+ message: sentence(failure2.message),
701
+ code: failure2.reasonCode ?? "SyntaxError",
702
+ position: failure2.pos
703
+ });
704
+ var tagName = (name) => {
705
+ if (name === void 0) return "";
706
+ if (name.type === "JSXIdentifier") return name.name;
707
+ if (name.type === "JSXMemberExpression")
708
+ return `${tagName(name.object)}.${name.property.name}`;
709
+ return `${name.namespace.name}:${name.name.name}`;
710
+ };
711
+ var NOT_NODES = /* @__PURE__ */ new Set(["loc", "extra", "errors", "tokens", "comments"]);
712
+ function* childNodes(node) {
713
+ for (const [key, value] of Object.entries(node)) {
714
+ if (NOT_NODES.has(key) || typeof value !== "object" || value === null) continue;
715
+ if (Array.isArray(value)) {
716
+ for (const item of value)
717
+ if (typeof item === "object" && item !== null && typeof item.type === "string")
718
+ yield item;
719
+ } else if (typeof value.type === "string") yield value;
660
720
  }
661
- if (unusable !== void 0)
662
- return { ready: false, reason: "typescript_unusable", detail: unusable };
721
+ }
722
+ var walk = (node, ancestors, visit) => {
723
+ visit(node, ancestors);
724
+ ancestors.push(node);
725
+ for (const child of childNodes(node)) walk(child, ancestors, visit);
726
+ ancestors.pop();
727
+ };
728
+ var unclosed = (element) => {
729
+ const name = element.openingElement.name;
663
730
  return {
664
- ready: false,
665
- reason: "typescript_missing",
666
- detail: `typescript could not be resolved from ${cwd}${options.installer === false ? "" : " or from beside the installer"}.`
731
+ line: name.loc?.start.line ?? 1,
732
+ column: (name.loc?.start.column ?? 0) + 1,
733
+ message: `JSX element '${tagName(name)}' has no corresponding closing tag.`,
734
+ code: "UnclosedJsxElement",
735
+ position: name.start ?? 0
667
736
  };
737
+ };
738
+ function problemsOf(tree, errors, limit, consumed) {
739
+ const mismatched = /* @__PURE__ */ new Map();
740
+ walk(tree.program, [], (node, ancestors) => {
741
+ if (node.type === "JSXElement" && node.closingElement && (node.closingElement.start ?? 0) < limit && tagName(node.openingElement.name) !== tagName(node.closingElement.name))
742
+ mismatched.set(node.closingElement.start ?? 0, { element: node, ancestors: [...ancestors] });
743
+ });
744
+ const problems = [];
745
+ for (const error of errors) {
746
+ if (error.pos >= limit) continue;
747
+ const entry = error.reasonCode === "MissingClosingTagElement" && mismatched.get(error.pos);
748
+ if (entry) {
749
+ const closing = tagName(entry.element.closingElement?.name);
750
+ const outer = [...entry.ancestors].reverse().find(
751
+ (ancestor) => ancestor.type === "JSXElement" && tagName(ancestor.openingElement.name) === closing && !consumed.has(ancestor)
752
+ );
753
+ if (outer !== void 0) {
754
+ consumed.add(outer);
755
+ problems.push(unclosed(entry.element));
756
+ continue;
757
+ }
758
+ }
759
+ problems.push(problemOf(error));
760
+ }
761
+ return problems;
762
+ }
763
+ var TYPE_ARGUMENTS = /(?<=[\w$\])])<(?:[^<>()]|<(?:[^<>()]|<[^<>()]*>)*>)*>(?=\()/g;
764
+ var LINE_BREAKS = new RegExp(`[^\\r\\n${String.fromCharCode(8232, 8233)}]`, "g");
765
+ var blankedOut = (match) => match.replace(LINE_BREAKS, " ");
766
+ var CLOSING_TAG = "</_>";
767
+ var COMPLETIONS = [CLOSING_TAG, ")", "}", "]"];
768
+ var MAX_COMPLETIONS = 24;
769
+ function complete(babel, prefix, file) {
770
+ let text2 = prefix;
771
+ let first = null;
772
+ for (let step = 0; step <= MAX_COMPLETIONS; step++) {
773
+ const tried = attempt(babel, text2, file);
774
+ if (tried.tree !== null) return { tree: tried.tree, errors: tried.errors, first };
775
+ const wantsMore = tried.fatal.reasonCode === "UnterminatedJsxContent" || tried.fatal.pos >= text2.length;
776
+ if (!wantsMore) return null;
777
+ const next = COMPLETIONS.find((piece) => {
778
+ const extended = attempt(babel, text2 + piece, file);
779
+ return extended.tree !== null || extended.fatal.reasonCode === "UnterminatedJsxContent" || extended.fatal.pos >= text2.length + piece.length;
780
+ });
781
+ if (next === void 0) return null;
782
+ first ??= next;
783
+ text2 += next;
784
+ }
785
+ return null;
786
+ }
787
+ function shutByCompletion(tree, limit) {
788
+ const shut = [];
789
+ walk(tree.program, [], (node) => {
790
+ if (node.type === "JSXElement" && node.closingElement && (node.closingElement.start ?? 0) >= limit)
791
+ shut.push(node);
792
+ });
793
+ return shut.sort((a, b) => (a.closingElement?.start ?? 0) - (b.closingElement?.start ?? 0));
794
+ }
795
+ var byPlace = (a, b) => a.position - b.position;
796
+ var MAX_EXPLAINED = 2e5;
797
+ function readSyntax(file, text2, babel = loadBabel()) {
798
+ const first = attempt(babel, text2, file);
799
+ if (first.tree !== null) {
800
+ const problems = problemsOf(first.tree, first.errors, Infinity, /* @__PURE__ */ new Set());
801
+ return problems.length === 0 ? { tree: first.tree, problems: [] } : { tree: null, problems: problems.sort(byPlace) };
802
+ }
803
+ let fatal = first.fatal;
804
+ if (text2.length > MAX_EXPLAINED) return { tree: null, problems: [problemOf(fatal)] };
805
+ if (/\.tsx$/.test(file) && fatal.reasonCode !== "UnterminatedJsxContent") {
806
+ const plain = text2.replace(TYPE_ARGUMENTS, blankedOut);
807
+ if (plain !== text2) {
808
+ const again = attempt(babel, plain, file);
809
+ if (again.tree === null && again.fatal.pos > fatal.pos) fatal = again.fatal;
810
+ }
811
+ }
812
+ try {
813
+ const finished = complete(babel, text2.slice(0, fatal.pos), file);
814
+ if (finished !== null) {
815
+ const limit = fatal.pos;
816
+ const consumed = /* @__PURE__ */ new Set();
817
+ const problems = problemsOf(finished.tree, finished.errors, limit, consumed);
818
+ const open = shutByCompletion(finished.tree, limit).filter(
819
+ (element) => !consumed.has(element)
820
+ );
821
+ const atEnd = fatal.reasonCode === "UnterminatedJsxContent" || fatal.pos >= text2.length;
822
+ const hints = (atEnd ? open : finished.first === CLOSING_TAG ? open.slice(0, 1) : []).map(
823
+ unclosed
824
+ );
825
+ const explained = [...problems, ...hints].some(({ code }) => code === "UnclosedJsxElement");
826
+ problems.push(...hints);
827
+ if (!(explained && fatal.reasonCode === "UnterminatedJsxContent"))
828
+ problems.push(problemOf(fatal));
829
+ return { tree: null, problems: problems.sort(byPlace) };
830
+ }
831
+ } catch {
832
+ }
833
+ return { tree: null, problems: [problemOf(fatal)] };
668
834
  }
669
835
 
670
836
  // src/check.ts
@@ -728,52 +894,43 @@ var quote = (text2) => {
728
894
  return flat.length > 48 ? `${flat.slice(0, 45)}...` : flat;
729
895
  };
730
896
  var MAX_SYNTAX_ERRORS = 10;
731
- function checkSource(typescript, contract2, file, text2) {
732
- const ts = typescript;
733
- const kind = /\.tsx$/.test(file) ? ts.ScriptKind.TSX : ts.ScriptKind.JSX;
734
- const source = ts.createSourceFile(file, text2, ts.ScriptTarget.Latest, true, kind);
735
- const parseDiagnostics = source.parseDiagnostics;
736
- if (!Array.isArray(parseDiagnostics))
737
- throw new CliError(
738
- `This TypeScript does not report syntax errors on a parsed file, so a file that does not parse could be called clean. Install TypeScript 5 or 6 in this project (${INSTALL_TYPESCRIPT}).`,
739
- 1,
740
- { code: "ERR_TYPESCRIPT_UNUSABLE" }
741
- );
742
- if (parseDiagnostics.length > 0) {
743
- const ordered = [...parseDiagnostics].sort((a, b) => (a.start ?? 0) - (b.start ?? 0));
744
- const shown = ordered.slice(0, MAX_SYNTAX_ERRORS);
897
+ function checkSource(contract2, file, text2) {
898
+ const reading = readSyntax(file, text2);
899
+ if (reading.tree === null) {
900
+ const shown = reading.problems.slice(0, MAX_SYNTAX_ERRORS);
901
+ const more = reading.problems.length - shown.length;
745
902
  return {
746
903
  file,
747
904
  status: "not_evaluated",
748
905
  limitations: [],
749
- findings: shown.map((diagnostic, index) => {
750
- const { line, character } = source.getLineAndCharacterOfPosition(diagnostic.start ?? 0);
751
- const more = ordered.length - shown.length;
752
- return {
906
+ findings: shown.map(
907
+ ({ line, column, message, code }, index) => ({
753
908
  code: "SYNTAX_ERROR",
754
909
  severity: "error",
755
910
  file,
756
- line: line + 1,
757
- column: character + 1,
758
- message: `${ts.flattenDiagnosticMessageText(diagnostic.messageText, " ")} (TS${diagnostic.code})` + (index === shown.length - 1 && more > 0 ? ` ${more} more syntax error${more === 1 ? "" : "s"} in this file not shown.` : "")
759
- };
760
- })
911
+ line,
912
+ column,
913
+ message: `${message} (${code})` + (index === shown.length - 1 && more > 0 ? ` ${more} more syntax error${more === 1 ? "" : "s"} in this file not shown.` : "")
914
+ })
915
+ )
761
916
  };
762
917
  }
763
918
  const findings = [];
764
919
  const limitations = [];
920
+ const place = (node) => ({
921
+ line: node.loc?.start.line ?? 1,
922
+ column: (node.loc?.start.column ?? 0) + 1
923
+ });
924
+ const textOf = (node) => text2.slice(node.start ?? 0, node.end ?? 0);
765
925
  const limit = (node, limitation) => {
766
- const { line, character } = source.getLineAndCharacterOfPosition(node.getStart(source));
767
- limitations.push({ ...limitation, file, line: line + 1, column: character + 1 });
926
+ limitations.push({ ...limitation, file, ...place(node) });
768
927
  };
769
928
  const report = (node, finding) => {
770
- const { line, character } = source.getLineAndCharacterOfPosition(node.getStart(source));
771
929
  findings.push({
772
930
  code: finding.code,
773
931
  severity: finding.severity,
774
932
  file,
775
- line: line + 1,
776
- column: character + 1,
933
+ ...place(node),
777
934
  ...finding.component === void 0 ? {} : { component: finding.component },
778
935
  ...finding.prop === void 0 ? {} : { prop: finding.prop },
779
936
  message: finding.message,
@@ -784,21 +941,18 @@ function checkSource(typescript, contract2, file, text2) {
784
941
  const isComponent = (name) => Object.hasOwn(contract2.components, name);
785
942
  const named = /* @__PURE__ */ new Map();
786
943
  const namespaces = /* @__PURE__ */ new Set();
787
- for (const statement of source.statements) {
788
- if (!ts.isImportDeclaration(statement) || !ts.isStringLiteral(statement.moduleSpecifier))
789
- continue;
790
- const from = statement.moduleSpecifier.text;
791
- const clause = statement.importClause;
792
- if (!clause || clause.isTypeOnly || !clause.namedBindings) continue;
944
+ for (const statement of reading.tree.program.body) {
945
+ if (statement.type !== "ImportDeclaration" || statement.importKind === "type") continue;
946
+ const from = statement.source.value;
793
947
  const copied = from !== PACKAGE && /[./@]/.test(from[0] ?? "") && isComponent(basename(from));
794
948
  if (from !== PACKAGE && !copied) continue;
795
- if (ts.isNamespaceImport(clause.namedBindings)) {
796
- if (from === PACKAGE) namespaces.add(clause.namedBindings.name.text);
797
- continue;
798
- }
799
- for (const element2 of clause.namedBindings.elements) {
800
- if (element2.isTypeOnly) continue;
801
- const exported = (element2.propertyName ?? element2.name).text;
949
+ for (const element2 of statement.specifiers) {
950
+ if (element2.type === "ImportNamespaceSpecifier") {
951
+ if (from === PACKAGE) namespaces.add(element2.local.name);
952
+ continue;
953
+ }
954
+ if (element2.type !== "ImportSpecifier" || element2.importKind === "type") continue;
955
+ const exported = element2.imported.type === "Identifier" ? element2.imported.name : element2.imported.value;
802
956
  if (from === PACKAGE && !contract2.exports.includes(exported)) {
803
957
  let suggestions = nearest(exported, Object.keys(contract2.components));
804
958
  if (suggestions.length === 0) suggestions = nearest(exported, contract2.exports);
@@ -823,51 +977,55 @@ function checkSource(typescript, contract2, file, text2) {
823
977
  });
824
978
  continue;
825
979
  }
826
- if (isComponent(exported)) named.set(element2.name.text, exported);
980
+ if (isComponent(exported)) named.set(element2.local.name, exported);
827
981
  }
828
982
  }
983
+ const isString = (node) => node.type === "StringLiteral";
984
+ const plainTemplate = (node) => node.type === "TemplateLiteral" && node.expressions.length === 0 ? node.quasis[0]?.value.cooked ?? node.quasis[0]?.value.raw : void 0;
829
985
  const literals = (expression) => {
830
986
  if (!expression) return [];
831
- if (ts.isStringLiteral(expression) || ts.isNoSubstitutionTemplateLiteral(expression))
832
- return [expression.text];
833
- if (ts.isParenthesizedExpression(expression)) return literals(expression.expression);
834
- if (ts.isConditionalExpression(expression))
835
- return [...literals(expression.whenTrue), ...literals(expression.whenFalse)];
987
+ if (isString(expression)) return [expression.value];
988
+ const template = plainTemplate(expression);
989
+ if (template !== void 0) return [template];
990
+ if (expression.type === "ConditionalExpression")
991
+ return [...literals(expression.consequent), ...literals(expression.alternate)];
836
992
  return [];
837
993
  };
838
- const valueOf = (attribute) => attribute.initializer === void 0 ? void 0 : ts.isJsxExpression(attribute.initializer) ? attribute.initializer.expression : attribute.initializer;
994
+ const valueOf = (attribute) => {
995
+ const value = attribute.value;
996
+ if (value === null || value === void 0) return void 0;
997
+ if (value.type === "JSXExpressionContainer")
998
+ return value.expression.type === "JSXEmptyExpression" ? void 0 : value.expression;
999
+ return value;
1000
+ };
839
1001
  const optionValue = (expression) => {
840
1002
  const read = [];
841
1003
  const unread = [];
842
- const walk = (node) => {
843
- if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node))
844
- read.push(node.text);
845
- else if (ts.isParenthesizedExpression(node) || ts.isAsExpression(node) || ts.isNonNullExpression(node) || ts.isTypeAssertionExpression(node) || typeof ts.isSatisfiesExpression === "function" && ts.isSatisfiesExpression(node))
846
- walk(node.expression);
847
- else if (ts.isConditionalExpression(node)) {
848
- walk(node.whenTrue);
849
- walk(node.whenFalse);
850
- } else if (ts.isBinaryExpression(node)) {
851
- const operator = node.operatorToken.kind;
852
- if (operator === ts.SyntaxKind.QuestionQuestionToken || operator === ts.SyntaxKind.BarBarToken) {
853
- walk(node.left);
854
- walk(node.right);
855
- } else if (operator === ts.SyntaxKind.AmpersandAmpersandToken) walk(node.right);
856
- else unread.push(node);
1004
+ const walk2 = (node) => {
1005
+ const template = plainTemplate(node);
1006
+ if (isString(node)) read.push(node.value);
1007
+ else if (template !== void 0) read.push(template);
1008
+ else if (node.type === "TSAsExpression" || node.type === "TSNonNullExpression" || node.type === "TSTypeAssertion" || node.type === "TSSatisfiesExpression")
1009
+ walk2(node.expression);
1010
+ else if (node.type === "ConditionalExpression") {
1011
+ walk2(node.consequent);
1012
+ walk2(node.alternate);
1013
+ } else if (node.type === "LogicalExpression") {
1014
+ if (node.operator === "??" || node.operator === "||") {
1015
+ walk2(node.left);
1016
+ walk2(node.right);
1017
+ } else walk2(node.right);
857
1018
  } else if (!// No value: the component's default applies.
858
- (ts.isIdentifier(node) && node.text === "undefined" || node.kind === ts.SyntaxKind.NullKeyword))
1019
+ (node.type === "Identifier" && node.name === "undefined" || node.type === "NullLiteral"))
859
1020
  unread.push(node);
860
1021
  };
861
- if (expression) walk(expression);
1022
+ if (expression) walk2(expression);
862
1023
  return { literals: read, unread };
863
1024
  };
864
1025
  const classText = (expression) => {
865
1026
  if (!expression) return "";
866
- if (ts.isTemplateExpression(expression))
867
- return [
868
- expression.head.text,
869
- ...expression.templateSpans.map((span) => span.literal.text)
870
- ].join(" ");
1027
+ if (expression.type === "TemplateLiteral" && expression.expressions.length > 0)
1028
+ return expression.quasis.map((span) => span.value.cooked ?? span.value.raw).join(" ");
871
1029
  return literals(expression).join(" ");
872
1030
  };
873
1031
  const houseRules = (attribute, name) => {
@@ -893,9 +1051,9 @@ function checkSource(typescript, contract2, file, text2) {
893
1051
  message: "A rounded corner. The radius is 0 everywhere in OPERATOR."
894
1052
  });
895
1053
  }
896
- if (name === "style" && value && ts.isObjectLiteralExpression(value)) {
1054
+ if (name === "style" && value && value.type === "ObjectExpression") {
897
1055
  for (const property of value.properties)
898
- if (ts.isPropertyAssignment(property) && literals(property.initializer).some((literal) => RAW_COLOR_VALUE.test(literal)))
1056
+ if (property.type === "ObjectProperty" && literals(property.value).some((literal) => RAW_COLOR_VALUE.test(literal)))
899
1057
  report(attribute, {
900
1058
  code: "RAW_COLOR",
901
1059
  severity: "warning",
@@ -903,47 +1061,49 @@ function checkSource(typescript, contract2, file, text2) {
903
1061
  });
904
1062
  }
905
1063
  };
1064
+ const attributeName = (attribute) => attribute.name.type === "JSXIdentifier" ? attribute.name.name : textOf(attribute.name);
906
1065
  const element = (opening, ancestors, parent, hasChildren, renderedAs) => {
907
- const tag = opening.tagName;
1066
+ const tag = opening.name;
908
1067
  let component = null;
909
- if (ts.isIdentifier(tag)) {
910
- component = named.get(tag.text) ?? null;
911
- if (component === null && !renderedAs && Object.hasOwn(NATIVE, tag.text))
1068
+ if (tag.type === "JSXIdentifier") {
1069
+ component = named.get(tag.name) ?? null;
1070
+ if (component === null && !renderedAs && Object.hasOwn(NATIVE, tag.name))
912
1071
  report(opening, {
913
1072
  code: "NATIVE_CONTROL",
914
1073
  severity: "warning",
915
- message: `A native <${tag.text}>: use ${NATIVE[tag.text]}.`
1074
+ message: `A native <${tag.name}>: use ${NATIVE[tag.name]}.`
916
1075
  });
917
- } else if (ts.isPropertyAccessExpression(tag) && ts.isIdentifier(tag.expression)) {
918
- const owner = tag.expression.text;
919
- if (namespaces.has(owner) && isComponent(tag.name.text)) component = tag.name.text;
1076
+ } else if (tag.type === "JSXMemberExpression" && tag.object.type === "JSXIdentifier") {
1077
+ const owner = tag.object.name;
1078
+ const member = tag.property.name;
1079
+ if (namespaces.has(owner) && isComponent(member)) component = member;
920
1080
  else if (named.has(owner)) {
921
- const joined = `${named.get(owner)}${tag.name.text}`;
1081
+ const joined = `${named.get(owner)}${member}`;
922
1082
  report(opening, {
923
1083
  code: "DOT_NOTATION",
924
1084
  severity: "error",
925
- message: `${owner}.${tag.name.text} is not a component.` + (isComponent(joined) ? ` Use <${joined}>.` : " OPERATOR's parts are components of their own, imported by name.")
1085
+ message: `${owner}.${member} is not a component.` + (isComponent(joined) ? ` Use <${joined}>.` : " OPERATOR's parts are components of their own, imported by name.")
926
1086
  });
927
1087
  }
928
1088
  }
929
- const attributes = opening.attributes.properties;
1089
+ const attributes = opening.attributes;
930
1090
  for (const attribute of attributes)
931
- if (ts.isJsxAttribute(attribute) && ts.isIdentifier(attribute.name))
932
- houseRules(attribute, attribute.name.text);
1091
+ if (attribute.type === "JSXAttribute" && attribute.name.type === "JSXIdentifier")
1092
+ houseRules(attribute, attribute.name.name);
933
1093
  if (component === null) return null;
934
1094
  const definition = contract2.components[component];
935
- const spread = attributes.some((attribute) => ts.isJsxSpreadAttribute(attribute));
1095
+ const spread = attributes.some((attribute) => attribute.type === "JSXSpreadAttribute");
936
1096
  for (const attribute of attributes)
937
- if (ts.isJsxSpreadAttribute(attribute))
1097
+ if (attribute.type === "JSXSpreadAttribute")
938
1098
  limit(attribute, {
939
1099
  code: "UNRESOLVED_SPREAD",
940
1100
  component,
941
- message: `Props spread into <${component}> (${quote(attribute.getText(source))}) were not read: option values and required props that arrive through them were not checked.`
1101
+ message: `Props spread into <${component}> (${quote(textOf(attribute))}) were not read: option values and required props that arrive through them were not checked.`
942
1102
  });
943
1103
  const given = /* @__PURE__ */ new Set();
944
1104
  for (const attribute of attributes) {
945
- if (!ts.isJsxAttribute(attribute)) continue;
946
- const prop = ts.isIdentifier(attribute.name) ? attribute.name.text : attribute.name.getText(source);
1105
+ if (attribute.type !== "JSXAttribute") continue;
1106
+ const prop = attributeName(attribute);
947
1107
  given.add(prop);
948
1108
  const known = Object.hasOwn(definition.props, prop) ? definition.props[prop] : void 0;
949
1109
  if (known === void 0) {
@@ -965,7 +1125,7 @@ function checkSource(typescript, contract2, file, text2) {
965
1125
  code: "UNRESOLVED_VALUE",
966
1126
  component,
967
1127
  prop,
968
- message: `${component} ${prop} is set from an expression (${quote(expression.getText(source))}) whose value is not in the source, so it was not compared with the options.`
1128
+ message: `${component} ${prop} is set from an expression (${quote(textOf(expression))}) whose value is not in the source, so it was not compared with the options.`
969
1129
  });
970
1130
  for (const literal of options) {
971
1131
  if (known.values.includes(literal)) continue;
@@ -1014,35 +1174,34 @@ function checkSource(typescript, contract2, file, text2) {
1014
1174
  return component;
1015
1175
  };
1016
1176
  const visit = (node, ancestors, parent, renderedAs = false) => {
1017
- if (ts.isJsxElement(node) || ts.isJsxSelfClosingElement(node)) {
1018
- const opening = ts.isJsxElement(node) ? node.openingElement : node;
1019
- const hasChildren = ts.isJsxElement(node) && node.children.some(
1020
- (child) => !(ts.isJsxText(child) && child.containsOnlyTriviaWhiteSpaces)
1177
+ if (node.type === "JSXElement") {
1178
+ const opening = node.openingElement;
1179
+ const hasChildren = node.children.some(
1180
+ (child) => !(child.type === "JSXText" && /^\s*$/.test(text2.slice(child.start ?? 0, child.end ?? 0)))
1021
1181
  );
1022
1182
  const component = element(opening, ancestors, parent, hasChildren, renderedAs);
1023
- const asChild = component !== null && opening.attributes.properties.some(
1024
- (attribute) => ts.isJsxAttribute(attribute) && attribute.name.getText(source) === "asChild"
1183
+ const asChild = component !== null && opening.attributes.some(
1184
+ (attribute) => attribute.type === "JSXAttribute" && attributeName(attribute) === "asChild"
1025
1185
  );
1026
- for (const attribute of opening.attributes.properties) visit(attribute, [], null);
1027
- if (ts.isJsxElement(node))
1028
- for (const child of node.children)
1029
- visit(
1030
- child,
1031
- component === null ? ancestors : [...ancestors, component],
1032
- component,
1033
- asChild
1034
- );
1186
+ for (const attribute of opening.attributes) visit(attribute, [], null);
1187
+ for (const child of node.children)
1188
+ visit(
1189
+ child,
1190
+ component === null ? ancestors : [...ancestors, component],
1191
+ component,
1192
+ asChild
1193
+ );
1035
1194
  return;
1036
1195
  }
1037
- ts.forEachChild(node, (child) => visit(child, ancestors, parent));
1196
+ for (const child of childNodes(node)) visit(child, ancestors, parent);
1038
1197
  };
1039
- visit(source, [], null);
1040
- const byPlace = (a, b) => a.line - b.line || a.column - b.column;
1198
+ visit(reading.tree.program, [], null);
1199
+ const byPlace2 = (a, b) => a.line - b.line || a.column - b.column;
1041
1200
  return {
1042
1201
  file,
1043
1202
  status: limitations.length > 0 ? "partial" : "evaluated",
1044
- findings: findings.sort(byPlace),
1045
- limitations: limitations.sort(byPlace)
1203
+ findings: findings.sort(byPlace2),
1204
+ limitations: limitations.sort(byPlace2)
1046
1205
  };
1047
1206
  }
1048
1207
  var SKIPPED = /* @__PURE__ */ new Set(["node_modules", "dist", "build", "out", "coverage"]);
@@ -1052,7 +1211,7 @@ var excluded = (name) => SKIPPED.has(name) || name.startsWith(".");
1052
1211
  function* sources(path) {
1053
1212
  if (excluded(basename(path))) return;
1054
1213
  if (statSync(path).isDirectory()) {
1055
- for (const entry of readdirSync(path).sort()) yield* sources(join3(path, entry));
1214
+ for (const entry of readdirSync(path).sort()) yield* sources(join2(path, entry));
1056
1215
  } else if (READ.has(extname(path)) && !NOT_INTERFACE.test(basename(path))) yield path;
1057
1216
  }
1058
1217
  function whyNotRead(path) {
@@ -1062,7 +1221,7 @@ function whyNotRead(path) {
1062
1221
  if (!READ.has(extname(path))) return "unsupported_extension";
1063
1222
  return NOT_INTERFACE.test(name) ? "test_or_story_file" : null;
1064
1223
  }
1065
- function buildCheckReport(evaluations, context) {
1224
+ function buildCheckReport(evaluations, context = {}) {
1066
1225
  const findings = evaluations.flatMap(({ findings: found }) => found);
1067
1226
  const count = (status2) => evaluations.filter((evaluation) => evaluation.status === status2).length;
1068
1227
  const partial = count("partial");
@@ -1089,7 +1248,7 @@ function buildCheckReport(evaluations, context) {
1089
1248
  limitations: evaluations.flatMap(({ limitations }) => limitations),
1090
1249
  skipped_inputs: context.skipped ?? [],
1091
1250
  scope: { checked: [...SCOPE.checked], not_checked: [...SCOPE.not_checked] },
1092
- parser: context.parser
1251
+ parser: context.parser ?? PARSER
1093
1252
  };
1094
1253
  }
1095
1254
  function checkExitCode(report, { strict }) {
@@ -1097,29 +1256,6 @@ function checkExitCode(report, { strict }) {
1097
1256
  if (report.summary.errors > 0 || report.coverage.files_not_evaluated > 0) return 1;
1098
1257
  return strict && report.summary.warnings > 0 ? 1 : 0;
1099
1258
  }
1100
- var describeProvided = (typescript) => ({
1101
- name: "typescript",
1102
- version: typescript.version ?? "unknown",
1103
- source: "provided"
1104
- });
1105
- function resolveParser(cwd, provided, who) {
1106
- if (provided !== void 0 && provided !== null)
1107
- return { typescript: provided, parser: describeProvided(provided) };
1108
- const probe = provided === null ? null : probeTypeScript(cwd);
1109
- if (probe?.ready) {
1110
- const { name, version, source } = probe.resolution;
1111
- return { typescript: probe.typescript, parser: { name, version, source } };
1112
- }
1113
- if (probe !== null && probe.reason === "typescript_unusable")
1114
- throw new CliError(`${who} cannot use the TypeScript it found: ${probe.detail}`, 1, {
1115
- code: "ERR_TYPESCRIPT_UNUSABLE"
1116
- });
1117
- throw new CliError(
1118
- `${who} reads code with the project's TypeScript, and found none. Install typescript in this project (${INSTALL_TYPESCRIPT}) and run it again. It looked from ${cwd}.`,
1119
- 1,
1120
- { code: "ERR_TYPESCRIPT_MISSING" }
1121
- );
1122
- }
1123
1259
  function checkPaths(paths, context) {
1124
1260
  const root = resolve2(context.cwd);
1125
1261
  const files = [];
@@ -1132,16 +1268,15 @@ function checkPaths(paths, context) {
1132
1268
  if (reason !== null) skipped.push({ path, reason });
1133
1269
  files.push(...sources(absolute));
1134
1270
  }
1135
- const { typescript, parser } = resolveParser(root, context.typescript, "oprt check");
1271
+ loadBabel();
1136
1272
  const evaluations = [...new Set(files)].map(
1137
1273
  (file) => checkSource(
1138
- typescript,
1139
1274
  context.contract,
1140
1275
  relative2(root, file).split(sep2).join("/"),
1141
1276
  readFileSync3(file, "utf8")
1142
1277
  )
1143
1278
  );
1144
- return buildCheckReport(evaluations, { parser, skipped });
1279
+ return buildCheckReport(evaluations, { skipped });
1145
1280
  }
1146
1281
  var plural = (amount, word) => `${amount} ${word}${amount === 1 ? "" : "s"}`;
1147
1282
  var MAX_LIMITS_SHOWN = 20;
@@ -1183,7 +1318,7 @@ function renderCheckText(report) {
1183
1318
 
1184
1319
  // src/doctor.ts
1185
1320
  import { existsSync as existsSync3, readFileSync as readFileSync4 } from "fs";
1186
- import { dirname as dirname3, join as join4 } from "path";
1321
+ import { dirname as dirname3, join as join3 } from "path";
1187
1322
 
1188
1323
  // src/agent-block.ts
1189
1324
  var START = "<!-- OPERATOR:START -->";
@@ -1265,10 +1400,10 @@ var INSTRUCTION_FILES = [
1265
1400
  ".claude/CLAUDE.md",
1266
1401
  ".cursor/rules/operator.mdc"
1267
1402
  ];
1268
- var minor = (version) => version.split(".").slice(0, 2).join(".");
1403
+ var minor = (version2) => version2.split(".").slice(0, 2).join(".");
1269
1404
  function installedVersion(cwd, name) {
1270
1405
  for (let folder = cwd; ; folder = dirname3(folder)) {
1271
- const manifest = join4(folder, "node_modules", name, "package.json");
1406
+ const manifest = join3(folder, "node_modules", name, "package.json");
1272
1407
  if (existsSync3(manifest))
1273
1408
  return JSON.parse(readFileSync4(manifest, "utf8")).version;
1274
1409
  if (dirname3(folder) === folder) return void 0;
@@ -1277,36 +1412,36 @@ function installedVersion(cwd, name) {
1277
1412
  function doctor({
1278
1413
  cwd,
1279
1414
  contract: contract2,
1280
- parser = probeTypeScript(cwd)
1415
+ parser = probeParser()
1281
1416
  }) {
1282
1417
  const checks = [];
1283
1418
  for (const name of PACKAGES) {
1284
- const version = installedVersion(cwd, name);
1419
+ const version2 = installedVersion(cwd, name);
1285
1420
  const id = `package:${name}`;
1286
- if (version === void 0)
1421
+ if (version2 === void 0)
1287
1422
  checks.push({
1288
1423
  id,
1289
1424
  status: "warn",
1290
1425
  message: `${name} is not installed.`,
1291
1426
  fix: `Install ${name}, or copy the sources you need with oprt add.`
1292
1427
  });
1293
- else if (minor(version) !== minor(contract2.version))
1428
+ else if (minor(version2) !== minor(contract2.version))
1294
1429
  checks.push({
1295
1430
  id,
1296
1431
  status: "warn",
1297
- message: `${name} is ${version}; this installer describes ${contract2.version}.`,
1432
+ message: `${name} is ${version2}; this installer describes ${contract2.version}.`,
1298
1433
  fix: "Install matching versions of the OPERATOR packages and @oprt/cli."
1299
1434
  });
1300
- else checks.push({ id, status: "ok", message: `${name} ${version} is installed.` });
1435
+ else checks.push({ id, status: "ok", message: `${name} ${version2} is installed.` });
1301
1436
  }
1302
1437
  const written = INSTRUCTION_FILES.map((path) => {
1303
- const file = join4(cwd, path);
1438
+ const file = join3(cwd, path);
1304
1439
  return {
1305
1440
  path,
1306
1441
  version: existsSync3(file) ? blockVersion(readFileSync4(file, "utf8")) : void 0
1307
1442
  };
1308
- }).filter(({ version }) => version !== void 0);
1309
- const stale = written.find(({ version }) => version !== contract2.version);
1443
+ }).filter(({ version: version2 }) => version2 !== void 0);
1444
+ const stale = written.find(({ version: version2 }) => version2 !== contract2.version);
1310
1445
  if (written.length === 0)
1311
1446
  checks.push({
1312
1447
  id: "agent-instructions",
@@ -1327,39 +1462,32 @@ function doctor({
1327
1462
  status: "ok",
1328
1463
  message: `${written.map(({ path }) => path).join(", ")} carries the OPERATOR block (${contract2.version}).`
1329
1464
  });
1330
- const install2 = `${INSTALL_TYPESCRIPT} (or your package manager's equivalent), then run oprt doctor again. Lookups work without it.`;
1331
1465
  let checkCode;
1332
1466
  if (parser.ready) {
1333
- const { version, resolved_from, source } = parser.resolution;
1467
+ const { name, version: version2 } = parser.parser;
1334
1468
  checkCode = {
1335
1469
  status: "ready",
1336
- message: `TypeScript ${version}, resolved from ${resolved_from}${source === "project" ? "" : " (beside oprt, not this project)"}.`,
1337
- typescript: { version, resolved_from, source }
1470
+ message: `${name} ${version2}, bundled with oprt: nothing to install.`,
1471
+ parser: parser.parser
1338
1472
  };
1339
- checks.push(
1340
- source === "project" ? {
1341
- id: "typescript",
1342
- status: "ok",
1343
- message: `TypeScript ${version} is installed in this project (${resolved_from}).`
1344
- } : {
1345
- id: "typescript",
1346
- status: "warn",
1347
- message: `TypeScript ${version} is not in this project; the one beside oprt is used (${resolved_from}).`,
1348
- fix: `${INSTALL_TYPESCRIPT}, so checking reads code with a version installed in the project.`
1349
- }
1350
- );
1473
+ checks.push({
1474
+ id: "parser",
1475
+ status: "ok",
1476
+ message: `Code checking reads code with ${name} ${version2}, which ships inside oprt.`
1477
+ });
1351
1478
  } else {
1479
+ const fix = "Install @oprt/cli again, then run oprt doctor again. Lookups work without it.";
1352
1480
  checkCode = {
1353
1481
  status: "blocked",
1354
1482
  message: `${parser.reason}: ${parser.detail}`,
1355
1483
  reason: parser.reason,
1356
- fix: install2
1484
+ fix
1357
1485
  };
1358
1486
  checks.push({
1359
- id: "typescript",
1487
+ id: "parser",
1360
1488
  status: "fail",
1361
1489
  message: `oprt check and the check_code tool cannot run (${parser.reason}): ${parser.detail}`,
1362
- fix: install2
1490
+ fix
1363
1491
  });
1364
1492
  }
1365
1493
  const count = (status) => checks.filter((check) => check.status === status).length;
@@ -1447,7 +1575,7 @@ var RESULT_TYPES = [
1447
1575
  "instructions"
1448
1576
  ];
1449
1577
  var envelope = (type, data, contract2) => ({
1450
- apiVersion: 1,
1578
+ apiVersion: RESULT_SCHEMA_VERSION,
1451
1579
  type,
1452
1580
  identity: identityOf(contract2),
1453
1581
  data
@@ -1461,7 +1589,7 @@ function errorEnvelope(error, contract2) {
1461
1589
  contract_digest
1462
1590
  }))(identityOf(contract2));
1463
1591
  return {
1464
- apiVersion: 1,
1592
+ apiVersion: RESULT_SCHEMA_VERSION,
1465
1593
  error: error instanceof Error ? error.message : String(error),
1466
1594
  code: known?.code ?? "ERR_FAILED",
1467
1595
  ...known?.suggestions === void 0 ? {} : { suggestions: known.suggestions },
@@ -1557,9 +1685,9 @@ var DEFS = {
1557
1685
  reason: words2("unsupported_extension", "test_or_story_file", "excluded_folder")
1558
1686
  }),
1559
1687
  parser: obj({
1560
- name: words2("typescript"),
1688
+ name: words2("@babel/parser"),
1561
1689
  version: str(),
1562
- source: words2("project", "installer", "provided")
1690
+ source: words2("bundled")
1563
1691
  }),
1564
1692
  check_data: obj({
1565
1693
  evaluation_status: words2(
@@ -1651,15 +1779,11 @@ var DEFS = {
1651
1779
  {
1652
1780
  status: words2("ready", "blocked"),
1653
1781
  message: str(),
1654
- reason: words2("typescript_missing", "typescript_unusable"),
1782
+ reason: words2("parser_missing", "parser_unusable"),
1655
1783
  fix: str(),
1656
- typescript: obj({
1657
- version: str(),
1658
- resolved_from: str(),
1659
- source: words2("project", "installer", "provided")
1660
- })
1784
+ parser: ref("parser")
1661
1785
  },
1662
- ["reason", "fix", "typescript"]
1786
+ ["reason", "fix", "parser"]
1663
1787
  )
1664
1788
  }),
1665
1789
  checks: arr(ref("doctor_check")),
@@ -1734,14 +1858,14 @@ var DATA = {
1734
1858
  instructions: "instructions_data"
1735
1859
  };
1736
1860
  var success = (type) => obj({
1737
- apiVersion: { const: 1 },
1861
+ apiVersion: { const: RESULT_SCHEMA_VERSION },
1738
1862
  type: { const: type },
1739
1863
  identity: ref("identity"),
1740
1864
  data: ref(DATA[type])
1741
1865
  });
1742
1866
  var failure = () => obj(
1743
1867
  {
1744
- apiVersion: { const: 1 },
1868
+ apiVersion: { const: RESULT_SCHEMA_VERSION },
1745
1869
  error: str(),
1746
1870
  code: str({ pattern: "^ERR_[A-Z_]+$" }),
1747
1871
  suggestions: names(),
@@ -2322,15 +2446,9 @@ function runTool(name, args, context) {
2322
2446
  structured: envelope("instructions", instructionsData(contract2), contract2)
2323
2447
  };
2324
2448
  case "check_code": {
2325
- const { typescript, parser } = resolveParser(
2326
- context.cwd ?? process.cwd(),
2327
- context.typescript,
2328
- "check_code"
2329
- );
2330
- const report = buildCheckReport(
2331
- [checkSource(typescript, contract2, "snippet.tsx", args["code"])],
2332
- { parser }
2333
- );
2449
+ const report = buildCheckReport([
2450
+ checkSource(contract2, "snippet.tsx", args["code"])
2451
+ ]);
2334
2452
  return { text: renderCheckText(report), structured: envelope("check", report, contract2) };
2335
2453
  }
2336
2454
  default:
@@ -2415,7 +2533,7 @@ function serve(input, output, context) {
2415
2533
  // src/index.ts
2416
2534
  function loadRegistry() {
2417
2535
  const here = dirname5(fileURLToPath3(import.meta.url));
2418
- const candidates = [join5(here, "registry.json"), join5(here, "../generated/registry.json")];
2536
+ const candidates = [join4(here, "registry.json"), join4(here, "../generated/registry.json")];
2419
2537
  const found = candidates.find((candidate) => existsSync5(candidate));
2420
2538
  if (found === void 0) {
2421
2539
  throw new Error(
@@ -2425,8 +2543,8 @@ function loadRegistry() {
2425
2543
  return JSON.parse(readFileSync6(found, "utf8"));
2426
2544
  }
2427
2545
  var registry = loadRegistry();
2428
- var loaded;
2429
- var contract = () => loaded ??= loadContract();
2546
+ var loaded2;
2547
+ var contract = () => loaded2 ??= loadContract();
2430
2548
  function install(options, context = {}) {
2431
2549
  return installSources(options, {
2432
2550
  cwd: context.cwd ?? process.cwd(),