@jay-framework/compiler-jay-html 0.12.0 → 0.13.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.d.cts +73 -2
- package/dist/index.js +308 -44
- package/package.json +9 -9
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { JayType, WithValidations, PluginComponentResolution, JayEnumType, RefsTree, CompilerSourceFile, SourceFileFormat, JayImportLink, MainRuntimeModes, GenerateTarget, Imports } from '@jay-framework/compiler-shared';
|
|
1
|
+
import { JayType, WithValidations, PluginComponentResolution, PluginManifest, JayEnumType, RefsTree, CompilerSourceFile, SourceFileFormat, JayImportLink, MainRuntimeModes, GenerateTarget, Imports, JayObjectType } from '@jay-framework/compiler-shared';
|
|
2
2
|
import { HTMLElement } from 'node-html-parser';
|
|
3
3
|
import { ResolveTsConfigOptions } from '@jay-framework/compiler-analyze-exported-types';
|
|
4
4
|
import { Coordinate } from '@jay-framework/runtime';
|
|
@@ -89,6 +89,7 @@ interface JayImportResolver {
|
|
|
89
89
|
contractPath: string;
|
|
90
90
|
metadata?: Record<string, unknown>;
|
|
91
91
|
}>;
|
|
92
|
+
resolvePluginManifest(pluginName: string, projectRoot: string): WithValidations<PluginManifest>;
|
|
92
93
|
}
|
|
93
94
|
declare const JAY_IMPORT_RESOLVER: JayImportResolver;
|
|
94
95
|
|
|
@@ -211,6 +212,58 @@ declare function renderRefsType(refs: RefsTree, refsType: string, generateTarget
|
|
|
211
212
|
|
|
212
213
|
declare function generateTypes(types: JayType): string;
|
|
213
214
|
|
|
215
|
+
/**
|
|
216
|
+
* Parser for .jay-action files (compact jay-type notation).
|
|
217
|
+
*
|
|
218
|
+
* Produces JayType from the compact format used by jay-html data scripts:
|
|
219
|
+
* - Primitives: string, number, boolean → JayAtomicType
|
|
220
|
+
* - Enums: enum(a | b | c) → JayEnumType
|
|
221
|
+
* - Arrays: YAML list / type[] shorthand → JayArrayType
|
|
222
|
+
* - Objects: nested YAML maps → JayObjectType (with optionalProps tracking)
|
|
223
|
+
* - Optional: ? suffix on property keys → tracked in JayObjectType.optionalProps
|
|
224
|
+
* - Contract imports: import: block → JayImportedType
|
|
225
|
+
*/
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Parsed action definition from a .jay-action file.
|
|
229
|
+
* Uses JayType for the type representation.
|
|
230
|
+
*/
|
|
231
|
+
interface ActionDefinition {
|
|
232
|
+
name: string;
|
|
233
|
+
description: string;
|
|
234
|
+
/** Import aliases: alias → contract subpath (e.g., productCard → product-card.jay-contract) */
|
|
235
|
+
imports: Record<string, string>;
|
|
236
|
+
/** Input schema as a JayObjectType. Optional props tracked in optionalProps. */
|
|
237
|
+
inputType: JayObjectType;
|
|
238
|
+
/** Output type (any JayType shape, or undefined if no output). */
|
|
239
|
+
outputType?: JayType;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Parses a .jay-action YAML string into an ActionDefinition using JayType.
|
|
243
|
+
*/
|
|
244
|
+
declare function parseAction(actionYaml: string, fileName: string): WithValidations<ActionDefinition>;
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Compiler for .jay-action files → .jay-action.d.ts
|
|
248
|
+
*
|
|
249
|
+
* Generates TypeScript Input/Output interfaces from JayType trees,
|
|
250
|
+
* including import statements for contract references (JayImportedType).
|
|
251
|
+
*/
|
|
252
|
+
|
|
253
|
+
interface ContractImportInfo {
|
|
254
|
+
importPath: string;
|
|
255
|
+
viewStateName: string;
|
|
256
|
+
}
|
|
257
|
+
type ContractResolver = (contractSubpath: string) => ContractImportInfo | null;
|
|
258
|
+
/**
|
|
259
|
+
* Default contract resolver that derives names from the subpath.
|
|
260
|
+
*/
|
|
261
|
+
declare function defaultContractResolver(contractSubpath: string): ContractImportInfo;
|
|
262
|
+
/**
|
|
263
|
+
* Compiles an ActionDefinition (with JayType) into a TypeScript .d.ts string.
|
|
264
|
+
*/
|
|
265
|
+
declare function compileAction(actionWithValidations: WithValidations<ActionDefinition>, contractResolver?: ContractResolver): WithValidations<string>;
|
|
266
|
+
|
|
214
267
|
/**
|
|
215
268
|
* Headless contract with its key (used for property path prefix)
|
|
216
269
|
*/
|
|
@@ -302,8 +355,26 @@ interface HeadlessInstanceResolvedData {
|
|
|
302
355
|
* a coordinateCounters map must be provided to auto-assign an index.
|
|
303
356
|
*/
|
|
304
357
|
declare function buildInstanceCoordinateKey(element: HTMLElement, contractName: string, coordinateCounters?: Map<string, number>): string;
|
|
358
|
+
/**
|
|
359
|
+
* A headless instance found inside a preserved forEach (fast phase).
|
|
360
|
+
* These instances have unresolved prop bindings and need per-item rendering
|
|
361
|
+
* at fast phase time. They are only allowed if the component has no slow phase.
|
|
362
|
+
*/
|
|
363
|
+
interface ForEachHeadlessInstance {
|
|
364
|
+
contractName: string;
|
|
365
|
+
/** The forEach attribute path on the ancestor element (e.g., "allProducts.items") */
|
|
366
|
+
forEachPath: string;
|
|
367
|
+
/** TrackBy key for the forEach (e.g., "_id") */
|
|
368
|
+
trackBy: string;
|
|
369
|
+
/** Prop bindings referencing forEach item fields (e.g., { productId: "{_id}" }) */
|
|
370
|
+
propBindings: Record<string, string>;
|
|
371
|
+
/** Coordinate suffix after trackBy values (e.g., "product-card:0") */
|
|
372
|
+
coordinateSuffix: string;
|
|
373
|
+
}
|
|
305
374
|
interface HeadlessInstanceDiscoveryResult {
|
|
306
375
|
instances: DiscoveredHeadlessInstance[];
|
|
376
|
+
/** Headless instances inside preserved forEach elements (need server-time validation) */
|
|
377
|
+
forEachInstances: ForEachHeadlessInstance[];
|
|
307
378
|
/** HTML with auto-generated ref attributes embedded on <jay:xxx> elements */
|
|
308
379
|
preRenderedJayHtml: string;
|
|
309
380
|
}
|
|
@@ -314,4 +385,4 @@ declare function resolveHeadlessInstances(preRenderedJayHtml: string, instanceDa
|
|
|
314
385
|
*/
|
|
315
386
|
declare function hasSlowPhaseProperties(contract: Contract | undefined): boolean;
|
|
316
387
|
|
|
317
|
-
export { type Contract, type ContractParam, type ContractProp, type ContractTag, ContractTagType, type DiscoveredHeadlessInstance, type EnumToImport, type HeadlessContractInfo, type HeadlessInstanceDiscoveryResult, type HeadlessInstanceResolvedData, JAY_IMPORT_RESOLVER, type JayContractImportLink, type JayHtmlSourceFile, type JayImportResolver, type PhaseViewStates, type RenderingPhase, type SlowRenderInput, type SlowRenderOutput, buildInstanceCoordinateKey, compileContract, contractToAllPhaseViewStates, contractToImportsViewStateAndRefs, contractToPhaseViewState, createPhaseContract, discoverHeadlessInstances, filterTagsByPhase, generateElementBridgeFile, generateElementDefinitionFile, generateElementFile, generateElementFileReactTarget, generateSandboxRootFile, generateTypes, getEffectivePhase, getJayHtmlImports, getLinkedContractDir, hasSlowPhaseProperties, isTagInPhase, loadLinkedContract, parseContract, parseEnumValues, parseIsEnum, parseJayFile, renderRefsType, resolveHeadlessInstances, slowRenderTransform, validateContractPhases };
|
|
388
|
+
export { type ActionDefinition, type Contract, type ContractImportInfo, type ContractParam, type ContractProp, type ContractResolver, type ContractTag, ContractTagType, type DiscoveredHeadlessInstance, type EnumToImport, type ForEachHeadlessInstance, type HeadlessContractInfo, type HeadlessInstanceDiscoveryResult, type HeadlessInstanceResolvedData, JAY_IMPORT_RESOLVER, type JayContractImportLink, type JayHtmlSourceFile, type JayImportResolver, type PhaseViewStates, type RenderingPhase, type SlowRenderInput, type SlowRenderOutput, buildInstanceCoordinateKey, compileAction, compileContract, contractToAllPhaseViewStates, contractToImportsViewStateAndRefs, contractToPhaseViewState, createPhaseContract, defaultContractResolver, discoverHeadlessInstances, filterTagsByPhase, generateElementBridgeFile, generateElementDefinitionFile, generateElementFile, generateElementFileReactTarget, generateSandboxRootFile, generateTypes, getEffectivePhase, getJayHtmlImports, getLinkedContractDir, hasSlowPhaseProperties, isTagInPhase, loadLinkedContract, parseAction, parseContract, parseEnumValues, parseIsEnum, parseJayFile, renderRefsType, resolveHeadlessInstances, slowRenderTransform, validateContractPhases };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { RenderFragment, Imports, Import, JayImportedType, isObjectType as isObjectType$1, isImportedType, isRecursiveType, JayUnknown, WithValidations, isArrayType as isArrayType$1, isAtomicType, isPromiseType, isEnumType, hasRefs, GenerateTarget, JayComponentType, JayTypeAlias, mkRefsTree, mkRef, equalJayTypes, JayUnionType, JayHTMLType, getModeFileExtension, RuntimeMode, ImportsFor, nestRefs, JayErrorType, mergeRefsTrees, JayObjectType, JayArrayType, JayPromiseType, JAY_CONTRACT_EXTENSION as JAY_CONTRACT_EXTENSION$1, JayRecursiveType, JAY_FULLSTACK_COMPONENTS, JayEnumType, resolvePrimitiveType, SourceFileFormat, resolvePluginComponent } from "@jay-framework/compiler-shared";
|
|
1
|
+
import { RenderFragment, Imports, Import, JayImportedType, isObjectType as isObjectType$1, isImportedType, isRecursiveType, JayUnknown, WithValidations, isArrayType as isArrayType$1, isAtomicType, isPromiseType, isEnumType, hasRefs, GenerateTarget, JayComponentType, JayTypeAlias, mkRefsTree, mkRef, equalJayTypes, JayUnionType, JayHTMLType, getModeFileExtension, RuntimeMode, ImportsFor, nestRefs, JayErrorType, mergeRefsTrees, JayObjectType, JayArrayType, JayPromiseType, JAY_CONTRACT_EXTENSION as JAY_CONTRACT_EXTENSION$1, JayRecursiveType, JAY_FULLSTACK_COMPONENTS, JayEnumType, resolvePrimitiveType, SourceFileFormat, JayOptionalType, isOptionalType, resolvePluginComponent, resolvePluginManifest } from "@jay-framework/compiler-shared";
|
|
2
2
|
import path from "path";
|
|
3
3
|
import { getLogger } from "@jay-framework/logger";
|
|
4
4
|
import fs from "fs/promises";
|
|
@@ -13345,7 +13345,7 @@ function getComponentName(tagName, importedSymbols, headlessContractNames) {
|
|
|
13345
13345
|
}
|
|
13346
13346
|
return null;
|
|
13347
13347
|
}
|
|
13348
|
-
function renderInterface(aType) {
|
|
13348
|
+
function renderInterface$1(aType) {
|
|
13349
13349
|
let childInterfaces = [];
|
|
13350
13350
|
let genInterface = "";
|
|
13351
13351
|
if (isObjectType$1(aType)) {
|
|
@@ -13361,12 +13361,12 @@ function renderInterface(aType) {
|
|
|
13361
13361
|
const optional = childType.isOptional ? "?" : "";
|
|
13362
13362
|
return ` ${prop}${optional}: ${childType.name}`;
|
|
13363
13363
|
} else if (isObjectType$1(childType)) {
|
|
13364
|
-
childInterfaces.push(renderInterface(childType));
|
|
13364
|
+
childInterfaces.push(renderInterface$1(childType));
|
|
13365
13365
|
return ` ${prop}: ${childType.name}`;
|
|
13366
13366
|
} else if (isArrayType$1(childType)) {
|
|
13367
13367
|
let arrayItemType = childType.itemType;
|
|
13368
13368
|
if (isObjectType$1(arrayItemType)) {
|
|
13369
|
-
childInterfaces.push(renderInterface(arrayItemType));
|
|
13369
|
+
childInterfaces.push(renderInterface$1(arrayItemType));
|
|
13370
13370
|
return ` ${prop}: Array<${arrayItemType.name}>`;
|
|
13371
13371
|
}
|
|
13372
13372
|
if (arrayItemType instanceof JayImportedType) {
|
|
@@ -13388,10 +13388,10 @@ function renderInterface(aType) {
|
|
|
13388
13388
|
if (isAtomicType(promiseItemType))
|
|
13389
13389
|
return ` ${prop}: Promise<${promiseItemType.name}>`;
|
|
13390
13390
|
else if (isObjectType$1(promiseItemType)) {
|
|
13391
|
-
childInterfaces.push(renderInterface(promiseItemType));
|
|
13391
|
+
childInterfaces.push(renderInterface$1(promiseItemType));
|
|
13392
13392
|
return ` ${prop}: Promise<${promiseItemType.name}>`;
|
|
13393
13393
|
} else if (isArrayType$1(promiseItemType) && isObjectType$1(promiseItemType.itemType)) {
|
|
13394
|
-
childInterfaces.push(renderInterface(promiseItemType.itemType));
|
|
13394
|
+
childInterfaces.push(renderInterface$1(promiseItemType.itemType));
|
|
13395
13395
|
return ` ${prop}: Promise<Array<${promiseItemType.itemType.name}>>`;
|
|
13396
13396
|
}
|
|
13397
13397
|
} else if (isEnumType(childType)) {
|
|
@@ -13417,7 +13417,7 @@ ${childType.values.map((_) => " " + _).join(",\n")}
|
|
|
13417
13417
|
return [...childInterfaces, genInterface].join("\n\n");
|
|
13418
13418
|
}
|
|
13419
13419
|
function generateTypes(types2) {
|
|
13420
|
-
return renderInterface(types2);
|
|
13420
|
+
return renderInterface$1(types2);
|
|
13421
13421
|
}
|
|
13422
13422
|
class Indent {
|
|
13423
13423
|
base;
|
|
@@ -14526,16 +14526,6 @@ ${indent.curr}return ${childElement.rendered}}, '${trackBy}')`,
|
|
|
14526
14526
|
);
|
|
14527
14527
|
}
|
|
14528
14528
|
function renderHeadlessInstance(htmlElement, newContext, contractName) {
|
|
14529
|
-
if (newContext.insideFastForEach) {
|
|
14530
|
-
return new RenderFragment(
|
|
14531
|
-
"",
|
|
14532
|
-
Imports.none(),
|
|
14533
|
-
[
|
|
14534
|
-
`<jay:${contractName}> cannot be used inside a fast-phase forEach. Headless component instances require server-side rendering which is not available for dynamically-iterated arrays. Change the array's phase to "slow" in the contract to use slowForEach, or use a key-based headless component instead.`
|
|
14535
|
-
],
|
|
14536
|
-
mkRefsTree([], {})
|
|
14537
|
-
);
|
|
14538
|
-
}
|
|
14539
14529
|
const headlessImport = newContext.headlessImports.find(
|
|
14540
14530
|
(h) => h.contractName === contractName
|
|
14541
14531
|
);
|
|
@@ -14555,7 +14545,7 @@ ${indent.curr}return ${childElement.rendered}}, '${trackBy}')`,
|
|
|
14555
14545
|
const interactiveViewStateType = `${pascal}InteractiveViewState`;
|
|
14556
14546
|
const refsTypeName = `${pascal}Refs`;
|
|
14557
14547
|
const elementType = `_Headless${pascal}${idx}Element`;
|
|
14558
|
-
const
|
|
14548
|
+
const renderType2 = `_Headless${pascal}${idx}ElementRender`;
|
|
14559
14549
|
const preRenderType = `_Headless${pascal}${idx}ElementPreRender`;
|
|
14560
14550
|
newContext.usedComponentImports.add(interactiveViewStateType);
|
|
14561
14551
|
for (const link of headlessImport.contractLinks) {
|
|
@@ -14629,15 +14619,14 @@ ${indent.curr}return ${childElement.rendered}}, '${trackBy}')`,
|
|
|
14629
14619
|
newContext.coordinateCounters.set(counterKey, localIndex + 1);
|
|
14630
14620
|
coordinateRef = String(localIndex);
|
|
14631
14621
|
}
|
|
14632
|
-
const
|
|
14633
|
-
|
|
14634
|
-
|
|
14635
|
-
].join("/");
|
|
14622
|
+
const isInsideForEach = newContext.insideFastForEach;
|
|
14623
|
+
const coordinateSuffix = `${contractName}:${coordinateRef}`;
|
|
14624
|
+
const coordinateKey2 = isInsideForEach ? void 0 : [...newContext.coordinatePrefix, coordinateSuffix].join("/");
|
|
14636
14625
|
const renderFnCode = `
|
|
14637
14626
|
// Inline template for headless component: ${contractName} #${idx}
|
|
14638
14627
|
type ${elementType} = JayElement<${interactiveViewStateType}, ${refsTypeName}>;
|
|
14639
|
-
type ${
|
|
14640
|
-
type ${preRenderType} = [${refsTypeName}, ${
|
|
14628
|
+
type ${renderType2} = RenderElement<${interactiveViewStateType}, ${refsTypeName}, ${elementType}>;
|
|
14629
|
+
type ${preRenderType} = [${refsTypeName}, ${renderType2}];
|
|
14641
14630
|
|
|
14642
14631
|
function ${renderFnName}(options?: RenderElementOptions): ${preRenderType} {
|
|
14643
14632
|
${renderedRefsManager}
|
|
@@ -14651,7 +14640,7 @@ ${inlineBody.rendered}
|
|
|
14651
14640
|
const ${componentSymbol} = makeHeadlessInstanceComponent(
|
|
14652
14641
|
${renderFnName},
|
|
14653
14642
|
${pluginComponentName}.comp,
|
|
14654
|
-
'${coordinateKey2}',
|
|
14643
|
+
${isInsideForEach ? `(dataIds) => [...dataIds, '${coordinateSuffix}'].toString()` : `'${coordinateKey2}'`},
|
|
14655
14644
|
${pluginComponentName}.contexts,
|
|
14656
14645
|
);`;
|
|
14657
14646
|
newContext.headlessInstanceDefs.push({
|
|
@@ -14999,7 +14988,7 @@ ${renderedRoot.rendered}
|
|
|
14999
14988
|
const elementType = baseElementName + "Element";
|
|
15000
14989
|
const refsType = baseElementName + "ElementRefs";
|
|
15001
14990
|
const viewStateType = types2.name;
|
|
15002
|
-
const
|
|
14991
|
+
const renderType2 = `${elementType}Render`;
|
|
15003
14992
|
const preRenderType = `${elementType}PreRender`;
|
|
15004
14993
|
const contractType = `${baseElementName}Contract`;
|
|
15005
14994
|
let imports = renderedRoot.imports.plus(Import.ConstructContext).plus(Import.RenderElementOptions).plus(Import.RenderElement).plus(Import.ReferencesManager).plus(Import.jayContract);
|
|
@@ -15009,8 +14998,8 @@ ${renderedRoot.rendered}
|
|
|
15009
14998
|
const { imports: refImports, renderedRefs } = renderRefsType(renderedRoot.refs, refsType);
|
|
15010
14999
|
imports = imports.plus(refImports);
|
|
15011
15000
|
let renderedElement = `export type ${elementType} = JayElement<${viewStateType}, ${refsType}>
|
|
15012
|
-
export type ${
|
|
15013
|
-
export type ${preRenderType} = [${refsType}, ${
|
|
15001
|
+
export type ${renderType2} = RenderElement<${viewStateType}, ${refsType}, ${elementType}>
|
|
15002
|
+
export type ${preRenderType} = [${refsType}, ${renderType2}]
|
|
15014
15003
|
export type ${contractType} = JayContract<${viewStateType}, ${refsType}>;
|
|
15015
15004
|
`;
|
|
15016
15005
|
if (importedSandboxedSymbols.size > 0) {
|
|
@@ -20427,9 +20416,252 @@ async function parseJayFile(html2, filename, filePath, options, linkedContractRe
|
|
|
20427
20416
|
}
|
|
20428
20417
|
function getJayHtmlImports(html2) {
|
|
20429
20418
|
const root = parse_2(html2);
|
|
20430
|
-
return root.querySelectorAll(
|
|
20431
|
-
|
|
20432
|
-
|
|
20419
|
+
return root.querySelectorAll('script[type="application/jay-headfull"]').map((script) => script.getAttribute("src")).filter((src) => src !== null);
|
|
20420
|
+
}
|
|
20421
|
+
function parseArrayShorthand(value) {
|
|
20422
|
+
if (value.endsWith("[]")) {
|
|
20423
|
+
return value.slice(0, -2);
|
|
20424
|
+
}
|
|
20425
|
+
return null;
|
|
20426
|
+
}
|
|
20427
|
+
function resolveStringType(value, importAliases, path2) {
|
|
20428
|
+
const arrayBase = parseArrayShorthand(value);
|
|
20429
|
+
if (arrayBase) {
|
|
20430
|
+
const itemType = resolveStringType(arrayBase, importAliases, path2);
|
|
20431
|
+
if (itemType)
|
|
20432
|
+
return new JayArrayType(itemType);
|
|
20433
|
+
return null;
|
|
20434
|
+
}
|
|
20435
|
+
if (value.endsWith("?")) {
|
|
20436
|
+
const alias = value.slice(0, -1);
|
|
20437
|
+
if (importAliases.has(alias)) {
|
|
20438
|
+
return new JayImportedType(alias, JayUnknown, true);
|
|
20439
|
+
}
|
|
20440
|
+
return null;
|
|
20441
|
+
}
|
|
20442
|
+
const primitive = resolvePrimitiveType(value);
|
|
20443
|
+
if (primitive !== JayUnknown) {
|
|
20444
|
+
return primitive;
|
|
20445
|
+
}
|
|
20446
|
+
if (parseIsEnum(value)) {
|
|
20447
|
+
const name = path2.length > 0 ? path2[path2.length - 1] : "value";
|
|
20448
|
+
return new JayEnumType(name, parseEnumValues(value));
|
|
20449
|
+
}
|
|
20450
|
+
if (importAliases.has(value)) {
|
|
20451
|
+
return new JayImportedType(value, JayUnknown, false);
|
|
20452
|
+
}
|
|
20453
|
+
return null;
|
|
20454
|
+
}
|
|
20455
|
+
function resolveActionType(value, importAliases, path2) {
|
|
20456
|
+
if (typeof value === "string") {
|
|
20457
|
+
return resolveStringType(value, importAliases, path2);
|
|
20458
|
+
}
|
|
20459
|
+
if (Array.isArray(value)) {
|
|
20460
|
+
if (value.length === 0)
|
|
20461
|
+
return null;
|
|
20462
|
+
const firstItem = value[0];
|
|
20463
|
+
if (typeof firstItem === "string") {
|
|
20464
|
+
const itemType = resolveStringType(firstItem, importAliases, [...path2, "item"]);
|
|
20465
|
+
if (itemType)
|
|
20466
|
+
return new JayArrayType(itemType);
|
|
20467
|
+
} else if (typeof firstItem === "object" && firstItem !== null) {
|
|
20468
|
+
const itemType = parseObjectType(firstItem, importAliases, [
|
|
20469
|
+
...path2,
|
|
20470
|
+
"item"
|
|
20471
|
+
]);
|
|
20472
|
+
return new JayArrayType(itemType);
|
|
20473
|
+
}
|
|
20474
|
+
return null;
|
|
20475
|
+
}
|
|
20476
|
+
if (typeof value === "object" && value !== null) {
|
|
20477
|
+
if (Object.keys(value).length === 0) {
|
|
20478
|
+
return new JayObjectType(path2.join("."), {});
|
|
20479
|
+
}
|
|
20480
|
+
return parseObjectType(value, importAliases, path2);
|
|
20481
|
+
}
|
|
20482
|
+
return null;
|
|
20483
|
+
}
|
|
20484
|
+
function parseObjectType(obj, importAliases, path2) {
|
|
20485
|
+
const props = {};
|
|
20486
|
+
for (const [rawKey, value] of Object.entries(obj)) {
|
|
20487
|
+
const isOptional = rawKey.endsWith("?");
|
|
20488
|
+
const name = isOptional ? rawKey.slice(0, -1) : rawKey;
|
|
20489
|
+
const type2 = resolveActionType(value, importAliases, [...path2, name]);
|
|
20490
|
+
if (type2) {
|
|
20491
|
+
props[name] = isOptional ? new JayOptionalType(type2) : type2;
|
|
20492
|
+
}
|
|
20493
|
+
}
|
|
20494
|
+
const typeName = path2.join(".");
|
|
20495
|
+
return new JayObjectType(typeName, props);
|
|
20496
|
+
}
|
|
20497
|
+
function parseAction(actionYaml, fileName) {
|
|
20498
|
+
try {
|
|
20499
|
+
const parsed = jsYaml.load(actionYaml);
|
|
20500
|
+
const validations = [];
|
|
20501
|
+
if (!parsed || typeof parsed !== "object") {
|
|
20502
|
+
return new WithValidations(void 0, [
|
|
20503
|
+
"Action file is empty or not a valid YAML object"
|
|
20504
|
+
]);
|
|
20505
|
+
}
|
|
20506
|
+
if (!parsed.name || typeof parsed.name !== "string") {
|
|
20507
|
+
validations.push(`Action must have a 'name' field (string)`);
|
|
20508
|
+
}
|
|
20509
|
+
if (!parsed.description || typeof parsed.description !== "string") {
|
|
20510
|
+
validations.push(`Action must have a 'description' field (string)`);
|
|
20511
|
+
}
|
|
20512
|
+
if (parsed.inputSchema === void 0 || parsed.inputSchema === null) {
|
|
20513
|
+
validations.push(`Action must have an 'inputSchema' field`);
|
|
20514
|
+
}
|
|
20515
|
+
if (validations.length > 0) {
|
|
20516
|
+
return new WithValidations(void 0, validations);
|
|
20517
|
+
}
|
|
20518
|
+
const imports = {};
|
|
20519
|
+
if (parsed.import && typeof parsed.import === "object") {
|
|
20520
|
+
for (const [alias, contractPath] of Object.entries(
|
|
20521
|
+
parsed.import
|
|
20522
|
+
)) {
|
|
20523
|
+
if (typeof contractPath === "string") {
|
|
20524
|
+
imports[alias] = contractPath;
|
|
20525
|
+
}
|
|
20526
|
+
}
|
|
20527
|
+
}
|
|
20528
|
+
const importAliases = new Set(Object.keys(imports));
|
|
20529
|
+
let inputType;
|
|
20530
|
+
if (typeof parsed.inputSchema === "object" && parsed.inputSchema !== null && Object.keys(parsed.inputSchema).length > 0) {
|
|
20531
|
+
inputType = parseObjectType(
|
|
20532
|
+
parsed.inputSchema,
|
|
20533
|
+
importAliases,
|
|
20534
|
+
["input"]
|
|
20535
|
+
);
|
|
20536
|
+
} else {
|
|
20537
|
+
inputType = new JayObjectType("input", {});
|
|
20538
|
+
}
|
|
20539
|
+
let outputType;
|
|
20540
|
+
if (parsed.outputSchema !== void 0 && parsed.outputSchema !== null) {
|
|
20541
|
+
outputType = resolveActionType(parsed.outputSchema, importAliases, ["output"]) ?? void 0;
|
|
20542
|
+
}
|
|
20543
|
+
const definition = {
|
|
20544
|
+
name: parsed.name,
|
|
20545
|
+
description: parsed.description,
|
|
20546
|
+
imports,
|
|
20547
|
+
inputType,
|
|
20548
|
+
outputType
|
|
20549
|
+
};
|
|
20550
|
+
return new WithValidations(definition, []);
|
|
20551
|
+
} catch (e2) {
|
|
20552
|
+
throw new Error(`Failed to parse action YAML for ${fileName}: ${e2.message}`);
|
|
20553
|
+
}
|
|
20554
|
+
}
|
|
20555
|
+
function defaultContractResolver(contractSubpath) {
|
|
20556
|
+
const baseName = contractSubpath.replace(".jay-contract", "");
|
|
20557
|
+
const viewStateName = pascalCase(baseName) + "ViewState";
|
|
20558
|
+
return { importPath: `./${contractSubpath}`, viewStateName };
|
|
20559
|
+
}
|
|
20560
|
+
function collectImportedAliases(type2, aliases2) {
|
|
20561
|
+
if (isImportedType(type2)) {
|
|
20562
|
+
aliases2.add(type2.name);
|
|
20563
|
+
} else if (isOptionalType(type2)) {
|
|
20564
|
+
collectImportedAliases(type2.innerType, aliases2);
|
|
20565
|
+
} else if (isObjectType$1(type2)) {
|
|
20566
|
+
for (const prop of Object.values(type2.props)) {
|
|
20567
|
+
collectImportedAliases(prop, aliases2);
|
|
20568
|
+
}
|
|
20569
|
+
} else if (isArrayType$1(type2)) {
|
|
20570
|
+
collectImportedAliases(type2.itemType, aliases2);
|
|
20571
|
+
}
|
|
20572
|
+
}
|
|
20573
|
+
function renderType(type2, aliasToViewState, indent) {
|
|
20574
|
+
if (isOptionalType(type2)) {
|
|
20575
|
+
return renderType(type2.innerType, aliasToViewState, indent);
|
|
20576
|
+
}
|
|
20577
|
+
if (isAtomicType(type2)) {
|
|
20578
|
+
return type2.name;
|
|
20579
|
+
}
|
|
20580
|
+
if (isEnumType(type2)) {
|
|
20581
|
+
return type2.values.map((v) => `'${v}'`).join(" | ");
|
|
20582
|
+
}
|
|
20583
|
+
if (isImportedType(type2)) {
|
|
20584
|
+
const viewStateName = aliasToViewState.get(type2.name) || type2.name;
|
|
20585
|
+
return type2.isOptional ? `${viewStateName} | null` : viewStateName;
|
|
20586
|
+
}
|
|
20587
|
+
if (isArrayType$1(type2)) {
|
|
20588
|
+
const itemStr = renderType(type2.itemType, aliasToViewState, indent + " ");
|
|
20589
|
+
return `Array<${itemStr}>`;
|
|
20590
|
+
}
|
|
20591
|
+
if (isObjectType$1(type2)) {
|
|
20592
|
+
if (Object.keys(type2.props).length === 0) {
|
|
20593
|
+
return "Record<string, unknown>";
|
|
20594
|
+
}
|
|
20595
|
+
return renderInlineObject(type2, aliasToViewState, indent);
|
|
20596
|
+
}
|
|
20597
|
+
return "unknown";
|
|
20598
|
+
}
|
|
20599
|
+
function renderInlineObject(type2, aliasToViewState, indent) {
|
|
20600
|
+
const childIndent = indent + " ";
|
|
20601
|
+
const lines = Object.entries(type2.props).map(([prop, propType]) => {
|
|
20602
|
+
const optional = isOptionalType(propType) ? "?" : "";
|
|
20603
|
+
const tsType = renderType(propType, aliasToViewState, childIndent);
|
|
20604
|
+
return `${childIndent}${prop}${optional}: ${tsType};`;
|
|
20605
|
+
});
|
|
20606
|
+
return `{
|
|
20607
|
+
${lines.join("\n")}
|
|
20608
|
+
${indent}}`;
|
|
20609
|
+
}
|
|
20610
|
+
function renderInterface(interfaceName, type2, aliasToViewState) {
|
|
20611
|
+
const propKeys = Object.keys(type2.props);
|
|
20612
|
+
if (propKeys.length === 0) {
|
|
20613
|
+
return `export interface ${interfaceName} {}`;
|
|
20614
|
+
}
|
|
20615
|
+
const lines = propKeys.map((prop) => {
|
|
20616
|
+
const optional = isOptionalType(type2.props[prop]) ? "?" : "";
|
|
20617
|
+
const tsType = renderType(type2.props[prop], aliasToViewState, " ");
|
|
20618
|
+
return ` ${prop}${optional}: ${tsType};`;
|
|
20619
|
+
});
|
|
20620
|
+
return `export interface ${interfaceName} {
|
|
20621
|
+
${lines.join("\n")}
|
|
20622
|
+
}`;
|
|
20623
|
+
}
|
|
20624
|
+
function renderOutputType(typeName, outputType, aliasToViewState) {
|
|
20625
|
+
if (isObjectType$1(outputType) && Object.keys(outputType.props).length > 0) {
|
|
20626
|
+
return renderInterface(typeName, outputType, aliasToViewState);
|
|
20627
|
+
}
|
|
20628
|
+
const tsType = renderType(outputType, aliasToViewState, "");
|
|
20629
|
+
return `export type ${typeName} = ${tsType};`;
|
|
20630
|
+
}
|
|
20631
|
+
function compileAction(actionWithValidations, contractResolver = defaultContractResolver) {
|
|
20632
|
+
return actionWithValidations.map((action) => {
|
|
20633
|
+
const baseName = pascalCase(action.name);
|
|
20634
|
+
const sections = [];
|
|
20635
|
+
const usedAliases = /* @__PURE__ */ new Set();
|
|
20636
|
+
collectImportedAliases(action.inputType, usedAliases);
|
|
20637
|
+
if (action.outputType) {
|
|
20638
|
+
collectImportedAliases(action.outputType, usedAliases);
|
|
20639
|
+
}
|
|
20640
|
+
const aliasToViewState = /* @__PURE__ */ new Map();
|
|
20641
|
+
const importStatements = [];
|
|
20642
|
+
for (const alias of usedAliases) {
|
|
20643
|
+
const contractSubpath = action.imports[alias];
|
|
20644
|
+
if (!contractSubpath)
|
|
20645
|
+
continue;
|
|
20646
|
+
const resolved = contractResolver(contractSubpath);
|
|
20647
|
+
if (!resolved)
|
|
20648
|
+
continue;
|
|
20649
|
+
aliasToViewState.set(alias, resolved.viewStateName);
|
|
20650
|
+
importStatements.push(
|
|
20651
|
+
`import { ${resolved.viewStateName} } from '${resolved.importPath}';`
|
|
20652
|
+
);
|
|
20653
|
+
}
|
|
20654
|
+
if (importStatements.length > 0) {
|
|
20655
|
+
sections.push(importStatements.join("\n"));
|
|
20656
|
+
}
|
|
20657
|
+
sections.push(renderInterface(`${baseName}Input`, action.inputType, aliasToViewState));
|
|
20658
|
+
if (action.outputType) {
|
|
20659
|
+
sections.push(
|
|
20660
|
+
renderOutputType(`${baseName}Output`, action.outputType, aliasToViewState)
|
|
20661
|
+
);
|
|
20662
|
+
}
|
|
20663
|
+
return sections.join("\n\n");
|
|
20664
|
+
});
|
|
20433
20665
|
}
|
|
20434
20666
|
const ALIAS = Symbol.for("yaml.alias");
|
|
20435
20667
|
const DOC = Symbol.for("yaml.document");
|
|
@@ -26890,6 +27122,9 @@ const JAY_IMPORT_RESOLVER = {
|
|
|
26890
27122
|
return new WithValidations(null, [
|
|
26891
27123
|
`Contract "${contractName}" not found for plugin "${pluginName}". For dynamic contracts, run 'jay-stack agent-kit' to materialize them first.`
|
|
26892
27124
|
]);
|
|
27125
|
+
},
|
|
27126
|
+
resolvePluginManifest(pluginName, projectRoot) {
|
|
27127
|
+
return resolvePluginManifest(projectRoot, pluginName);
|
|
26893
27128
|
}
|
|
26894
27129
|
};
|
|
26895
27130
|
function buildPhaseMap(contract, headlessContracts, importResolver) {
|
|
@@ -27208,20 +27443,23 @@ function discoverHeadlessInstances(preRenderedJayHtml) {
|
|
|
27208
27443
|
}
|
|
27209
27444
|
});
|
|
27210
27445
|
const instances = [];
|
|
27446
|
+
const forEachInstances = [];
|
|
27211
27447
|
const coordinateCounters = /* @__PURE__ */ new Map();
|
|
27212
|
-
function walk(element, insidePreservedForEach) {
|
|
27448
|
+
function walk(element, insidePreservedForEach, forEachContexts) {
|
|
27213
27449
|
const tagName = element.tagName?.toLowerCase();
|
|
27214
|
-
const
|
|
27450
|
+
const forEachAttr = element.getAttribute("forEach");
|
|
27451
|
+
const hasForEach = forEachAttr != null;
|
|
27452
|
+
const trackByAttr = element.getAttribute("trackBy");
|
|
27215
27453
|
if (tagName?.startsWith("jay:")) {
|
|
27216
|
-
|
|
27217
|
-
|
|
27218
|
-
|
|
27219
|
-
|
|
27220
|
-
|
|
27221
|
-
|
|
27222
|
-
props[toCamelCase(key)] = value;
|
|
27223
|
-
}
|
|
27454
|
+
const contractName = tagName.substring(4);
|
|
27455
|
+
const props = {};
|
|
27456
|
+
for (const [key, value] of Object.entries(element.attributes)) {
|
|
27457
|
+
const lowerKey = key.toLowerCase();
|
|
27458
|
+
if (lowerKey !== "ref" && !lowerKey.startsWith("jay")) {
|
|
27459
|
+
props[toCamelCase(key)] = value;
|
|
27224
27460
|
}
|
|
27461
|
+
}
|
|
27462
|
+
if (!insidePreservedForEach) {
|
|
27225
27463
|
const hasUnresolvedProps = Object.values(props).some((v) => hasBindings(v));
|
|
27226
27464
|
if (!hasUnresolvedProps) {
|
|
27227
27465
|
const prefix = buildCoordinatePrefix(element);
|
|
@@ -27240,20 +27478,43 @@ function discoverHeadlessInstances(preRenderedJayHtml) {
|
|
|
27240
27478
|
coordinate
|
|
27241
27479
|
});
|
|
27242
27480
|
}
|
|
27481
|
+
} else {
|
|
27482
|
+
const innerForEach = forEachContexts[forEachContexts.length - 1];
|
|
27483
|
+
if (innerForEach) {
|
|
27484
|
+
let ref = element.getAttribute("ref");
|
|
27485
|
+
if (!ref) {
|
|
27486
|
+
const counterKey = ["forEach", contractName].join("/");
|
|
27487
|
+
const localIndex = coordinateCounters.get(counterKey) ?? 0;
|
|
27488
|
+
coordinateCounters.set(counterKey, localIndex + 1);
|
|
27489
|
+
ref = String(localIndex);
|
|
27490
|
+
}
|
|
27491
|
+
forEachInstances.push({
|
|
27492
|
+
contractName,
|
|
27493
|
+
forEachPath: innerForEach.forEachPath,
|
|
27494
|
+
trackBy: innerForEach.trackBy,
|
|
27495
|
+
propBindings: props,
|
|
27496
|
+
coordinateSuffix: `${contractName}:${ref}`
|
|
27497
|
+
});
|
|
27498
|
+
}
|
|
27243
27499
|
}
|
|
27244
27500
|
return;
|
|
27245
27501
|
}
|
|
27502
|
+
const updatedForEachContexts = hasForEach && forEachAttr && trackByAttr ? [...forEachContexts, { forEachPath: forEachAttr, trackBy: trackByAttr }] : forEachContexts;
|
|
27246
27503
|
for (const child of element.childNodes) {
|
|
27247
27504
|
if (child.nodeType === NodeType.ELEMENT_NODE) {
|
|
27248
|
-
walk(
|
|
27505
|
+
walk(
|
|
27506
|
+
child,
|
|
27507
|
+
insidePreservedForEach || hasForEach,
|
|
27508
|
+
updatedForEachContexts
|
|
27509
|
+
);
|
|
27249
27510
|
}
|
|
27250
27511
|
}
|
|
27251
27512
|
}
|
|
27252
27513
|
const body = root.querySelector("body");
|
|
27253
27514
|
if (body) {
|
|
27254
|
-
walk(body, false);
|
|
27515
|
+
walk(body, false, []);
|
|
27255
27516
|
}
|
|
27256
|
-
return { instances, preRenderedJayHtml: root.toString() };
|
|
27517
|
+
return { instances, forEachInstances, preRenderedJayHtml: root.toString() };
|
|
27257
27518
|
}
|
|
27258
27519
|
function coordinateKey(coord) {
|
|
27259
27520
|
return coord.join("/");
|
|
@@ -27344,11 +27605,13 @@ export {
|
|
|
27344
27605
|
ContractTagType,
|
|
27345
27606
|
JAY_IMPORT_RESOLVER,
|
|
27346
27607
|
buildInstanceCoordinateKey,
|
|
27608
|
+
compileAction,
|
|
27347
27609
|
compileContract,
|
|
27348
27610
|
contractToAllPhaseViewStates,
|
|
27349
27611
|
contractToImportsViewStateAndRefs,
|
|
27350
27612
|
contractToPhaseViewState,
|
|
27351
27613
|
createPhaseContract,
|
|
27614
|
+
defaultContractResolver,
|
|
27352
27615
|
discoverHeadlessInstances,
|
|
27353
27616
|
filterTagsByPhase,
|
|
27354
27617
|
generateElementBridgeFile,
|
|
@@ -27363,6 +27626,7 @@ export {
|
|
|
27363
27626
|
hasSlowPhaseProperties,
|
|
27364
27627
|
isTagInPhase,
|
|
27365
27628
|
loadLinkedContract,
|
|
27629
|
+
parseAction,
|
|
27366
27630
|
parseContract,
|
|
27367
27631
|
parseEnumValues,
|
|
27368
27632
|
parseIsEnum,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jay-framework/compiler-jay-html",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -34,12 +34,12 @@
|
|
|
34
34
|
},
|
|
35
35
|
"author": "",
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@jay-framework/compiler-analyze-exported-types": "^0.
|
|
38
|
-
"@jay-framework/compiler-shared": "^0.
|
|
39
|
-
"@jay-framework/component": "^0.
|
|
40
|
-
"@jay-framework/logger": "^0.
|
|
41
|
-
"@jay-framework/runtime": "^0.
|
|
42
|
-
"@jay-framework/secure": "^0.
|
|
37
|
+
"@jay-framework/compiler-analyze-exported-types": "^0.13.0",
|
|
38
|
+
"@jay-framework/compiler-shared": "^0.13.0",
|
|
39
|
+
"@jay-framework/component": "^0.13.0",
|
|
40
|
+
"@jay-framework/logger": "^0.13.0",
|
|
41
|
+
"@jay-framework/runtime": "^0.13.0",
|
|
42
|
+
"@jay-framework/secure": "^0.13.0",
|
|
43
43
|
"@types/js-yaml": "^4.0.9",
|
|
44
44
|
"change-case": "^4.1.2",
|
|
45
45
|
"js-yaml": "^4.1.0",
|
|
@@ -51,8 +51,8 @@
|
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
53
|
"@caiogondim/strip-margin": "^1.0.0",
|
|
54
|
-
"@jay-framework/4-react": "^0.
|
|
55
|
-
"@jay-framework/dev-environment": "^0.
|
|
54
|
+
"@jay-framework/4-react": "^0.13.0",
|
|
55
|
+
"@jay-framework/dev-environment": "^0.13.0",
|
|
56
56
|
"@testing-library/jest-dom": "^6.2.0",
|
|
57
57
|
"@types/js-beautify": "^1",
|
|
58
58
|
"@types/node": "^20.11.5",
|