@jay-framework/jay-stack-cli 0.18.1 → 0.18.3

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 +136 -3
  2. package/package.json +11 -11
package/dist/index.js CHANGED
@@ -10,7 +10,7 @@ import YAML from "yaml";
10
10
  import { getLogger, setDevLogger, createDevLogger } from "@jay-framework/logger";
11
11
  import { parseJayFile, JAY_IMPORT_RESOLVER, generateElementDefinitionFile, ContractTagType, parseContract, generateElementFile, generateServerElementFile, htmlElementTagNameMap } from "@jay-framework/compiler-jay-html";
12
12
  import { JAY_CONTRACT_EXTENSION, JAY_EXTENSION, resolvePluginManifest, LOCAL_PLUGIN_PATH, JayAtomicType, JayEnumType, loadPluginManifest, RuntimeMode, GenerateTarget } from "@jay-framework/compiler-shared";
13
- import { listContracts, materializeContracts, scanPlugins as scanPlugins$1 } from "@jay-framework/stack-server-runtime";
13
+ import { scanPlugins as scanPlugins$1, listContracts, materializeContracts } from "@jay-framework/stack-server-runtime";
14
14
  import { listContracts as listContracts2, materializeContracts as materializeContracts2 } from "@jay-framework/stack-server-runtime";
15
15
  import { Command } from "commander";
16
16
  import chalk from "chalk";
@@ -3358,6 +3358,46 @@ async function validateSchema(context, result) {
3358
3358
  });
3359
3359
  }
3360
3360
  }
3361
+ if (manifest.validators) {
3362
+ if (!Array.isArray(manifest.validators)) {
3363
+ result.errors.push({
3364
+ type: "schema",
3365
+ message: 'Field "validators" must be an array',
3366
+ location: "plugin.yaml"
3367
+ });
3368
+ } else {
3369
+ manifest.validators.forEach((validator, index) => {
3370
+ if (!validator.name) {
3371
+ result.errors.push({
3372
+ type: "schema",
3373
+ message: `Validator at index ${index} is missing "name" field`,
3374
+ location: "plugin.yaml"
3375
+ });
3376
+ }
3377
+ if (!validator.handler) {
3378
+ result.errors.push({
3379
+ type: "schema",
3380
+ message: `Validator "${validator.name || index}" is missing "handler" field`,
3381
+ location: "plugin.yaml",
3382
+ suggestion: "Specify the relative path to the validator handler module"
3383
+ });
3384
+ }
3385
+ if (validator.handler && !context.isNpmPackage) {
3386
+ const handlerPath = path.join(context.pluginPath, validator.handler);
3387
+ const extensions = ["", ".ts", ".js", "/index.ts", "/index.js"];
3388
+ const found = extensions.some((ext) => fs.existsSync(handlerPath + ext));
3389
+ if (!found) {
3390
+ result.errors.push({
3391
+ type: "file-missing",
3392
+ message: `Validator "${validator.name}" handler not found: ${validator.handler}`,
3393
+ location: "plugin.yaml validators",
3394
+ suggestion: `Create the validator handler at ${handlerPath}.ts`
3395
+ });
3396
+ }
3397
+ }
3398
+ });
3399
+ }
3400
+ }
3361
3401
  }
3362
3402
  function resolveContractFile(contractSpec, context) {
3363
3403
  if (context.isNpmPackage) {
@@ -4226,6 +4266,88 @@ function checkHeadlessInstanceProps(jayHtml, file) {
4226
4266
  walkElement(jayHtml.body);
4227
4267
  return warnings;
4228
4268
  }
4269
+ async function runPluginValidators(projectRoot, parsedFiles, errors, warnings) {
4270
+ const scannedPlugins = await scanPlugins$1({ projectRoot });
4271
+ for (const [, plugin] of scannedPlugins) {
4272
+ if (!plugin.manifest.validators)
4273
+ continue;
4274
+ for (const validatorDef of plugin.manifest.validators) {
4275
+ let validatorFn;
4276
+ try {
4277
+ const handlerPath = path.resolve(plugin.pluginPath, validatorDef.handler);
4278
+ const handlerModule = await import(handlerPath);
4279
+ validatorFn = handlerModule.validate;
4280
+ if (typeof validatorFn !== "function") {
4281
+ errors.push({
4282
+ file: `plugin:${plugin.name}`,
4283
+ message: `Validator "${validatorDef.name}" handler does not export a "validate" function`,
4284
+ stage: "plugin"
4285
+ });
4286
+ continue;
4287
+ }
4288
+ } catch (loadErr) {
4289
+ errors.push({
4290
+ file: `plugin:${plugin.name}`,
4291
+ message: `Failed to load validator "${validatorDef.name}": ${loadErr.message}`,
4292
+ stage: "plugin"
4293
+ });
4294
+ continue;
4295
+ }
4296
+ const source = `${plugin.name}/${validatorDef.name}`;
4297
+ for (const { relativePath, parsed } of parsedFiles) {
4298
+ const ctx = {
4299
+ filePath: relativePath,
4300
+ body: parsed.body,
4301
+ contract: parsed.contract ? {
4302
+ name: parsed.contract.name,
4303
+ tags: parsed.contract.tags,
4304
+ props: parsed.contract.props,
4305
+ params: parsed.contract.params
4306
+ } : void 0,
4307
+ headlessImports: parsed.headlessImports.map((imp) => ({
4308
+ key: imp.key,
4309
+ contractName: imp.contractName,
4310
+ contract: imp.contract ? {
4311
+ name: imp.contract.name,
4312
+ tags: imp.contract.tags,
4313
+ props: imp.contract.props,
4314
+ params: imp.contract.params
4315
+ } : void 0
4316
+ })),
4317
+ projectRoot
4318
+ };
4319
+ try {
4320
+ const findings = await validatorFn(ctx);
4321
+ for (const finding of findings) {
4322
+ if (finding.severity === "error") {
4323
+ errors.push({
4324
+ file: relativePath,
4325
+ message: finding.message,
4326
+ stage: "plugin",
4327
+ source,
4328
+ suggestion: finding.suggestion
4329
+ });
4330
+ } else {
4331
+ warnings.push({
4332
+ file: relativePath,
4333
+ message: finding.message,
4334
+ source,
4335
+ suggestion: finding.suggestion
4336
+ });
4337
+ }
4338
+ }
4339
+ } catch (runErr) {
4340
+ errors.push({
4341
+ file: relativePath,
4342
+ message: `Validator "${source}" threw: ${runErr.message}`,
4343
+ stage: "plugin",
4344
+ source
4345
+ });
4346
+ }
4347
+ }
4348
+ }
4349
+ }
4350
+ }
4229
4351
  async function validateJayFiles(options = {}) {
4230
4352
  const config = loadConfig();
4231
4353
  const resolvedConfig = getConfigWithDefaults(config);
@@ -4234,6 +4356,7 @@ async function validateJayFiles(options = {}) {
4234
4356
  const errors = [];
4235
4357
  const warnings = [];
4236
4358
  const coverage = [];
4359
+ const parsedFiles = [];
4237
4360
  const jayHtmlFiles = await findJayFiles(scanDir);
4238
4361
  const contractFiles = await findContractFiles(scanDir);
4239
4362
  if (options.verbose) {
@@ -4299,6 +4422,7 @@ async function validateJayFiles(options = {}) {
4299
4422
  }
4300
4423
  continue;
4301
4424
  }
4425
+ parsedFiles.push({ relativePath, parsed: parsedFile.val });
4302
4426
  const routeParamWarnings = checkRouteParams(parsedFile.val, jayFile, scanDir, content);
4303
4427
  for (const msg of routeParamWarnings) {
4304
4428
  warnings.push({ file: relativePath, message: msg });
@@ -4363,6 +4487,7 @@ async function validateJayFiles(options = {}) {
4363
4487
  }
4364
4488
  }
4365
4489
  }
4490
+ await runPluginValidators(projectRoot, parsedFiles, errors, warnings);
4366
4491
  return {
4367
4492
  valid: errors.length === 0,
4368
4493
  jayHtmlFilesScanned: jayHtmlFiles.length,
@@ -4389,8 +4514,12 @@ function printJayValidationResult(result, options) {
4389
4514
  logger.important(chalk.red("❌ Jay Stack validation failed\n"));
4390
4515
  logger.important("Errors:");
4391
4516
  for (const error of result.errors) {
4517
+ const prefix = error.source ? `[${error.source}] ` : "";
4392
4518
  logger.important(chalk.red(` ❌ ${error.file}`));
4393
- logger.important(chalk.gray(` ${error.message}`));
4519
+ logger.important(chalk.gray(` ${prefix}${error.message}`));
4520
+ if (error.suggestion) {
4521
+ logger.important(chalk.blue(` Suggestion: ${error.suggestion}`));
4522
+ }
4394
4523
  logger.important("");
4395
4524
  }
4396
4525
  const validFiles = result.jayHtmlFilesScanned + result.contractFilesScanned - result.errors.length;
@@ -4402,8 +4531,12 @@ function printJayValidationResult(result, options) {
4402
4531
  logger.important("");
4403
4532
  logger.important(chalk.yellow("Warnings:"));
4404
4533
  for (const warning of result.warnings) {
4534
+ const prefix = warning.source ? `[${warning.source}] ` : "";
4405
4535
  logger.important(chalk.yellow(` ⚠ ${warning.file}`));
4406
- logger.important(chalk.gray(` ${warning.message}`));
4536
+ logger.important(chalk.gray(` ${prefix}${warning.message}`));
4537
+ if (warning.suggestion) {
4538
+ logger.important(chalk.blue(` Suggestion: ${warning.suggestion}`));
4539
+ }
4407
4540
  logger.important("");
4408
4541
  }
4409
4542
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jay-framework/jay-stack-cli",
3
- "version": "0.18.1",
3
+ "version": "0.18.3",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -24,15 +24,15 @@
24
24
  "test:watch": "vitest"
25
25
  },
26
26
  "dependencies": {
27
- "@jay-framework/compiler-jay-html": "^0.18.1",
28
- "@jay-framework/compiler-shared": "^0.18.1",
29
- "@jay-framework/dev-server": "^0.18.1",
30
- "@jay-framework/editor-server": "^0.18.1",
31
- "@jay-framework/fullstack-component": "^0.18.1",
32
- "@jay-framework/logger": "^0.18.1",
33
- "@jay-framework/plugin-validator": "^0.18.1",
34
- "@jay-framework/production-server": "^0.18.1",
35
- "@jay-framework/stack-server-runtime": "^0.18.1",
27
+ "@jay-framework/compiler-jay-html": "^0.18.3",
28
+ "@jay-framework/compiler-shared": "^0.18.3",
29
+ "@jay-framework/dev-server": "^0.18.3",
30
+ "@jay-framework/editor-server": "^0.18.3",
31
+ "@jay-framework/fullstack-component": "^0.18.3",
32
+ "@jay-framework/logger": "^0.18.3",
33
+ "@jay-framework/plugin-validator": "^0.18.3",
34
+ "@jay-framework/production-server": "^0.18.3",
35
+ "@jay-framework/stack-server-runtime": "^0.18.3",
36
36
  "chalk": "^4.1.2",
37
37
  "commander": "^14.0.0",
38
38
  "express": "^5.0.1",
@@ -43,7 +43,7 @@
43
43
  "yaml": "^2.3.4"
44
44
  },
45
45
  "devDependencies": {
46
- "@jay-framework/dev-environment": "^0.18.1",
46
+ "@jay-framework/dev-environment": "^0.18.3",
47
47
  "@types/express": "^5.0.2",
48
48
  "@types/node": "^22.15.21",
49
49
  "nodemon": "^3.0.3",