@ng-forge/dynamic-forms 0.10.0-next.2 → 0.10.0-next.4

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 (60) hide show
  1. package/fesm2022/{ng-forge-dynamic-forms-array-field.component-PPh_uVVh.mjs → ng-forge-dynamic-forms-array-field.component-BfRH3TBm.mjs} +4 -3
  2. package/fesm2022/ng-forge-dynamic-forms-array-field.component-BfRH3TBm.mjs.map +1 -0
  3. package/fesm2022/{ng-forge-dynamic-forms-container-field.component-3dZDF3kh.mjs → ng-forge-dynamic-forms-container-field.component-bA-8ICHF.mjs} +4 -3
  4. package/fesm2022/ng-forge-dynamic-forms-container-field.component-bA-8ICHF.mjs.map +1 -0
  5. package/fesm2022/{ng-forge-dynamic-forms-container-utils-B7kCznbc.mjs → ng-forge-dynamic-forms-container-utils-BvXn8T_i.mjs} +2 -2
  6. package/fesm2022/ng-forge-dynamic-forms-container-utils-BvXn8T_i.mjs.map +1 -0
  7. package/fesm2022/{ng-forge-dynamic-forms-css-wrapper.component-DtWBauY3.mjs → ng-forge-dynamic-forms-css-wrapper.component-DjhcONFh.mjs} +2 -2
  8. package/fesm2022/ng-forge-dynamic-forms-css-wrapper.component-DjhcONFh.mjs.map +1 -0
  9. package/fesm2022/{ng-forge-dynamic-forms-emit-initialization-CbY8WZZB.mjs → ng-forge-dynamic-forms-emit-initialization-DhHwU6FJ.mjs} +2 -2
  10. package/fesm2022/ng-forge-dynamic-forms-emit-initialization-DhHwU6FJ.mjs.map +1 -0
  11. package/fesm2022/{ng-forge-dynamic-forms-group-field.component-Dr8Z628-.mjs → ng-forge-dynamic-forms-group-field.component-D0_nmXf2.mjs} +4 -3
  12. package/fesm2022/ng-forge-dynamic-forms-group-field.component-D0_nmXf2.mjs.map +1 -0
  13. package/fesm2022/ng-forge-dynamic-forms-integration.mjs +14 -10
  14. package/fesm2022/ng-forge-dynamic-forms-integration.mjs.map +1 -1
  15. package/fesm2022/ng-forge-dynamic-forms-internal.mjs +3372 -0
  16. package/fesm2022/ng-forge-dynamic-forms-internal.mjs.map +1 -0
  17. package/fesm2022/{ng-forge-dynamic-forms-ng-forge-dynamic-forms-ww_BnAt_.mjs → ng-forge-dynamic-forms-ng-forge-dynamic-forms-CNc4a_Xv.mjs} +725 -3866
  18. package/fesm2022/ng-forge-dynamic-forms-ng-forge-dynamic-forms-CNc4a_Xv.mjs.map +1 -0
  19. package/fesm2022/{ng-forge-dynamic-forms-page-field.component-DoGKs3LN.mjs → ng-forge-dynamic-forms-page-field.component-BJRNnoz-.mjs} +4 -3
  20. package/fesm2022/ng-forge-dynamic-forms-page-field.component-BJRNnoz-.mjs.map +1 -0
  21. package/fesm2022/{ng-forge-dynamic-forms-row-wrapper.component-CFtnatYj.mjs → ng-forge-dynamic-forms-row-wrapper.component-CwkYn5yl.mjs} +1 -1
  22. package/fesm2022/ng-forge-dynamic-forms-row-wrapper.component-CwkYn5yl.mjs.map +1 -0
  23. package/fesm2022/ng-forge-dynamic-forms-schema.mjs.map +1 -1
  24. package/fesm2022/{ng-forge-dynamic-forms-text-field.component-C3TPVvj_.mjs → ng-forge-dynamic-forms-text-field.component-DABkT0O7.mjs} +2 -2
  25. package/fesm2022/ng-forge-dynamic-forms-text-field.component-DABkT0O7.mjs.map +1 -0
  26. package/fesm2022/ng-forge-dynamic-forms.mjs +2 -1
  27. package/fesm2022/ng-forge-dynamic-forms.mjs.map +1 -1
  28. package/package.json +14 -1
  29. package/schematics/collection.json +10 -0
  30. package/schematics/generators.json +12 -0
  31. package/schematics/ng-add/index.js +87 -0
  32. package/schematics/ng-add/index.js.map +1 -0
  33. package/schematics/ng-add/nx-wrapper.js +5 -0
  34. package/schematics/ng-add/nx-wrapper.js.map +1 -0
  35. package/schematics/ng-add/recipes.js +60 -0
  36. package/schematics/ng-add/recipes.js.map +1 -0
  37. package/schematics/ng-add/rules/add-packages.js +73 -0
  38. package/schematics/ng-add/rules/add-packages.js.map +1 -0
  39. package/schematics/ng-add/rules/add-providers.js +132 -0
  40. package/schematics/ng-add/rules/add-providers.js.map +1 -0
  41. package/schematics/ng-add/rules/add-styles.js +123 -0
  42. package/schematics/ng-add/rules/add-styles.js.map +1 -0
  43. package/schematics/ng-add/schema.json +48 -0
  44. package/schematics/package.json +1 -0
  45. package/schematics/versions.js +33 -0
  46. package/schematics/versions.js.map +1 -0
  47. package/types/ng-forge-dynamic-forms-integration.d.ts +31 -464
  48. package/types/ng-forge-dynamic-forms-internal.d.ts +3508 -0
  49. package/types/ng-forge-dynamic-forms-schema.d.ts +2 -117
  50. package/types/ng-forge-dynamic-forms.d.ts +87 -7170
  51. package/fesm2022/ng-forge-dynamic-forms-array-field.component-PPh_uVVh.mjs.map +0 -1
  52. package/fesm2022/ng-forge-dynamic-forms-container-field.component-3dZDF3kh.mjs.map +0 -1
  53. package/fesm2022/ng-forge-dynamic-forms-container-utils-B7kCznbc.mjs.map +0 -1
  54. package/fesm2022/ng-forge-dynamic-forms-css-wrapper.component-DtWBauY3.mjs.map +0 -1
  55. package/fesm2022/ng-forge-dynamic-forms-emit-initialization-CbY8WZZB.mjs.map +0 -1
  56. package/fesm2022/ng-forge-dynamic-forms-group-field.component-Dr8Z628-.mjs.map +0 -1
  57. package/fesm2022/ng-forge-dynamic-forms-ng-forge-dynamic-forms-ww_BnAt_.mjs.map +0 -1
  58. package/fesm2022/ng-forge-dynamic-forms-page-field.component-DoGKs3LN.mjs.map +0 -1
  59. package/fesm2022/ng-forge-dynamic-forms-row-wrapper.component-CFtnatYj.mjs.map +0 -1
  60. package/fesm2022/ng-forge-dynamic-forms-text-field.component-C3TPVvj_.mjs.map +0 -1
@@ -1,9 +1,10 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { inject, DestroyRef, Injector, EnvironmentInjector, input, computed, ChangeDetectionStrategy, Component } from '@angular/core';
3
- import { ao as injectFieldRegistry, E as EventBus, s as DynamicFormLogger, b2 as validatePageNesting, aa as derivedFromDeferred, a4 as createFieldResolutionPipe, o as DfFieldOutlet } from './ng-forge-dynamic-forms-ng-forge-dynamic-forms-ww_BnAt_.mjs';
3
+ import { L as injectFieldRegistry, s as createFieldResolutionPipe, h as DfFieldOutlet } from './ng-forge-dynamic-forms-ng-forge-dynamic-forms-CNc4a_Xv.mjs';
4
4
  import { outputFromObservable } from '@angular/core/rxjs-interop';
5
5
  import { explicitEffect } from 'ngxtension/explicit-effect';
6
- import { c as computeContainerHostClasses, s as setupContainerInitEffect } from './ng-forge-dynamic-forms-container-utils-B7kCznbc.mjs';
6
+ import { EventBus, DynamicFormLogger, validatePageNesting, derivedFromDeferred } from '@ng-forge/dynamic-forms/internal';
7
+ import { c as computeContainerHostClasses, s as setupContainerInitEffect } from './ng-forge-dynamic-forms-container-utils-BvXn8T_i.mjs';
7
8
 
8
9
  class PageFieldComponent {
9
10
  destroyRef = inject(DestroyRef);
@@ -83,4 +84,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
83
84
  }], ctorParameters: () => [], propDecorators: { field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: true }] }], key: [{ type: i0.Input, args: [{ isSignal: true, alias: "key", required: true }] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], pageIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageIndex", required: true }] }], isVisible: [{ type: i0.Input, args: [{ isSignal: true, alias: "isVisible", required: true }] }], nextPage: [{ type: i0.Output, args: ["nextPage"] }], previousPage: [{ type: i0.Output, args: ["previousPage"] }], pageChange: [{ type: i0.Output, args: ["pageChange"] }] } });
84
85
 
85
86
  export { PageFieldComponent as default };
86
- //# sourceMappingURL=ng-forge-dynamic-forms-page-field.component-DoGKs3LN.mjs.map
87
+ //# sourceMappingURL=ng-forge-dynamic-forms-page-field.component-BJRNnoz-.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ng-forge-dynamic-forms-page-field.component-BJRNnoz-.mjs","mappings":";;;;;;;;AAsCc;YAKK;;AAEA;AACE;AACF;AACA;YAMjB;AACA;;AAEA;AACA;YAMS;AAEA;AAEA;AAEQ;YAMR;AACA;AACA;YAMQ;AACf;AACE;;;AAGJ;AAEmB;AAGf;AACA;;;AAGA;AACE;;;;AAOH;YAQH;;;;;AAWE;;;;;AAQA;;;AA/FiB;;;;;;AAdlB;;;;AARS;;;;;;;AAWR;AACA;AACA;AACA;AACA;AACA;AACA;AACA","names":[],"ignoreList":[],"sources":["../../../../packages/dynamic-forms/src/lib/fields/page/page-field.component.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, computed, DestroyRef, EnvironmentInjector, inject, Injector, input } from '@angular/core';\nimport { DfFieldOutlet } from '../../directives/df-field-outlet/df-field-outlet.directive';\nimport { outputFromObservable } from '@angular/core/rxjs-interop';\nimport { explicitEffect } from 'ngxtension/explicit-effect';\nimport { derivedFromDeferred } from '@ng-forge/dynamic-forms/internal';\nimport { createFieldResolutionPipe, ResolvedField } from '../../utils/resolve-field/resolve-field';\nimport { computeContainerHostClasses, setupContainerInitEffect } from '../../utils/container-utils/container-utils';\nimport { PageField, validatePageNesting } from '@ng-forge/dynamic-forms/internal';\nimport { injectFieldRegistry } from '../../utils/inject-field-registry/inject-field-registry';\nimport { EventBus } from '@ng-forge/dynamic-forms/internal';\nimport { NextPageEvent, PageChangeEvent, PreviousPageEvent } from '../../events/constants';\nimport { FieldDef } from '@ng-forge/dynamic-forms/internal';\nimport { DynamicFormLogger } from '@ng-forge/dynamic-forms/internal';\n\n/** Renders a single page in multi-page (wizard) forms. */\n@Component({\n selector: 'section[page-field]',\n imports: [DfFieldOutlet],\n template: `\n @for (field of resolvedFields(); track field.key) {\n @if (!field.hidden()) {\n <ng-container *dfFieldOutlet=\"field; environmentInjector: environmentInjector\" />\n }\n }\n `,\n styleUrl: './page-field.component.scss',\n host: {\n '[class]': 'hostClasses()',\n '[class.disabled]': 'disabled()',\n '[class.df-page-visible]': 'isVisible()',\n '[class.df-page-hidden]': '!isVisible()',\n '[attr.aria-hidden]': '!isVisible()',\n '[attr.data-page-index]': 'pageIndex()',\n '[id]': '`${key()}`',\n '[attr.data-testid]': 'key()',\n },\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport default class PageFieldComponent {\n // ─────────────────────────────────────────────────────────────────────────────\n // Dependencies\n // ─────────────────────────────────────────────────────────────────────────────\n\n private readonly destroyRef = inject(DestroyRef);\n private readonly fieldRegistry = injectFieldRegistry();\n private readonly injector = inject(Injector);\n protected readonly environmentInjector = inject(EnvironmentInjector);\n private readonly eventBus = inject(EventBus);\n private readonly logger = inject(DynamicFormLogger);\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Inputs\n // ─────────────────────────────────────────────────────────────────────────────\n\n field = input.required<PageField>();\n key = input.required<string>();\n className = input<string>();\n pageIndex = input.required<number>();\n isVisible = input.required<boolean>();\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Computed Signals\n // ─────────────────────────────────────────────────────────────────────────────\n\n readonly hostClasses = computed(() => computeContainerHostClasses('page-field', this.className()));\n\n readonly disabled = computed(() => this.field().disabled || false);\n\n readonly isValid = computed(() => validatePageNesting(this.field()));\n\n private readonly rawFieldRegistry = computed(() => this.fieldRegistry.raw);\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Outputs\n // ─────────────────────────────────────────────────────────────────────────────\n\n readonly nextPage = outputFromObservable(this.eventBus.on<NextPageEvent>('next-page'));\n readonly previousPage = outputFromObservable(this.eventBus.on<PreviousPageEvent>('previous-page'));\n readonly pageChange = outputFromObservable(this.eventBus.on<PageChangeEvent>('page-change'));\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Field Resolution\n // ─────────────────────────────────────────────────────────────────────────────\n\n private readonly fieldsSource = computed(() => {\n if (!this.isValid()) {\n return [];\n }\n return this.field().fields || [];\n });\n\n protected readonly resolvedFields = derivedFromDeferred(\n this.fieldsSource,\n createFieldResolutionPipe(() => ({\n loadTypeComponent: (type: string) => this.fieldRegistry.loadTypeComponent(type),\n registry: this.rawFieldRegistry(),\n injector: this.injector,\n destroyRef: this.destroyRef,\n onError: (fieldDef: FieldDef<unknown>, error: unknown) => {\n const fieldKey = fieldDef.key || '<no key>';\n this.logger.error(\n `Failed to load component for field type '${fieldDef.type}' (key: ${fieldKey}) ` +\n `within page '${this.field().key}'. Ensure the field type is registered in your field registry.`,\n error,\n );\n },\n })),\n { initialValue: [] as ResolvedField[], injector: this.injector },\n );\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Constructor\n // ─────────────────────────────────────────────────────────────────────────────\n\n constructor() {\n this.setupEffects();\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Private Methods\n // ─────────────────────────────────────────────────────────────────────────────\n\n private setupEffects(): void {\n setupContainerInitEffect(this.resolvedFields, this.eventBus, 'page', () => this.field().key, this.injector);\n\n explicitEffect([this.isValid, this.field], ([valid, pageField]) => {\n if (!valid) {\n this.logger.error(\n `Invalid configuration: Page '${pageField.key}' contains nested page fields. ` +\n `Pages cannot contain other pages. Consider using groups or rows for nested structure.`,\n pageField,\n );\n }\n });\n }\n}\n"]}
@@ -15,4 +15,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
15
15
  }], propDecorators: { fieldComponent: [{ type: i0.ViewChild, args: ['fieldComponent', { ...{ read: ViewContainerRef }, isSignal: true }] }], fieldInputs: [{ type: i0.Input, args: [{ isSignal: true, alias: "fieldInputs", required: false }] }] } });
16
16
 
17
17
  export { RowWrapperComponent, RowWrapperComponent as default };
18
- //# sourceMappingURL=ng-forge-dynamic-forms-row-wrapper.component-CFtnatYj.mjs.map
18
+ //# sourceMappingURL=ng-forge-dynamic-forms-row-wrapper.component-CwkYn5yl.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ng-forge-dynamic-forms-row-wrapper.component-CwkYn5yl.mjs","mappings":";;;AAcc;AACH;AAET;;;;;;;AAPE;;AAK2C","names":[],"ignoreList":[],"sources":["../../../../packages/dynamic-forms/src/lib/wrappers/row/row-wrapper.component.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, input, ViewContainerRef, viewChild } from '@angular/core';\nimport { FieldWrapper } from '@ng-forge/dynamic-forms/internal';\nimport { WrapperFieldInputs } from '@ng-forge/dynamic-forms/internal';\n\n/** Built-in row wrapper component. */\n@Component({\n selector: 'df-row-wrapper',\n template: `<ng-container #fieldComponent></ng-container>`,\n styleUrl: './row-wrapper.component.scss',\n host: {\n class: 'df-field df-row',\n },\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport default class RowWrapperComponent implements FieldWrapper {\n readonly fieldComponent = viewChild.required('fieldComponent', { read: ViewContainerRef });\n\n /**\n * Mapper outputs for the wrapped field.\n * The row wrapper itself does not consume these — the input is accepted\n * for contract consistency so the outlet can set it on any wrapper.\n */\n readonly fieldInputs = input<WrapperFieldInputs>();\n}\n\nexport { RowWrapperComponent };\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"ng-forge-dynamic-forms-schema.mjs","mappings":"AAOO;AAiJD;;AAEF;;;AAGJ;AAuBM;AACJ;AAEE;AACA;;;AAIJ","names":[],"ignoreList":[],"sources":["../../../../packages/dynamic-forms/schema/src/standard-schema-marker.ts"],"sourcesContent":["import type { SchemaPath, SchemaPathTree } from '@angular/forms/signals';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\n\n/**\n * Internal marker symbol to identify StandardSchemaMarker instances.\n * Uses ɵ prefix following Angular's convention for internal APIs.\n */\nexport const STANDARD_SCHEMA_KIND = 'standardSchema' as const;\n\n/**\n * Angular schema callback function type.\n * This is the raw callback that Angular's schema() function accepts.\n *\n * Use this when you want to write Angular schema validation directly\n * without any wrapper. This is ideal for Angular-only projects that\n * want full access to Angular's form validation APIs.\n *\n * @typeParam T - The form value type\n *\n * @example\n * ```typescript\n * import { FormConfig } from '@ng-forge/dynamic-forms';\n * import { validateTree } from '@angular/forms/signals';\n *\n * const config = {\n * // Raw callback - no wrapper needed!\n * schema: (path) => {\n * validateTree(path, (ctx) => {\n * const { password, confirmPassword } = ctx.value();\n * if (password !== confirmPassword) {\n * return [{ kind: 'passwordMismatch', fieldTree: ctx.fieldTreeOf(path).confirmPassword }];\n * }\n * return null;\n * });\n * },\n * fields: [\n * { key: 'password', type: 'input', label: 'Password', required: true },\n * { key: 'confirmPassword', type: 'input', label: 'Confirm', required: true,\n * validationMessages: { passwordMismatch: 'Passwords must match' } },\n * ],\n * } as const satisfies FormConfig;\n * ```\n */\nexport type AngularSchemaCallback<T = unknown> = (path: SchemaPath<T> & SchemaPathTree<T>) => void;\n\n/**\n * Wrapper interface that marks a schema as implementing the Standard Schema spec.\n * This allows the dynamic forms system to identify and use standard-compliant schemas\n * for validation without coupling to any specific schema library.\n *\n * The `out` variance modifier makes this type covariant in T, meaning a\n * StandardSchemaMarker<SpecificType> is assignable to StandardSchemaMarker<BroaderType>.\n * This enables Zod/Valibot schemas to be used with FormConfig's inferred TValue.\n *\n * @typeParam T - The inferred type that the schema validates to (covariant)\n *\n * @example\n * ```typescript\n * import { z } from 'zod';\n * import { standardSchema } from '@ng-forge/dynamic-forms/schema';\n *\n * const userSchema = z.object({\n * name: z.string(),\n * email: z.string().email(),\n * });\n *\n * // Wrap the Zod schema with standardSchema marker\n * const formSchema = standardSchema(userSchema);\n * ```\n */\nexport interface StandardSchemaMarker<out T = unknown> {\n /**\n * Internal marker identifying this as a standard schema wrapper.\n * @internal\n */\n readonly ɵkind: typeof STANDARD_SCHEMA_KIND;\n\n /**\n * The underlying schema that implements the Standard Schema spec.\n */\n readonly schema: StandardSchemaV1<T>;\n}\n\n/**\n * Type alias for form schemas that can be used with dynamic forms.\n * Supports both Standard Schema compliant schemas (Zod, Valibot, ArkType)\n * and raw Angular schema callbacks.\n *\n * The covariance from StandardSchemaMarker flows through, allowing\n * schemas with specific types to be assigned to broader form value types.\n *\n * @typeParam T - The inferred type that the schema validates to\n *\n * @example Standard Schema (Zod)\n * ```typescript\n * import { z } from 'zod';\n * import { standardSchema } from '@ng-forge/dynamic-forms/schema';\n *\n * const config = {\n * schema: standardSchema(z.object({ ... })),\n * fields: [...],\n * };\n * ```\n *\n * @example Angular Schema Callback\n * ```typescript\n * import { validateTree } from '@angular/forms/signals';\n *\n * const config = {\n * schema: (path) => {\n * validateTree(path, (ctx) => { ... });\n * },\n * fields: [...],\n * };\n * ```\n */\nexport type FormSchema<T = unknown> = StandardSchemaMarker<T> | AngularSchemaCallback<T>;\n\n/**\n * Wraps a Standard Schema compliant schema for use with dynamic forms.\n *\n * This function creates a marker wrapper around schemas that implement the\n * Standard Schema spec (Zod, Valibot, ArkType, etc.), allowing the dynamic\n * forms system to identify and use them for validation.\n *\n * @typeParam T - The inferred type that the schema validates to\n * @param schema - A schema implementing the Standard Schema V1 spec\n * @returns A wrapped schema marker that can be passed to dynamic form configuration\n *\n * @example\n * ```typescript\n * import { z } from 'zod';\n * import { standardSchema } from '@ng-forge/dynamic-forms/schema';\n *\n * const loginSchema = z.object({\n * email: z.string().email('Invalid email format'),\n * password: z.string().min(8, 'Password must be at least 8 characters'),\n * });\n *\n * // Wrap for use with dynamic forms\n * const formSchema = standardSchema(loginSchema);\n *\n * // Use in form configuration\n * const formConfig = {\n * schema: formSchema,\n * fields: [\n * input({ key: 'email', label: 'Email' }),\n * input({ key: 'password', label: 'Password', props: { type: 'password' } }),\n * ],\n * };\n * ```\n */\nexport function standardSchema<T>(schema: StandardSchemaV1<T>): StandardSchemaMarker<T> {\n return {\n ɵkind: STANDARD_SCHEMA_KIND,\n schema,\n };\n}\n\n/**\n * Type guard to check if a value is a StandardSchemaMarker.\n *\n * This is useful when processing form configuration to determine if a schema\n * has been provided and should be used for validation.\n *\n * @param value - The value to check\n * @returns True if the value is a StandardSchemaMarker, false otherwise\n *\n * @example\n * ```typescript\n * import { isStandardSchemaMarker } from '@ng-forge/dynamic-forms/schema';\n *\n * function processFormConfig(config: FormConfig) {\n * if (config.schema && isStandardSchemaMarker(config.schema)) {\n * // Use the schema for validation\n * const result = config.schema.schema['~standard'].validate(formValue);\n * }\n * }\n * ```\n */\nexport function isStandardSchemaMarker(value: unknown): value is StandardSchemaMarker {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'ɵkind' in value &&\n (value as StandardSchemaMarker).ɵkind === STANDARD_SCHEMA_KIND &&\n 'schema' in value\n );\n}\n\n/**\n * Extracts the output type from a Standard Schema.\n * Works with Zod, Valibot, ArkType, and other Standard Schema compliant libraries.\n */\nexport type InferSchemaOutput<T> = T extends StandardSchemaV1<infer Output> ? Output : never;\n"]}
1
+ {"version":3,"file":"ng-forge-dynamic-forms-schema.mjs","mappings":"AAOO;AA6CD;;AAEF;;;AAGJ;AAQM;AACJ;AAEE;AACA;;;AAIJ","names":[],"ignoreList":[],"sources":["../../../../packages/dynamic-forms/schema/src/standard-schema-marker.ts"],"sourcesContent":["import type { SchemaPath, SchemaPathTree } from '@angular/forms/signals';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\n\n/**\n * Internal marker symbol to identify StandardSchemaMarker instances.\n * Uses ɵ prefix following Angular's convention for internal APIs.\n */\nexport const STANDARD_SCHEMA_KIND = 'standardSchema' as const;\n\n/**\n * Angular schema callback function type.\n * This is the raw callback that Angular's schema() function accepts.\n *\n * @typeParam T - The form value type\n */\nexport type AngularSchemaCallback<T = unknown> = (path: SchemaPath<T> & SchemaPathTree<T>) => void;\n\n/**\n * Wrapper interface that marks a schema as implementing the Standard Schema spec.\n * This allows the dynamic forms system to identify and use standard-compliant schemas\n * for validation without coupling to any specific schema library.\n *\n * @typeParam T - The inferred type that the schema validates to (covariant)\n */\nexport interface StandardSchemaMarker<out T = unknown> {\n /**\n * Internal marker identifying this as a standard schema wrapper.\n *\n * @internal\n */\n readonly ɵkind: typeof STANDARD_SCHEMA_KIND;\n\n /** The underlying schema that implements the Standard Schema spec. */\n readonly schema: StandardSchemaV1<T>;\n}\n\n/**\n * Type alias for form schemas that can be used with dynamic forms.\n * Supports both Standard Schema compliant schemas (Zod, Valibot, ArkType)\n * and raw Angular schema callbacks.\n *\n * @typeParam T - The inferred type that the schema validates to\n */\nexport type FormSchema<T = unknown> = StandardSchemaMarker<T> | AngularSchemaCallback<T>;\n\n/**\n * Wraps a Standard Schema compliant schema for use with dynamic forms.\n *\n * @typeParam T - The inferred type that the schema validates to\n * @param schema - A schema implementing the Standard Schema V1 spec\n * @returns A wrapped schema marker that can be passed to dynamic form configuration\n */\nexport function standardSchema<T>(schema: StandardSchemaV1<T>): StandardSchemaMarker<T> {\n return {\n ɵkind: STANDARD_SCHEMA_KIND,\n schema,\n };\n}\n\n/**\n * Type guard to check if a value is a StandardSchemaMarker.\n *\n * @param value - The value to check\n * @returns True if the value is a StandardSchemaMarker, false otherwise\n */\nexport function isStandardSchemaMarker(value: unknown): value is StandardSchemaMarker {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'ɵkind' in value &&\n (value as StandardSchemaMarker).ɵkind === STANDARD_SCHEMA_KIND &&\n 'schema' in value\n );\n}\n\n/**\n * Extracts the output type from a Standard Schema.\n * Works with Zod, Valibot, ArkType, and other Standard Schema compliant libraries.\n */\nexport type InferSchemaOutput<T> = T extends StandardSchemaV1<infer Output> ? Output : never;\n"]}
@@ -1,7 +1,7 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { input, computed, ChangeDetectionStrategy, Component } from '@angular/core';
3
3
  import { AsyncPipe } from '@angular/common';
4
- import { t as DynamicTextPipe } from './ng-forge-dynamic-forms-ng-forge-dynamic-forms-ww_BnAt_.mjs';
4
+ import { DynamicTextPipe } from '@ng-forge/dynamic-forms/internal';
5
5
 
6
6
  class TextFieldComponent {
7
7
  key = input.required(...(ngDevMode ? [{ debugName: "key" }] : /* istanbul ignore next */ []));
@@ -116,4 +116,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
116
116
  }], propDecorators: { key: [{ type: i0.Input, args: [{ isSignal: true, alias: "key", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], tabIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabIndex", required: false }] }], hidden: [{ type: i0.Input, args: [{ isSignal: true, alias: "hidden", required: false }] }], props: [{ type: i0.Input, args: [{ isSignal: true, alias: "props", required: false }] }] } });
117
117
 
118
118
  export { TextFieldComponent as default };
119
- //# sourceMappingURL=ng-forge-dynamic-forms-text-field.component-C3TPVvj_.mjs.map
119
+ //# sourceMappingURL=ng-forge-dynamic-forms-text-field.component-DABkT0O7.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ng-forge-dynamic-forms-text-field.component-DABkT0O7.mjs","sources":["../../../../packages/dynamic-forms/src/lib/fields/text/text-field.component.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';\nimport { AsyncPipe } from '@angular/common';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms/internal';\nimport { TextProps } from '@ng-forge/dynamic-forms/internal';\nimport { DynamicText } from '@ng-forge/dynamic-forms/internal';\n\n@Component({\n selector: 'df-text',\n template: `\n @switch (elementType()) {\n @case ('p') {\n <p class=\"df-text df-text-p\" [class]=\"className() || ''\">\n {{ label() | dynamicText | async }}\n </p>\n }\n @case ('h1') {\n <h1 class=\"df-text df-text-h1\" [class]=\"className() || ''\">\n {{ label() | dynamicText | async }}\n </h1>\n }\n @case ('h2') {\n <h2 class=\"df-text df-text-h2\" [class]=\"className() || ''\">\n {{ label() | dynamicText | async }}\n </h2>\n }\n @case ('h3') {\n <h3 class=\"df-text df-text-h3\" [class]=\"className() || ''\">\n {{ label() | dynamicText | async }}\n </h3>\n }\n @case ('h4') {\n <h4 class=\"df-text df-text-h4\" [class]=\"className() || ''\">\n {{ label() | dynamicText | async }}\n </h4>\n }\n @case ('h5') {\n <h5 class=\"df-text df-text-h5\" [class]=\"className() || ''\">\n {{ label() | dynamicText | async }}\n </h5>\n }\n @case ('h6') {\n <h6 class=\"df-text df-text-h6\" [class]=\"className() || ''\">\n {{ label() | dynamicText | async }}\n </h6>\n }\n @case ('span') {\n <span class=\"df-text df-text-span\" [class]=\"className() || ''\">\n {{ label() | dynamicText | async }}\n </span>\n }\n @default {\n never;\n }\n }\n `,\n styleUrl: './text-field.component.scss',\n imports: [AsyncPipe, DynamicTextPipe],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n '[id]': '`${key()}`',\n '[attr.data-testid]': 'key()',\n '[attr.hidden]': 'hidden() || null',\n },\n})\nexport default class TextFieldComponent {\n readonly key = input.required<string>();\n readonly label = input.required<DynamicText>();\n readonly className = input<string>();\n readonly tabIndex = input<number>();\n readonly hidden = input<boolean>();\n readonly props = input<TextProps>();\n\n readonly elementType = computed(() => this.props()?.elementType || 'p');\n}\n"],"names":[],"mappings":"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"}
@@ -1,2 +1,3 @@
1
- export { A as ADDON_ACTION_HANDLERS, a as ADDON_ACTION_REGISTRY, b as ADDON_KIND_COMPONENT_CACHE, c as ADDON_KIND_DEFINITIONS, d as ADDON_KIND_REGISTRY, e as ARRAY_CONTEXT, f as AppendArrayItemEvent, B as BUILT_IN_FIELDS, h as ComponentAddonComponent, j as ConsoleLogger, D as DEFAULT_PROPS, k as DEFAULT_VALIDATION_MESSAGES, l as DEFAULT_WRAPPERS, m as DF_FIELD_TEMPLATES, n as DfAddonSlot, p as DfTemplate, q as DynamicForm, r as DynamicFormError, s as DynamicFormLogger, t as DynamicTextPipe, E as EventBus, u as EventDispatcher, F as FIELD_REGISTRY, v as FIELD_SIGNAL_CONTEXT, w as FORM_OPTIONS, x as FormClearEvent, y as FormResetEvent, G as GROUP_CONTEXT, I as INITIALIZATION_TIMEOUT_MS, z as InsertArrayItemEvent, M as MoveArrayItemEvent, N as NextPageEvent, H as NoopLogger, P as PageChangeEvent, J as PopArrayItemEvent, K as PrependArrayItemEvent, L as PreviousPageEvent, R as RemoveAtIndexEvent, O as RootFormRegistryService, S as ShiftArrayItemEvent, Q as SubmitEvent, T as TemplateAddonComponent, U as TextAddonComponent, V as WRAPPER_REGISTRY, X as applyMetaToElement, Y as applyValidator, Z as applyValidators, _ as arrayEvent, $ as arrayFieldMapper, a0 as baseFieldMapper, a1 as buildBaseInputs, a2 as containerFieldMapper, a3 as createField, a9 as createWrappers, ab as dynamicTextToObservable, ac as evaluateNonFieldDisabled, ad as evaluateNonFieldHidden, ae as field, af as formConfig, ag as formatAddonWarning, ah as getArrayLength, al as groupFieldMapper, am as hasFormValue, an as injectAddonKindRegistry, ap as injectFieldSignalContext, aq as injectFieldsSupportingAddons, ar as interpolateParams, as as isArrayField, at as isCheckedField, au as isContainerField, av as isContainerTypedField, aw as isDisplayOnlyField, ax as isEqual, ay as isFieldBoundContext, az as isFormStateCondition, aA as isGroupField, aB as isLeafField, aC as isPageField, aD as isRowField, aF as isSimplifiedArrayField, aG as isValueBearingField, aH as isValueField, aI as isWrapperTypeDefinition, aJ as isWrappersBundle, aK as logAddonWarnings, aM as omit, aN as pageFieldMapper, aO as provideAddonActions, aP as provideDynamicForm, aQ as resolveDynamicValue, aR as resolveNextButtonDisabled, aS as resolveNonFieldDisabled, aT as resolveNonFieldHidden, aU as resolveSubmitButtonDisabled, aV as resolveTokens, aX as rowFieldMapper, aY as runPresetAction, aZ as sanitizeFormConfig, a_ as sanitizeFormConfigPure, a$ as textFieldMapper, b0 as toReadonlyFieldTree, b1 as validateFieldAddons, b3 as walkAndValidateAddons, b4 as withCustomAddon, b5 as withEventFormValue, b6 as withLegacyStatusClasses, b7 as withLoggerConfig, b8 as withPreviousValue, b9 as withValidationExecutionDefaults, ba as withValueExclusionDefaults, bb as wrapperProps, bc as writeToFieldValue } from './ng-forge-dynamic-forms-ng-forge-dynamic-forms-ww_BnAt_.mjs';
1
+ export { A as ADDON_ACTION_HANDLERS, a as ADDON_ACTION_REGISTRY, b as ADDON_KIND_COMPONENT_CACHE, c as ADDON_KIND_DEFINITIONS, d as AppendArrayItemEvent, B as BUILT_IN_FIELDS, f as ComponentAddonComponent, g as ConsoleLogger, D as DfAddonSlot, i as DfTemplate, j as DynamicForm, E as EventDispatcher, F as FormClearEvent, k as FormResetEvent, I as InsertArrayItemEvent, M as MoveArrayItemEvent, N as NextPageEvent, P as PageChangeEvent, l as PopArrayItemEvent, m as PrependArrayItemEvent, n as PreviousPageEvent, R as RemoveAtIndexEvent, S as ShiftArrayItemEvent, o as SubmitEvent, T as TemplateAddonComponent, p as TextAddonComponent, q as arrayEvent, r as createField, x as createWrappers, y as field, z as formConfig, G as formatAddonWarning, K as injectAddonKindRegistry, O as injectFieldsSupportingAddons, U as isWrappersBundle, V as logAddonWarnings, X as provideAddonActions, Y as provideDynamicForm, _ as runPresetAction, $ as sanitizeFormConfig, a0 as sanitizeFormConfigPure, a1 as validateFieldAddons, a2 as walkAndValidateAddons, a3 as withCustomAddon, a4 as withEventFormValue, a5 as withLegacyStatusClasses, a6 as withLoggerConfig, a7 as withValidationExecutionDefaults, a8 as withValueExclusionDefaults, a9 as wrapperProps } from './ng-forge-dynamic-forms-ng-forge-dynamic-forms-CNc4a_Xv.mjs';
2
+ export { ADDON_KIND_REGISTRY, DF_FIELD_TEMPLATES, DynamicFormError, DynamicFormLogger, NoopLogger, WRAPPER_REGISTRY, evaluateNonFieldDisabled, evaluateNonFieldHidden, hasFormValue, isArrayField, isCheckedField, isContainerField, isContainerTypedField, isDisplayOnlyField, isFieldBoundContext, isFormStateCondition, isGroupField, isLeafField, isPageField, isRowField, isSimplifiedArrayField, isValueBearingField, isValueField, isWrapperTypeDefinition, resolveDynamicValue, resolveNextButtonDisabled, resolveNonFieldDisabled, resolveNonFieldHidden, resolveSubmitButtonDisabled } from '@ng-forge/dynamic-forms/internal';
2
3
  //# sourceMappingURL=ng-forge-dynamic-forms.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"ng-forge-dynamic-forms.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":""}
1
+ {"version":3,"file":"ng-forge-dynamic-forms.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ng-forge/dynamic-forms",
3
- "version": "0.10.0-next.2",
3
+ "version": "0.10.0-next.4",
4
4
  "description": "Type-safe, signal-powered dynamic forms for Angular. Build complex forms from configuration with full TypeScript inference.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -30,6 +30,8 @@
30
30
  "config-driven"
31
31
  ],
32
32
  "sideEffects": false,
33
+ "schematics": "./schematics/collection.json",
34
+ "generators": "./schematics/generators.json",
33
35
  "engines": {
34
36
  "node": ">=20"
35
37
  },
@@ -58,9 +60,20 @@
58
60
  "types": "./types/ng-forge-dynamic-forms-integration.d.ts",
59
61
  "default": "./fesm2022/ng-forge-dynamic-forms-integration.mjs"
60
62
  },
63
+ "./internal": {
64
+ "types": "./types/ng-forge-dynamic-forms-internal.d.ts",
65
+ "default": "./fesm2022/ng-forge-dynamic-forms-internal.mjs"
66
+ },
61
67
  "./schema": {
62
68
  "types": "./types/ng-forge-dynamic-forms-schema.d.ts",
63
69
  "default": "./fesm2022/ng-forge-dynamic-forms-schema.mjs"
64
70
  }
71
+ },
72
+ "ngForgeSchematicDeps": {
73
+ "bootstrap": "^5.3.8",
74
+ "primeng": "^21.1.8",
75
+ "@primeuix/themes": "^2.0.3",
76
+ "primeicons": "^7.0.0",
77
+ "@ionic/angular": "^8.8.8"
65
78
  }
66
79
  }
@@ -0,0 +1,10 @@
1
+ {
2
+ "$schema": "../../../node_modules/@angular-devkit/schematics/collection-schema.json",
3
+ "schematics": {
4
+ "ng-add": {
5
+ "description": "Set up @ng-forge/dynamic-forms with a UI adapter (Material, Bootstrap, PrimeNG, Ionic, or none).",
6
+ "factory": "./ng-add/index#default",
7
+ "schema": "./ng-add/schema.json"
8
+ }
9
+ }
10
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "$schema": "../../../node_modules/nx/schemas/generators-schema.json",
3
+ "name": "@ng-forge/dynamic-forms",
4
+ "version": "0.0.1",
5
+ "generators": {
6
+ "add": {
7
+ "factory": "./ng-add/nx-wrapper#default",
8
+ "schema": "./ng-add/schema.json",
9
+ "description": "Set up @ng-forge/dynamic-forms with a UI adapter (Material, Bootstrap, PrimeNG, Ionic, or none)."
10
+ }
11
+ }
12
+ }
@@ -0,0 +1,87 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = ngAdd;
4
+ const schematics_1 = require("@angular-devkit/schematics");
5
+ const workspace_1 = require("@schematics/angular/utility/workspace");
6
+ const recipes_1 = require("./recipes");
7
+ const add_packages_1 = require("./rules/add-packages");
8
+ const add_styles_1 = require("./rules/add-styles");
9
+ const add_providers_1 = require("./rules/add-providers");
10
+ function ngAdd(options) {
11
+ return async (tree, ctx) => {
12
+ if (!options.adapter) {
13
+ throw new schematics_1.SchematicsException('No adapter selected. Pass --adapter=<material|bootstrap|primeng|ionic|none>.');
14
+ }
15
+ if (!(options.adapter in recipes_1.RECIPES)) {
16
+ throw new schematics_1.SchematicsException(`Unknown adapter "${options.adapter}".`);
17
+ }
18
+ const recipe = recipes_1.RECIPES[options.adapter];
19
+ const projectName = await resolveProjectName(tree, options.project);
20
+ const legacyStatusClasses = options.legacyStatusClasses !== false;
21
+ // Shared across rules: addPackages records how many deps it queued so the
22
+ // final summary can show the install hint only when an install was actually
23
+ // scheduled (e.g. not on an idempotent re-run).
24
+ const installState = { added: 0 };
25
+ ctx.logger.info(`Setting up @ng-forge/dynamic-forms with ${recipe.label}${projectName ? ` (project: ${projectName})` : ''}.`);
26
+ return (0, schematics_1.chain)([
27
+ (0, add_packages_1.addPackages)(recipe, !options.skipProviders, installState),
28
+ options.skipStyles ? noopRule() : (0, add_styles_1.addStyles)(recipe, projectName),
29
+ options.skipProviders ? noopRule() : (0, add_providers_1.addProviders)(recipe, legacyStatusClasses, projectName),
30
+ summarize(recipe, options, projectName, installState),
31
+ ]);
32
+ };
33
+ }
34
+ function noopRule() {
35
+ return (tree) => tree;
36
+ }
37
+ async function resolveProjectName(tree, requested) {
38
+ if (requested) {
39
+ return requested;
40
+ }
41
+ let workspace;
42
+ try {
43
+ workspace = await (0, workspace_1.getWorkspace)(tree);
44
+ }
45
+ catch {
46
+ return undefined;
47
+ }
48
+ const explicitDefault = workspace.extensions['defaultProject'];
49
+ if (typeof explicitDefault === 'string' && workspace.projects.has(explicitDefault)) {
50
+ return explicitDefault;
51
+ }
52
+ const apps = [];
53
+ for (const [name, project] of workspace.projects) {
54
+ if (project.extensions['projectType'] === 'application') {
55
+ apps.push(name);
56
+ }
57
+ }
58
+ if (apps.length === 0) {
59
+ return undefined;
60
+ }
61
+ if (apps.length === 1) {
62
+ return apps[0];
63
+ }
64
+ // Multiple apps and no --project: silently picking one would wire
65
+ // ng-forge into a random project. Fail loudly with the list so the user
66
+ // can re-run with --project=<name>.
67
+ throw new schematics_1.SchematicsException(`Multiple application projects found (${apps.join(', ')}). Pass --project=<name> to choose which one to set up.`);
68
+ }
69
+ function summarize(recipe, options, projectName, installState) {
70
+ return (_tree, ctx) => {
71
+ const providersStatus = options.skipProviders ? 'skipped' : projectName ? 'wired' : 'skipped (no application project found)';
72
+ const lines = [];
73
+ lines.push('@ng-forge/dynamic-forms setup complete.');
74
+ lines.push(` adapter: ${recipe.label}`);
75
+ if (projectName) {
76
+ lines.push(` project: ${projectName}`);
77
+ }
78
+ lines.push(` styles: ${options.skipStyles ? 'skipped' : recipe.styles.length === 0 ? 'n/a' : 'imported'}`);
79
+ lines.push(` providers: ${providersStatus}`);
80
+ if (installState.added > 0) {
81
+ lines.push(" Run `npm install` if it didn't run automatically.");
82
+ }
83
+ lines.push(' Docs: https://ng-forge.com/dynamic-forms/getting-started');
84
+ ctx.logger.info(lines.join('\n'));
85
+ };
86
+ }
87
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/dynamic-forms/schematics/ng-add/index.ts"],"names":[],"mappings":";;AASA,wBA2BC;AApCD,2DAAsG;AACtG,qEAAqE;AAGrE,uCAAmD;AACnD,uDAAiE;AACjE,mDAA+C;AAC/C,yDAAqD;AAErD,SAAwB,KAAK,CAAC,OAAqB;IACjD,OAAO,KAAK,EAAE,IAAU,EAAE,GAAqB,EAAE,EAAE;QACjD,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;YACrB,MAAM,IAAI,gCAAmB,CAAC,8EAA8E,CAAC,CAAC;QAChH,CAAC;QACD,IAAI,CAAC,CAAC,OAAO,CAAC,OAAO,IAAI,iBAAO,CAAC,EAAE,CAAC;YAClC,MAAM,IAAI,gCAAmB,CAAC,oBAAoB,OAAO,CAAC,OAAO,IAAI,CAAC,CAAC;QACzE,CAAC;QAED,MAAM,MAAM,GAAG,iBAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QACxC,MAAM,WAAW,GAAG,MAAM,kBAAkB,CAAC,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;QACpE,MAAM,mBAAmB,GAAG,OAAO,CAAC,mBAAmB,KAAK,KAAK,CAAC;QAElE,0EAA0E;QAC1E,4EAA4E;QAC5E,gDAAgD;QAChD,MAAM,YAAY,GAAiB,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;QAEhD,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,2CAA2C,MAAM,CAAC,KAAK,GAAG,WAAW,CAAC,CAAC,CAAC,cAAc,WAAW,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QAE9H,OAAO,IAAA,kBAAK,EAAC;YACX,IAAA,0BAAW,EAAC,MAAM,EAAE,CAAC,OAAO,CAAC,aAAa,EAAE,YAAY,CAAC;YACzD,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,IAAA,sBAAS,EAAC,MAAM,EAAE,WAAW,CAAC;YAChE,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,IAAA,4BAAY,EAAC,MAAM,EAAE,mBAAmB,EAAE,WAAW,CAAC;YAC3F,SAAS,CAAC,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,YAAY,CAAC;SACtD,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC;AAED,SAAS,QAAQ;IACf,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC;AACxB,CAAC;AAED,KAAK,UAAU,kBAAkB,CAAC,IAAU,EAAE,SAA6B;IACzE,IAAI,SAAS,EAAE,CAAC;QACd,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,SAAmD,CAAC;IACxD,IAAI,CAAC;QACH,SAAS,GAAG,MAAM,IAAA,wBAAY,EAAC,IAAI,CAAC,CAAC;IACvC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,eAAe,GAAG,SAAS,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAC;IAC/D,IAAI,OAAO,eAAe,KAAK,QAAQ,IAAI,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,eAAe,CAAC,EAAE,CAAC;QACnF,OAAO,eAAe,CAAC;IACzB,CAAC;IAED,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,KAAK,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,SAAS,CAAC,QAAQ,EAAE,CAAC;QACjD,IAAI,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC,KAAK,aAAa,EAAE,CAAC;YACxD,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;IAED,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtB,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtB,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC;IACjB,CAAC;IACD,kEAAkE;IAClE,wEAAwE;IACxE,oCAAoC;IACpC,MAAM,IAAI,gCAAmB,CAC3B,wCAAwC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,yDAAyD,CACjH,CAAC;AACJ,CAAC;AAED,SAAS,SAAS,CAAC,MAAqB,EAAE,OAAqB,EAAE,WAA+B,EAAE,YAA0B;IAC1H,OAAO,CAAC,KAAW,EAAE,GAAqB,EAAE,EAAE;QAC5C,MAAM,eAAe,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,wCAAwC,CAAC;QAC7H,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,KAAK,CAAC,IAAI,CAAC,yCAAyC,CAAC,CAAC;QACtD,KAAK,CAAC,IAAI,CAAC,cAAc,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;QACzC,IAAI,WAAW,EAAE,CAAC;YAChB,KAAK,CAAC,IAAI,CAAC,cAAc,WAAW,EAAE,CAAC,CAAC;QAC1C,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,gBAAgB,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC;QAC/G,KAAK,CAAC,IAAI,CAAC,gBAAgB,eAAe,EAAE,CAAC,CAAC;QAC9C,IAAI,YAAY,CAAC,KAAK,GAAG,CAAC,EAAE,CAAC;YAC3B,KAAK,CAAC,IAAI,CAAC,qDAAqD,CAAC,CAAC;QACpE,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,4DAA4D,CAAC,CAAC;QACzE,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACpC,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const ngcli_adapter_1 = require("@nx/devkit/ngcli-adapter");
4
+ exports.default = (0, ngcli_adapter_1.wrapAngularDevkitSchematic)('@ng-forge/dynamic-forms', 'ng-add');
5
+ //# sourceMappingURL=nx-wrapper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nx-wrapper.js","sourceRoot":"","sources":["../../../../../packages/dynamic-forms/schematics/ng-add/nx-wrapper.ts"],"names":[],"mappings":";;AAAA,4DAAsE;AAEtE,kBAAe,IAAA,0CAA0B,EAAC,yBAAyB,EAAE,QAAQ,CAAC,CAAC"}
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RECIPES = void 0;
4
+ const versions_1 = require("../versions");
5
+ const v = (name) => ({ name, version: versions_1.VERSIONS[name] });
6
+ exports.RECIPES = {
7
+ material: {
8
+ label: 'Angular Material',
9
+ packages: [v('@ng-forge/dynamic-forms-material'), v('@angular/material'), v('@angular/cdk')],
10
+ styles: ['@angular/material/prebuilt-themes/azure-blue.css'],
11
+ adapterProviders: [],
12
+ withFields: { from: '@ng-forge/dynamic-forms-material', symbol: 'withMaterialFields' },
13
+ },
14
+ bootstrap: {
15
+ label: 'Bootstrap',
16
+ packages: [v('@ng-forge/dynamic-forms-bootstrap'), v('bootstrap')],
17
+ styles: ['bootstrap/dist/css/bootstrap.min.css'],
18
+ adapterProviders: [],
19
+ withFields: { from: '@ng-forge/dynamic-forms-bootstrap', symbol: 'withBootstrapFields' },
20
+ },
21
+ primeng: {
22
+ label: 'PrimeNG',
23
+ packages: [v('@ng-forge/dynamic-forms-primeng'), v('primeng'), v('@primeuix/themes'), v('primeicons')],
24
+ styles: ['primeicons/primeicons.css'],
25
+ adapterProviders: [
26
+ {
27
+ marker: 'providePrimeNG',
28
+ defaultImports: [{ symbol: 'Aura', from: '@primeuix/themes/aura' }],
29
+ builder: ({ code, external }) => code `${external('providePrimeNG', 'primeng/config')}({ theme: { preset: Aura } })`,
30
+ },
31
+ ],
32
+ withFields: { from: '@ng-forge/dynamic-forms-primeng', symbol: 'withPrimeNGFields' },
33
+ },
34
+ ionic: {
35
+ label: 'Ionic',
36
+ packages: [v('@ng-forge/dynamic-forms-ionic'), v('@ionic/angular')],
37
+ styles: [
38
+ '@ionic/angular/css/core.css',
39
+ '@ionic/angular/css/normalize.css',
40
+ '@ionic/angular/css/structure.css',
41
+ '@ionic/angular/css/typography.css',
42
+ ],
43
+ adapterProviders: [
44
+ {
45
+ marker: 'provideIonicAngular',
46
+ defaultImports: [],
47
+ builder: ({ code, external }) => code `${external('provideIonicAngular', '@ionic/angular/standalone')}({ mode: 'md' })`,
48
+ },
49
+ ],
50
+ withFields: { from: '@ng-forge/dynamic-forms-ionic', symbol: 'withIonicFields' },
51
+ },
52
+ none: {
53
+ label: 'Core only',
54
+ packages: [],
55
+ styles: [],
56
+ adapterProviders: [],
57
+ withFields: null,
58
+ },
59
+ };
60
+ //# sourceMappingURL=recipes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recipes.js","sourceRoot":"","sources":["../../../../../packages/dynamic-forms/schematics/ng-add/recipes.ts"],"names":[],"mappings":";;;AAGA,0CAAqD;AA0BrD,MAAM,CAAC,GAAG,CAAC,IAAkB,EAAe,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,mBAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAEtE,QAAA,OAAO,GAAmC;IACrD,QAAQ,EAAE;QACR,KAAK,EAAE,kBAAkB;QACzB,QAAQ,EAAE,CAAC,CAAC,CAAC,kCAAkC,CAAC,EAAE,CAAC,CAAC,mBAAmB,CAAC,EAAE,CAAC,CAAC,cAAc,CAAC,CAAC;QAC5F,MAAM,EAAE,CAAC,kDAAkD,CAAC;QAC5D,gBAAgB,EAAE,EAAE;QACpB,UAAU,EAAE,EAAE,IAAI,EAAE,kCAAkC,EAAE,MAAM,EAAE,oBAAoB,EAAE;KACvF;IACD,SAAS,EAAE;QACT,KAAK,EAAE,WAAW;QAClB,QAAQ,EAAE,CAAC,CAAC,CAAC,mCAAmC,CAAC,EAAE,CAAC,CAAC,WAAW,CAAC,CAAC;QAClE,MAAM,EAAE,CAAC,sCAAsC,CAAC;QAChD,gBAAgB,EAAE,EAAE;QACpB,UAAU,EAAE,EAAE,IAAI,EAAE,mCAAmC,EAAE,MAAM,EAAE,qBAAqB,EAAE;KACzF;IACD,OAAO,EAAE;QACP,KAAK,EAAE,SAAS;QAChB,QAAQ,EAAE,CAAC,CAAC,CAAC,iCAAiC,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,kBAAkB,CAAC,EAAE,CAAC,CAAC,YAAY,CAAC,CAAC;QACtG,MAAM,EAAE,CAAC,2BAA2B,CAAC;QACrC,gBAAgB,EAAE;YAChB;gBACE,MAAM,EAAE,gBAAgB;gBACxB,cAAc,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,uBAAuB,EAAE,CAAC;gBACnE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,IAAI,CAAA,GAAG,QAAQ,CAAC,gBAAgB,EAAE,gBAAgB,CAAC,+BAA+B;aACpH;SACF;QACD,UAAU,EAAE,EAAE,IAAI,EAAE,iCAAiC,EAAE,MAAM,EAAE,mBAAmB,EAAE;KACrF;IACD,KAAK,EAAE;QACL,KAAK,EAAE,OAAO;QACd,QAAQ,EAAE,CAAC,CAAC,CAAC,+BAA+B,CAAC,EAAE,CAAC,CAAC,gBAAgB,CAAC,CAAC;QACnE,MAAM,EAAE;YACN,6BAA6B;YAC7B,kCAAkC;YAClC,kCAAkC;YAClC,mCAAmC;SACpC;QACD,gBAAgB,EAAE;YAChB;gBACE,MAAM,EAAE,qBAAqB;gBAC7B,cAAc,EAAE,EAAE;gBAClB,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,IAAI,CAAA,GAAG,QAAQ,CAAC,qBAAqB,EAAE,2BAA2B,CAAC,kBAAkB;aACvH;SACF;QACD,UAAU,EAAE,EAAE,IAAI,EAAE,+BAA+B,EAAE,MAAM,EAAE,iBAAiB,EAAE;KACjF;IACD,IAAI,EAAE;QACJ,KAAK,EAAE,WAAW;QAClB,QAAQ,EAAE,EAAE;QACZ,MAAM,EAAE,EAAE;QACV,gBAAgB,EAAE,EAAE;QACpB,UAAU,EAAE,IAAI;KACjB;CACF,CAAC"}
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.addPackages = addPackages;
4
+ const tasks_1 = require("@angular-devkit/schematics/tasks");
5
+ const dependencies_1 = require("@schematics/angular/utility/dependencies");
6
+ const versions_1 = require("../../versions");
7
+ const CORE_PACKAGE = {
8
+ name: '@ng-forge/dynamic-forms',
9
+ version: versions_1.VERSIONS['@ng-forge/dynamic-forms'],
10
+ };
11
+ // provideAnimations() (wired unless providers are skipped) imports from
12
+ // @angular/platform-browser/animations, which needs @angular/animations —
13
+ // not present in a fresh Angular app.
14
+ // This is installed even for the `none` (core-only) adapter, because the
15
+ // schematic still wires provideAnimations() unconditionally on the providers
16
+ // path. Pass --skipProviders to opt out of both the wiring and the install.
17
+ const ANIMATIONS_PACKAGE = {
18
+ name: '@angular/animations',
19
+ version: versions_1.VERSIONS['@angular/animations'],
20
+ };
21
+ function addPackages(recipe, includeAnimations, state) {
22
+ return (tree, ctx) => {
23
+ const all = [CORE_PACKAGE, ...(includeAnimations ? [ANIMATIONS_PACKAGE] : []), ...recipe.packages];
24
+ // @angular/animations ships from the core monorepo and pins @angular/core
25
+ // to its EXACT patch version. So it must match the installed core exactly,
26
+ // otherwise npm resolves the range to a newer patch and the peer check
27
+ // fails (e.g. animations@21.2.15 vs core@21.2.14). Material/CDK are a
28
+ // separate train (peer `^21.0.0`) and keep their declared range.
29
+ const angularVersion = getInstalledAngularVersion(tree);
30
+ let added = 0;
31
+ for (const pkg of all) {
32
+ const existing = (0, dependencies_1.getPackageJsonDependency)(tree, pkg.name);
33
+ if (existing) {
34
+ ctx.logger.info(`${pkg.name} already declared (${existing.version}); leaving as-is.`);
35
+ continue;
36
+ }
37
+ const version = pkg.name === '@angular/animations' && angularVersion ? angularVersion : pkg.version;
38
+ (0, dependencies_1.addPackageJsonDependency)(tree, {
39
+ type: dependencies_1.NodeDependencyType.Default,
40
+ name: pkg.name,
41
+ version,
42
+ overwrite: false,
43
+ });
44
+ added++;
45
+ }
46
+ if (added > 0) {
47
+ ctx.addTask(new tasks_1.NodePackageInstallTask());
48
+ }
49
+ state.added = added;
50
+ return tree;
51
+ };
52
+ }
53
+ /**
54
+ * Exact installed @angular/core version (e.g. "21.2.14"), so @angular/*
55
+ * packages we add lockstep with it. Prefers the resolved version in
56
+ * node_modules; falls back to the spec in package.json; null if neither.
57
+ */
58
+ function getInstalledAngularVersion(tree) {
59
+ const corePkgPath = '/node_modules/@angular/core/package.json';
60
+ if (tree.exists(corePkgPath)) {
61
+ try {
62
+ const json = JSON.parse(tree.read(corePkgPath).toString('utf-8'));
63
+ if (typeof json.version === 'string' && json.version.length > 0) {
64
+ return json.version;
65
+ }
66
+ }
67
+ catch {
68
+ // fall through to package.json spec
69
+ }
70
+ }
71
+ return (0, dependencies_1.getPackageJsonDependency)(tree, '@angular/core')?.version ?? null;
72
+ }
73
+ //# sourceMappingURL=add-packages.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"add-packages.js","sourceRoot":"","sources":["../../../../../../packages/dynamic-forms/schematics/ng-add/rules/add-packages.ts"],"names":[],"mappings":";;AAgCA,kCAkCC;AAjED,4DAA0E;AAC1E,2EAAkI;AAGlI,6CAA0C;AAW1C,MAAM,YAAY,GAAgB;IAChC,IAAI,EAAE,yBAAyB;IAC/B,OAAO,EAAE,mBAAQ,CAAC,yBAAyB,CAAC;CAC7C,CAAC;AAEF,wEAAwE;AACxE,0EAA0E;AAC1E,sCAAsC;AACtC,yEAAyE;AACzE,6EAA6E;AAC7E,4EAA4E;AAC5E,MAAM,kBAAkB,GAAgB;IACtC,IAAI,EAAE,qBAAqB;IAC3B,OAAO,EAAE,mBAAQ,CAAC,qBAAqB,CAAC;CACzC,CAAC;AAEF,SAAgB,WAAW,CAAC,MAAqB,EAAE,iBAA0B,EAAE,KAAmB;IAChG,OAAO,CAAC,IAAU,EAAE,GAAqB,EAAE,EAAE;QAC3C,MAAM,GAAG,GAAG,CAAC,YAAY,EAAE,GAAG,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;QACnG,0EAA0E;QAC1E,2EAA2E;QAC3E,uEAAuE;QACvE,sEAAsE;QACtE,iEAAiE;QACjE,MAAM,cAAc,GAAG,0BAA0B,CAAC,IAAI,CAAC,CAAC;QACxD,IAAI,KAAK,GAAG,CAAC,CAAC;QAEd,KAAK,MAAM,GAAG,IAAI,GAAG,EAAE,CAAC;YACtB,MAAM,QAAQ,GAAG,IAAA,uCAAwB,EAAC,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;YAC1D,IAAI,QAAQ,EAAE,CAAC;gBACb,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,IAAI,sBAAsB,QAAQ,CAAC,OAAO,mBAAmB,CAAC,CAAC;gBACtF,SAAS;YACX,CAAC;YACD,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,KAAK,qBAAqB,IAAI,cAAc,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC;YACpG,IAAA,uCAAwB,EAAC,IAAI,EAAE;gBAC7B,IAAI,EAAE,iCAAkB,CAAC,OAAO;gBAChC,IAAI,EAAE,GAAG,CAAC,IAAI;gBACd,OAAO;gBACP,SAAS,EAAE,KAAK;aACjB,CAAC,CAAC;YACH,KAAK,EAAE,CAAC;QACV,CAAC;QAED,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;YACd,GAAG,CAAC,OAAO,CAAC,IAAI,8BAAsB,EAAE,CAAC,CAAC;QAC5C,CAAC;QACD,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC;QAEpB,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,SAAS,0BAA0B,CAAC,IAAU;IAC5C,MAAM,WAAW,GAAG,0CAA0C,CAAC;IAC/D,IAAI,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC;QAC7B,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,CAA0B,CAAC;YAC5F,IAAI,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAChE,OAAO,IAAI,CAAC,OAAO,CAAC;YACtB,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,oCAAoC;QACtC,CAAC;IACH,CAAC;IACD,OAAO,IAAA,uCAAwB,EAAC,IAAI,EAAE,eAAe,CAAC,EAAE,OAAO,IAAI,IAAI,CAAC;AAC1E,CAAC"}
@@ -0,0 +1,132 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.addProviders = addProviders;
4
+ const schematics_1 = require("@angular-devkit/schematics");
5
+ const ast_utils_1 = require("@schematics/angular/utility/ast-utils");
6
+ const rules_1 = require("@schematics/angular/utility/standalone/rules");
7
+ const app_config_1 = require("@schematics/angular/utility/standalone/app_config");
8
+ const util_1 = require("@schematics/angular/utility/standalone/util");
9
+ function addProviders(recipe, legacyStatusClasses, projectName) {
10
+ return (tree, ctx) => {
11
+ // No target project (workspace has no application, or the workspace file
12
+ // couldn't be read). Don't guess a name — addRootProvider would throw and
13
+ // abort the whole ng-add. Warn and skip, mirroring addStyles' behaviour.
14
+ if (!projectName) {
15
+ ctx.logger.warn('No application project found; skipping provider wiring. ' +
16
+ 'Add provideDynamicForm(...) to your ApplicationConfig manually — see ' +
17
+ 'https://ng-forge.com/dynamic-forms/getting-started');
18
+ return tree;
19
+ }
20
+ const project = projectName;
21
+ return (0, schematics_1.chain)([
22
+ maybeAddRootProvider(project, 'provideAnimations', ({ code, external }) => code `${external('provideAnimations', '@angular/platform-browser/animations')}()`),
23
+ ...recipe.adapterProviders.flatMap((spec) => adapterProviderRules(project, spec)),
24
+ maybeAddRootProvider(project, 'provideDynamicForm', ({ code, external }) => {
25
+ const provide = external('provideDynamicForm', '@ng-forge/dynamic-forms');
26
+ if (recipe.withFields === null) {
27
+ return code `${provide}()`;
28
+ }
29
+ const withFields = external(recipe.withFields.symbol, recipe.withFields.from);
30
+ if (legacyStatusClasses) {
31
+ const withLegacy = external('withLegacyStatusClasses', '@ng-forge/dynamic-forms');
32
+ return code `${provide}(...${withFields}(), ${withLegacy}())`;
33
+ }
34
+ return code `${provide}(...${withFields}())`;
35
+ }),
36
+ ]);
37
+ };
38
+ }
39
+ function adapterProviderRules(project, spec) {
40
+ return [
41
+ ...spec.defaultImports.map((imp) => addDefaultImportRule(imp.symbol, imp.from, project)),
42
+ maybeAddRootProvider(project, spec.marker, spec.builder),
43
+ ];
44
+ }
45
+ function addDefaultImportRule(symbol, module, project) {
46
+ return async (tree, ctx) => {
47
+ let mainFilePath;
48
+ try {
49
+ mainFilePath = await (0, util_1.getMainFilePath)(tree, project);
50
+ }
51
+ catch {
52
+ ctx.logger.warn(`Could not locate main.ts; skipping default import for ${symbol}.`);
53
+ return tree;
54
+ }
55
+ let filePath = mainFilePath;
56
+ try {
57
+ const bootstrapCall = (0, util_1.findBootstrapApplicationCall)(tree, mainFilePath);
58
+ const appConfig = (0, app_config_1.findAppConfig)(bootstrapCall, tree, mainFilePath);
59
+ if (appConfig) {
60
+ filePath = appConfig.filePath;
61
+ }
62
+ }
63
+ catch {
64
+ // fall back to main.ts
65
+ }
66
+ // insertImport throws (rather than no-op'ing) for an already-present
67
+ // DEFAULT import, so guard idempotency ourselves before calling it.
68
+ // Strip comments first so a commented-out import doesn't suppress the
69
+ // real insertion.
70
+ const existing = stripJsComments(tree.read(filePath)?.toString('utf-8') ?? '');
71
+ const moduleSpecifier = new RegExp(`from\\s+['"]${escapeRegExp(module)}['"]`);
72
+ if (moduleSpecifier.test(existing)) {
73
+ ctx.logger.info(`import ${symbol} from '${module}' already present; skipping.`);
74
+ return tree;
75
+ }
76
+ const source = (0, util_1.getSourceFile)(tree, filePath);
77
+ const change = (0, ast_utils_1.insertImport)(source, filePath, symbol, module, true);
78
+ (0, util_1.applyChangesToFile)(tree, filePath, [change]);
79
+ return tree;
80
+ };
81
+ }
82
+ function escapeRegExp(value) {
83
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
84
+ }
85
+ /**
86
+ * Strips line and block comments from JS/TS source for token-presence
87
+ * checks. Naive: doesn't account for `//` or `/*` characters that appear
88
+ * inside string/template literals — those would be unusual and aren't a
89
+ * realistic concern for the call-expression markers we're scanning for.
90
+ */
91
+ function stripJsComments(source) {
92
+ return source.replace(/\/\*[\s\S]*?\*\//g, '').replace(/\/\/[^\n]*/g, '');
93
+ }
94
+ function maybeAddRootProvider(project, marker, callback) {
95
+ return async (tree, ctx) => {
96
+ const present = await isProviderPresent(tree, project, marker);
97
+ if (present) {
98
+ ctx.logger.info(`${marker}(...) already present in providers; skipping.`);
99
+ return (0, schematics_1.noop)();
100
+ }
101
+ return (0, rules_1.addRootProvider)(project, callback);
102
+ };
103
+ }
104
+ async function isProviderPresent(tree, project, marker) {
105
+ let mainFilePath;
106
+ try {
107
+ mainFilePath = await (0, util_1.getMainFilePath)(tree, project);
108
+ }
109
+ catch {
110
+ return false;
111
+ }
112
+ let filePath = mainFilePath;
113
+ try {
114
+ const bootstrapCall = (0, util_1.findBootstrapApplicationCall)(tree, mainFilePath);
115
+ const appConfig = (0, app_config_1.findAppConfig)(bootstrapCall, tree, mainFilePath);
116
+ if (appConfig) {
117
+ filePath = appConfig.filePath;
118
+ }
119
+ }
120
+ catch {
121
+ return false;
122
+ }
123
+ const buffer = tree.read(filePath);
124
+ if (!buffer) {
125
+ return false;
126
+ }
127
+ // Strip comments so a commented-out reference (e.g. `// provideDynamicForm()`)
128
+ // doesn't cause the schematic to skip real wiring.
129
+ const source = stripJsComments(buffer.toString('utf-8'));
130
+ return new RegExp(`\\b${marker}\\s*\\(`).test(source);
131
+ }
132
+ //# sourceMappingURL=add-providers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"add-providers.js","sourceRoot":"","sources":["../../../../../../packages/dynamic-forms/schematics/ng-add/rules/add-providers.ts"],"names":[],"mappings":"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"}