@jay-framework/plugin-validator 0.20.0 → 0.21.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.
Files changed (2) hide show
  1. package/dist/index.js +146 -12
  2. package/package.json +6 -6
package/dist/index.js CHANGED
@@ -451,6 +451,14 @@ async function validateSchema(context, result) {
451
451
  location: "plugin.yaml",
452
452
  suggestion: 'Specify the exported member name from the module (e.g., "moodTracker")'
453
453
  });
454
+ } else {
455
+ validateHandlerRef(
456
+ contract.component,
457
+ `Contract "${contract.name}" component`,
458
+ `plugin.yaml contracts[${index}]`,
459
+ context,
460
+ result
461
+ );
454
462
  }
455
463
  });
456
464
  }
@@ -483,6 +491,24 @@ async function validateSchema(context, result) {
483
491
  suggestion: 'Specify prefix for dynamic contract names (e.g., "cms")'
484
492
  });
485
493
  }
494
+ if (config.component) {
495
+ validateHandlerRef(
496
+ config.component,
497
+ `dynamic_contracts[${prefix}] component`,
498
+ `plugin.yaml dynamic_contracts`,
499
+ context,
500
+ result
501
+ );
502
+ }
503
+ if (config.generator) {
504
+ validateHandlerRef(
505
+ config.generator,
506
+ `dynamic_contracts[${prefix}] generator`,
507
+ `plugin.yaml dynamic_contracts`,
508
+ context,
509
+ result
510
+ );
511
+ }
486
512
  }
487
513
  }
488
514
  if (!manifest.contracts && !manifest.dynamic_contracts) {
@@ -553,6 +579,23 @@ async function validateSchema(context, result) {
553
579
  });
554
580
  }
555
581
  }
582
+ if (manifest.actions) {
583
+ for (const entry of manifest.actions) {
584
+ const exportName = typeof entry === "string" ? entry : entry.name;
585
+ if (exportName) {
586
+ validateHandlerRef(
587
+ exportName,
588
+ `Action "${exportName}"`,
589
+ "plugin.yaml actions",
590
+ context,
591
+ result
592
+ );
593
+ }
594
+ }
595
+ }
596
+ if (manifest.init) {
597
+ validateHandlerRef(manifest.init, "Init handler", "plugin.yaml init", context, result);
598
+ }
556
599
  if (manifest.routes) {
557
600
  if (!Array.isArray(manifest.routes)) {
558
601
  result.errors.push({
@@ -584,6 +627,14 @@ async function validateSchema(context, result) {
584
627
  location: "plugin.yaml",
585
628
  suggestion: "Specify the exported member name for the page component"
586
629
  });
630
+ } else {
631
+ validateHandlerRef(
632
+ route.component,
633
+ `Route "${route.path}" component`,
634
+ `plugin.yaml routes`,
635
+ context,
636
+ result
637
+ );
587
638
  }
588
639
  if (route.jayHtml) {
589
640
  validateDocFile(
@@ -623,22 +674,105 @@ async function validateSchema(context, result) {
623
674
  suggestion: "Specify the relative path to the validator handler module"
624
675
  });
625
676
  }
626
- if (validator.handler && !context.isNpmPackage) {
627
- const handlerPath = path.join(context.pluginPath, validator.handler);
628
- const extensions = ["", ".ts", ".js", "/index.ts", "/index.js"];
629
- const found = extensions.some((ext) => fs.existsSync(handlerPath + ext));
630
- if (!found) {
631
- result.errors.push({
632
- type: "file-missing",
633
- message: `Validator "${validator.name}" handler not found: ${validator.handler}`,
634
- location: "plugin.yaml validators",
635
- suggestion: `Create the validator handler at ${handlerPath}.ts`
636
- });
637
- }
677
+ if (validator.handler) {
678
+ validateHandlerRef(
679
+ validator.handler,
680
+ `Validator "${validator.name}" handler`,
681
+ "plugin.yaml validators",
682
+ context,
683
+ result
684
+ );
638
685
  }
639
686
  });
640
687
  }
641
688
  }
689
+ if (manifest.setup) {
690
+ if (manifest.setup.handler) {
691
+ validateHandlerRef(
692
+ manifest.setup.handler,
693
+ "Setup handler",
694
+ "plugin.yaml setup.handler",
695
+ context,
696
+ result
697
+ );
698
+ }
699
+ if (manifest.setup.references) {
700
+ validateHandlerRef(
701
+ manifest.setup.references,
702
+ "References handler",
703
+ "plugin.yaml setup.references",
704
+ context,
705
+ result
706
+ );
707
+ }
708
+ }
709
+ }
710
+ function checkExportExists(exportName, context) {
711
+ const packageJsonPath = path.join(context.pluginPath, "package.json");
712
+ if (!fs.existsSync(packageJsonPath))
713
+ return true;
714
+ let mainPath;
715
+ try {
716
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
717
+ if (packageJson.exports?.["."]) {
718
+ const entry = packageJson.exports["."];
719
+ const entryPath = typeof entry === "string" ? entry : entry.default || entry.import;
720
+ if (entryPath)
721
+ mainPath = path.join(context.pluginPath, entryPath);
722
+ }
723
+ if (!mainPath && packageJson.main) {
724
+ mainPath = path.join(context.pluginPath, packageJson.main);
725
+ }
726
+ } catch {
727
+ return true;
728
+ }
729
+ if (!mainPath || !fs.existsSync(mainPath))
730
+ return true;
731
+ try {
732
+ const content = fs.readFileSync(mainPath, "utf-8");
733
+ const patterns = [
734
+ new RegExp(`export\\s*\\{[^}]*\\b${exportName}\\b[^}]*\\}`, "m"),
735
+ new RegExp(`export\\s+(?:async\\s+)?function\\s+${exportName}\\b`),
736
+ new RegExp(`export\\s+(?:const|let|var)\\s+${exportName}\\b`)
737
+ ];
738
+ return patterns.some((p) => p.test(content));
739
+ } catch {
740
+ return true;
741
+ }
742
+ }
743
+ function isRelativePath(value) {
744
+ return value.startsWith("./") || value.startsWith("../");
745
+ }
746
+ function validateHandlerRef(value, label, location, context, result) {
747
+ if (context.isNpmPackage) {
748
+ if (isRelativePath(value)) {
749
+ result.errors.push({
750
+ type: "export-mismatch",
751
+ message: `${label} "${value}" is a relative path, but NPM plugins must use an export name`,
752
+ location,
753
+ suggestion: `Export the function from the package entry point and use the export name instead of a path`
754
+ });
755
+ } else if (!checkExportExists(value, context)) {
756
+ result.errors.push({
757
+ type: "export-mismatch",
758
+ message: `${label} "${value}" is not exported from the package`,
759
+ location,
760
+ suggestion: `Add "export { ${value} } from '...'" to the package entry point`
761
+ });
762
+ }
763
+ } else if (isRelativePath(value)) {
764
+ const handlerPath = path.join(context.pluginPath, value);
765
+ const extensions = ["", ".ts", ".js", "/index.ts", "/index.js"];
766
+ const found = extensions.some((ext) => fs.existsSync(handlerPath + ext));
767
+ if (!found) {
768
+ result.errors.push({
769
+ type: "file-missing",
770
+ message: `${label} not found: ${value}`,
771
+ location,
772
+ suggestion: `Create the handler at ${handlerPath}.ts`
773
+ });
774
+ }
775
+ }
642
776
  }
643
777
  function resolveContractFile(contractSpec, context) {
644
778
  if (context.isNpmPackage) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jay-framework/plugin-validator",
3
- "version": "0.20.0",
3
+ "version": "0.21.0",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "description": "Validation tool for Jay Stack plugins",
@@ -25,15 +25,15 @@
25
25
  "test:watch": ":"
26
26
  },
27
27
  "dependencies": {
28
- "@jay-framework/compiler-jay-html": "^0.20.0",
29
- "@jay-framework/compiler-shared": "^0.20.0",
30
- "@jay-framework/editor-protocol": "^0.20.0",
31
- "@jay-framework/typescript-bridge": "^0.20.0",
28
+ "@jay-framework/compiler-jay-html": "^0.21.0",
29
+ "@jay-framework/compiler-shared": "^0.21.0",
30
+ "@jay-framework/editor-protocol": "^0.21.0",
31
+ "@jay-framework/typescript-bridge": "^0.21.0",
32
32
  "chalk": "^4.1.2",
33
33
  "yaml": "^2.3.4"
34
34
  },
35
35
  "devDependencies": {
36
- "@jay-framework/dev-environment": "^0.20.0",
36
+ "@jay-framework/dev-environment": "^0.21.0",
37
37
  "@types/node": "^22.15.21",
38
38
  "rimraf": "^5.0.5",
39
39
  "tsup": "^8.0.1",