@aiready/core 0.7.13 → 0.8.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.mjs CHANGED
@@ -1,3 +1,34 @@
1
+ // src/types/language.ts
2
+ var Language = /* @__PURE__ */ ((Language3) => {
3
+ Language3["TypeScript"] = "typescript";
4
+ Language3["JavaScript"] = "javascript";
5
+ Language3["Python"] = "python";
6
+ Language3["Java"] = "java";
7
+ Language3["Go"] = "go";
8
+ Language3["Rust"] = "rust";
9
+ Language3["CSharp"] = "csharp";
10
+ return Language3;
11
+ })(Language || {});
12
+ var LANGUAGE_EXTENSIONS = {
13
+ ".ts": "typescript" /* TypeScript */,
14
+ ".tsx": "typescript" /* TypeScript */,
15
+ ".js": "javascript" /* JavaScript */,
16
+ ".jsx": "javascript" /* JavaScript */,
17
+ ".py": "python" /* Python */,
18
+ ".java": "java" /* Java */,
19
+ ".go": "go" /* Go */,
20
+ ".rs": "rust" /* Rust */,
21
+ ".cs": "csharp" /* CSharp */
22
+ };
23
+ var ParseError = class extends Error {
24
+ constructor(message, filePath, loc) {
25
+ super(message);
26
+ this.filePath = filePath;
27
+ this.loc = loc;
28
+ this.name = "ParseError";
29
+ }
30
+ };
31
+
1
32
  // src/utils/file-scanner.ts
2
33
  import { glob } from "glob";
3
34
  import { readFile } from "fs/promises";
@@ -51,8 +82,8 @@ var DEFAULT_EXCLUDE = [
51
82
  async function scanFiles(options) {
52
83
  const {
53
84
  rootDir,
54
- include = ["**/*.{ts,tsx,js,jsx,py,java}"],
55
- // Broad default - tools should filter further
85
+ include = ["**/*.{ts,tsx,js,jsx,py,java,go,rs,cs}"],
86
+ // Multi-language support
56
87
  exclude
57
88
  } = options;
58
89
  const ignoreFilePath = join(rootDir || ".", ".aireadyignore");
@@ -522,10 +553,496 @@ function formatToolScore(output) {
522
553
  }
523
554
  return result;
524
555
  }
556
+
557
+ // src/parsers/typescript-parser.ts
558
+ import { parse as parse2 } from "@typescript-eslint/typescript-estree";
559
+ var TypeScriptParser = class {
560
+ constructor() {
561
+ this.language = "typescript" /* TypeScript */;
562
+ this.extensions = [".ts", ".tsx", ".js", ".jsx"];
563
+ }
564
+ parse(code, filePath) {
565
+ try {
566
+ const isJavaScript = filePath.match(/\.jsx?$/i);
567
+ const ast = parse2(code, {
568
+ loc: true,
569
+ range: true,
570
+ jsx: filePath.match(/\.[jt]sx$/i) !== null,
571
+ filePath,
572
+ sourceType: "module",
573
+ ecmaVersion: "latest"
574
+ });
575
+ const imports = this.extractImports(ast);
576
+ const exports = this.extractExports(ast, imports);
577
+ return {
578
+ exports,
579
+ imports,
580
+ language: isJavaScript ? "javascript" /* JavaScript */ : "typescript" /* TypeScript */,
581
+ warnings: []
582
+ };
583
+ } catch (error) {
584
+ const err = error;
585
+ throw new ParseError(
586
+ `Failed to parse ${filePath}: ${err.message}`,
587
+ filePath
588
+ );
589
+ }
590
+ }
591
+ getNamingConventions() {
592
+ return {
593
+ // camelCase for variables and functions
594
+ variablePattern: /^[a-z][a-zA-Z0-9]*$/,
595
+ functionPattern: /^[a-z][a-zA-Z0-9]*$/,
596
+ // PascalCase for classes
597
+ classPattern: /^[A-Z][a-zA-Z0-9]*$/,
598
+ // UPPER_CASE for constants
599
+ constantPattern: /^[A-Z][A-Z0-9_]*$/,
600
+ // Common exceptions (React hooks, etc.)
601
+ exceptions: ["__filename", "__dirname", "__esModule"]
602
+ };
603
+ }
604
+ canHandle(filePath) {
605
+ return this.extensions.some((ext) => filePath.toLowerCase().endsWith(ext));
606
+ }
607
+ extractImports(ast) {
608
+ const imports = [];
609
+ for (const node of ast.body) {
610
+ if (node.type === "ImportDeclaration") {
611
+ const specifiers = [];
612
+ let isTypeOnly = false;
613
+ if (node.importKind === "type") {
614
+ isTypeOnly = true;
615
+ }
616
+ for (const spec of node.specifiers) {
617
+ if (spec.type === "ImportSpecifier") {
618
+ const imported = spec.imported;
619
+ const name = imported.type === "Identifier" ? imported.name : imported.value;
620
+ specifiers.push(name);
621
+ } else if (spec.type === "ImportDefaultSpecifier") {
622
+ specifiers.push("default");
623
+ } else if (spec.type === "ImportNamespaceSpecifier") {
624
+ specifiers.push("*");
625
+ }
626
+ }
627
+ imports.push({
628
+ source: node.source.value,
629
+ specifiers,
630
+ isTypeOnly,
631
+ loc: node.loc ? {
632
+ start: { line: node.loc.start.line, column: node.loc.start.column },
633
+ end: { line: node.loc.end.line, column: node.loc.end.column }
634
+ } : void 0
635
+ });
636
+ }
637
+ }
638
+ return imports;
639
+ }
640
+ extractExports(ast, imports) {
641
+ const exports = [];
642
+ const importedNames = new Set(
643
+ imports.flatMap((imp) => imp.specifiers.filter((s) => s !== "*" && s !== "default"))
644
+ );
645
+ for (const node of ast.body) {
646
+ if (node.type === "ExportNamedDeclaration" && node.declaration) {
647
+ const extracted = this.extractFromDeclaration(node.declaration, importedNames);
648
+ exports.push(...extracted);
649
+ } else if (node.type === "ExportDefaultDeclaration") {
650
+ let name = "default";
651
+ let type = "default";
652
+ if (node.declaration.type === "FunctionDeclaration" && node.declaration.id) {
653
+ name = node.declaration.id.name;
654
+ type = "function";
655
+ } else if (node.declaration.type === "ClassDeclaration" && node.declaration.id) {
656
+ name = node.declaration.id.name;
657
+ type = "class";
658
+ }
659
+ exports.push({
660
+ name,
661
+ type,
662
+ loc: node.loc ? {
663
+ start: { line: node.loc.start.line, column: node.loc.start.column },
664
+ end: { line: node.loc.end.line, column: node.loc.end.column }
665
+ } : void 0
666
+ });
667
+ }
668
+ }
669
+ return exports;
670
+ }
671
+ extractFromDeclaration(declaration, importedNames) {
672
+ const exports = [];
673
+ if (declaration.type === "FunctionDeclaration" && declaration.id) {
674
+ exports.push({
675
+ name: declaration.id.name,
676
+ type: "function",
677
+ parameters: declaration.params.map(
678
+ (p) => p.type === "Identifier" ? p.name : "unknown"
679
+ ),
680
+ loc: declaration.loc ? {
681
+ start: {
682
+ line: declaration.loc.start.line,
683
+ column: declaration.loc.start.column
684
+ },
685
+ end: { line: declaration.loc.end.line, column: declaration.loc.end.column }
686
+ } : void 0
687
+ });
688
+ } else if (declaration.type === "ClassDeclaration" && declaration.id) {
689
+ exports.push({
690
+ name: declaration.id.name,
691
+ type: "class",
692
+ loc: declaration.loc ? {
693
+ start: {
694
+ line: declaration.loc.start.line,
695
+ column: declaration.loc.start.column
696
+ },
697
+ end: { line: declaration.loc.end.line, column: declaration.loc.end.column }
698
+ } : void 0
699
+ });
700
+ } else if (declaration.type === "VariableDeclaration") {
701
+ for (const declarator of declaration.declarations) {
702
+ if (declarator.id.type === "Identifier") {
703
+ exports.push({
704
+ name: declarator.id.name,
705
+ type: "const",
706
+ loc: declarator.loc ? {
707
+ start: {
708
+ line: declarator.loc.start.line,
709
+ column: declarator.loc.start.column
710
+ },
711
+ end: {
712
+ line: declarator.loc.end.line,
713
+ column: declarator.loc.end.column
714
+ }
715
+ } : void 0
716
+ });
717
+ }
718
+ }
719
+ } else if (declaration.type === "TSTypeAliasDeclaration") {
720
+ exports.push({
721
+ name: declaration.id.name,
722
+ type: "type",
723
+ loc: declaration.loc ? {
724
+ start: {
725
+ line: declaration.loc.start.line,
726
+ column: declaration.loc.start.column
727
+ },
728
+ end: { line: declaration.loc.end.line, column: declaration.loc.end.column }
729
+ } : void 0
730
+ });
731
+ } else if (declaration.type === "TSInterfaceDeclaration") {
732
+ exports.push({
733
+ name: declaration.id.name,
734
+ type: "interface",
735
+ loc: declaration.loc ? {
736
+ start: {
737
+ line: declaration.loc.start.line,
738
+ column: declaration.loc.start.column
739
+ },
740
+ end: { line: declaration.loc.end.line, column: declaration.loc.end.column }
741
+ } : void 0
742
+ });
743
+ }
744
+ return exports;
745
+ }
746
+ };
747
+
748
+ // src/parsers/python-parser.ts
749
+ var PythonParser = class {
750
+ constructor() {
751
+ this.language = "python" /* Python */;
752
+ this.extensions = [".py"];
753
+ this.parser = null;
754
+ this.initialized = false;
755
+ }
756
+ /**
757
+ * Initialize the tree-sitter parser
758
+ * This is async because tree-sitter WASM needs to be loaded
759
+ */
760
+ async initialize() {
761
+ if (this.initialized) return;
762
+ try {
763
+ this.initialized = true;
764
+ } catch (error) {
765
+ throw new Error(`Failed to initialize Python parser: ${error.message}`);
766
+ }
767
+ }
768
+ parse(code, filePath) {
769
+ try {
770
+ const imports = this.extractImportsRegex(code, filePath);
771
+ const exports = this.extractExportsRegex(code, filePath);
772
+ return {
773
+ exports,
774
+ imports,
775
+ language: "python" /* Python */,
776
+ warnings: ["Python parsing is currently using regex-based extraction. Tree-sitter support coming soon."]
777
+ };
778
+ } catch (error) {
779
+ throw new ParseError(
780
+ `Failed to parse Python file ${filePath}: ${error.message}`,
781
+ filePath
782
+ );
783
+ }
784
+ }
785
+ getNamingConventions() {
786
+ return {
787
+ // snake_case for variables and functions
788
+ variablePattern: /^[a-z_][a-z0-9_]*$/,
789
+ functionPattern: /^[a-z_][a-z0-9_]*$/,
790
+ // PascalCase for classes
791
+ classPattern: /^[A-Z][a-zA-Z0-9]*$/,
792
+ // UPPER_CASE for constants
793
+ constantPattern: /^[A-Z][A-Z0-9_]*$/,
794
+ // Python special methods and common exceptions
795
+ exceptions: [
796
+ "__init__",
797
+ "__str__",
798
+ "__repr__",
799
+ "__name__",
800
+ "__main__",
801
+ "__file__",
802
+ "__doc__",
803
+ "__all__",
804
+ "__version__",
805
+ "__author__",
806
+ "__dict__",
807
+ "__class__",
808
+ "__module__",
809
+ "__bases__"
810
+ ]
811
+ };
812
+ }
813
+ canHandle(filePath) {
814
+ return filePath.toLowerCase().endsWith(".py");
815
+ }
816
+ /**
817
+ * Regex-based import extraction (temporary implementation)
818
+ */
819
+ extractImportsRegex(code, filePath) {
820
+ const imports = [];
821
+ const lines = code.split("\n");
822
+ const importRegex = /^\s*import\s+([a-zA-Z0-9_., ]+)/;
823
+ const fromImportRegex = /^\s*from\s+([a-zA-Z0-9_.]+)\s+import\s+(.+)/;
824
+ lines.forEach((line, idx) => {
825
+ if (line.trim().startsWith("#")) return;
826
+ const importMatch = line.match(importRegex);
827
+ if (importMatch) {
828
+ const modules = importMatch[1].split(",").map((m) => m.trim().split(" as ")[0]);
829
+ modules.forEach((module) => {
830
+ imports.push({
831
+ source: module,
832
+ specifiers: [module],
833
+ loc: {
834
+ start: { line: idx + 1, column: 0 },
835
+ end: { line: idx + 1, column: line.length }
836
+ }
837
+ });
838
+ });
839
+ return;
840
+ }
841
+ const fromMatch = line.match(fromImportRegex);
842
+ if (fromMatch) {
843
+ const module = fromMatch[1];
844
+ const imports_str = fromMatch[2];
845
+ if (imports_str.trim() === "*") {
846
+ imports.push({
847
+ source: module,
848
+ specifiers: ["*"],
849
+ loc: {
850
+ start: { line: idx + 1, column: 0 },
851
+ end: { line: idx + 1, column: line.length }
852
+ }
853
+ });
854
+ return;
855
+ }
856
+ const specifiers = imports_str.split(",").map((s) => s.trim().split(" as ")[0]);
857
+ imports.push({
858
+ source: module,
859
+ specifiers,
860
+ loc: {
861
+ start: { line: idx + 1, column: 0 },
862
+ end: { line: idx + 1, column: line.length }
863
+ }
864
+ });
865
+ }
866
+ });
867
+ return imports;
868
+ }
869
+ /**
870
+ * Regex-based export extraction (temporary implementation)
871
+ *
872
+ * Python doesn't have explicit exports like JavaScript.
873
+ * We extract:
874
+ * - Functions defined at module level (def)
875
+ * - Classes defined at module level (class)
876
+ * - Variables in __all__ list
877
+ */
878
+ extractExportsRegex(code, filePath) {
879
+ const exports = [];
880
+ const lines = code.split("\n");
881
+ const functionRegex = /^def\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*\(/;
882
+ const classRegex = /^class\s+([a-zA-Z_][a-zA-Z0-9_]*)/;
883
+ const allRegex = /__all__\s*=\s*\[([^\]]+)\]/;
884
+ let inClass = false;
885
+ let classIndent = 0;
886
+ lines.forEach((line, idx) => {
887
+ const indent = line.search(/\S/);
888
+ if (line.match(classRegex)) {
889
+ inClass = true;
890
+ classIndent = indent;
891
+ } else if (inClass && indent <= classIndent && line.trim()) {
892
+ inClass = false;
893
+ }
894
+ if (inClass) {
895
+ const classMatch = line.match(classRegex);
896
+ if (classMatch) {
897
+ exports.push({
898
+ name: classMatch[1],
899
+ type: "class",
900
+ loc: {
901
+ start: { line: idx + 1, column: indent },
902
+ end: { line: idx + 1, column: line.length }
903
+ }
904
+ });
905
+ }
906
+ return;
907
+ }
908
+ const funcMatch = line.match(functionRegex);
909
+ if (funcMatch && indent === 0) {
910
+ const name = funcMatch[1];
911
+ if (!name.startsWith("_") || name.startsWith("__")) {
912
+ exports.push({
913
+ name,
914
+ type: "function",
915
+ loc: {
916
+ start: { line: idx + 1, column: 0 },
917
+ end: { line: idx + 1, column: line.length }
918
+ }
919
+ });
920
+ }
921
+ }
922
+ const allMatch = line.match(allRegex);
923
+ if (allMatch) {
924
+ const names = allMatch[1].split(",").map((n) => n.trim().replace(/['"]/g, ""));
925
+ names.forEach((name) => {
926
+ if (name && !exports.find((e) => e.name === name)) {
927
+ exports.push({
928
+ name,
929
+ type: "variable",
930
+ loc: {
931
+ start: { line: idx + 1, column: 0 },
932
+ end: { line: idx + 1, column: line.length }
933
+ }
934
+ });
935
+ }
936
+ });
937
+ }
938
+ });
939
+ return exports;
940
+ }
941
+ };
942
+
943
+ // src/parsers/parser-factory.ts
944
+ var ParserFactory = class _ParserFactory {
945
+ constructor() {
946
+ this.parsers = /* @__PURE__ */ new Map();
947
+ this.extensionMap = new Map(
948
+ Object.entries(LANGUAGE_EXTENSIONS).map(([ext, lang]) => [ext, lang])
949
+ );
950
+ this.registerParser(new TypeScriptParser());
951
+ this.registerParser(new PythonParser());
952
+ }
953
+ /**
954
+ * Get singleton instance
955
+ */
956
+ static getInstance() {
957
+ if (!_ParserFactory.instance) {
958
+ _ParserFactory.instance = new _ParserFactory();
959
+ }
960
+ return _ParserFactory.instance;
961
+ }
962
+ /**
963
+ * Register a language parser
964
+ */
965
+ registerParser(parser) {
966
+ this.parsers.set(parser.language, parser);
967
+ parser.extensions.forEach((ext) => {
968
+ this.extensionMap.set(ext, parser.language);
969
+ });
970
+ }
971
+ /**
972
+ * Get parser for a specific language
973
+ */
974
+ getParserForLanguage(language) {
975
+ return this.parsers.get(language) || null;
976
+ }
977
+ /**
978
+ * Get parser for a file based on its extension
979
+ */
980
+ getParserForFile(filePath) {
981
+ const ext = this.getFileExtension(filePath);
982
+ const language = this.extensionMap.get(ext);
983
+ if (!language) {
984
+ return null;
985
+ }
986
+ return this.parsers.get(language) || null;
987
+ }
988
+ /**
989
+ * Check if a file is supported
990
+ */
991
+ isSupported(filePath) {
992
+ return this.getParserForFile(filePath) !== null;
993
+ }
994
+ /**
995
+ * Get all registered languages
996
+ */
997
+ getSupportedLanguages() {
998
+ return Array.from(this.parsers.keys());
999
+ }
1000
+ /**
1001
+ * Get all supported file extensions
1002
+ */
1003
+ getSupportedExtensions() {
1004
+ return Array.from(this.extensionMap.keys());
1005
+ }
1006
+ /**
1007
+ * Get language for a file
1008
+ */
1009
+ getLanguageForFile(filePath) {
1010
+ const ext = this.getFileExtension(filePath);
1011
+ return this.extensionMap.get(ext) || null;
1012
+ }
1013
+ /**
1014
+ * Extract file extension (with dot)
1015
+ */
1016
+ getFileExtension(filePath) {
1017
+ const match = filePath.match(/\.[^.]+$/);
1018
+ return match ? match[0].toLowerCase() : "";
1019
+ }
1020
+ /**
1021
+ * Reset factory (useful for testing)
1022
+ */
1023
+ static reset() {
1024
+ _ParserFactory.instance = new _ParserFactory();
1025
+ }
1026
+ };
1027
+ function getParser(filePath) {
1028
+ return ParserFactory.getInstance().getParserForFile(filePath);
1029
+ }
1030
+ function isFileSupported(filePath) {
1031
+ return ParserFactory.getInstance().isSupported(filePath);
1032
+ }
1033
+ function getSupportedLanguages() {
1034
+ return ParserFactory.getInstance().getSupportedLanguages();
1035
+ }
525
1036
  export {
526
1037
  DEFAULT_EXCLUDE,
527
1038
  DEFAULT_TOOL_WEIGHTS,
1039
+ LANGUAGE_EXTENSIONS,
1040
+ Language,
1041
+ ParseError,
1042
+ ParserFactory,
1043
+ PythonParser,
528
1044
  TOOL_NAME_MAP,
1045
+ TypeScriptParser,
529
1046
  calculateImportSimilarity,
530
1047
  calculateOverallScore,
531
1048
  estimateTokens,
@@ -535,11 +1052,14 @@ export {
535
1052
  formatToolScore,
536
1053
  getElapsedTime,
537
1054
  getFileExtension,
1055
+ getParser,
538
1056
  getRating,
539
1057
  getRatingDisplay,
1058
+ getSupportedLanguages,
540
1059
  getToolWeight,
541
1060
  handleCLIError,
542
1061
  handleJSONOutput,
1062
+ isFileSupported,
543
1063
  isSourceFile,
544
1064
  loadConfig,
545
1065
  loadMergedConfig,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiready/core",
3
- "version": "0.7.13",
3
+ "version": "0.8.0",
4
4
  "description": "Shared utilities for AIReady analysis tools",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",