@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.
- package/dist/index.js +136 -3
- 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 {
|
|
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.
|
|
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.
|
|
28
|
-
"@jay-framework/compiler-shared": "^0.18.
|
|
29
|
-
"@jay-framework/dev-server": "^0.18.
|
|
30
|
-
"@jay-framework/editor-server": "^0.18.
|
|
31
|
-
"@jay-framework/fullstack-component": "^0.18.
|
|
32
|
-
"@jay-framework/logger": "^0.18.
|
|
33
|
-
"@jay-framework/plugin-validator": "^0.18.
|
|
34
|
-
"@jay-framework/production-server": "^0.18.
|
|
35
|
-
"@jay-framework/stack-server-runtime": "^0.18.
|
|
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.
|
|
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",
|