/** * @class Cmd.auto.Dependency * * The Cmd Auto Dependency system is an api that allows Cmd to automatically detect class * dependencies based purely on usages in code without needing to reference a needed class * in either the requires or uses arrays. * * It works by combining the values of class configs with metadata from associated auto * dependency directives to allow Cmd to interpret the value of a config as a constructor * parameter to another class. * * As an example, consider the following auto dependency directive for the `items` config * of the Container class from the classic toolkit: * * Ext.define('Ext.container.Container', { * ... * // @cmd-auto-dependency {aliasPrefix: "widget.", typeProperty: "xtype", defaultTypeProperty: "defaultType", defaultsProperty: "defaults"} * items: undefined, * ... * }); * * Cmd requires the directive to be contained in a single line, but for the purposes of * discussing the structure of the directive, here is the same directive formatted for * readability: * * Ext.define('Ext.container.Container', { * ... * // @cmd-auto-dependency { * // aliasPrefix: "widget.", * // typeProperty: "xtype", * // defaultTypeProperty: "defaultType", * // defaultsProperty: "defaults" * // } * items: undefined, * ... * }); * * In this example, the presence of the cmd-auto-dependency directive above the items * config is enough to indicate to Cmd that the value of an items config, when supplied, * should be processed as a constructor parameter to one or more class instances. * * To do this, Cmd must first determine the class being constructed. It does this by first * looking at the value of the config. If the value of the config is a string, like: * * new Ext.container.Container({ * items: "button" * }); * * the value of the string is first checked to see if it is already a resolvable reference * to a class definition. If not, the data from the directive is used to produce other * match candidates. In this case, the directive indicates that the `aliasPrefix` to use * when resolving class names here is "widget.", so the name, "button", is combined with * the `aliasPrefix` to produce the candidate name "widget.button", which will resolve to * Ext.button.Button. * * If, however, the value of the config is an object literal, the the object literal will * need to be inspected to determine the string literal to use. * * new Ext.container.Container({ * items: { * xtype: "button" * } * }); * * To determine the class name for configs of this type, Cmd must inspect the object * literal to detect a specified class name. In this case, the directive specifies a * `typeProperty` of "xtype". This tells Cmd that if the object literal contains a property * by that name, then the value of that property should be used as the input string to * perform the above lookup. * * For container items, an array may also be supplied as the value of the config. In this * case, Cmd will iterate all of the items of the array, applying the same class name * lookup process: * * new Ext.container.Container({ * items: [{ * xtype: "button" * },{ * xtype: "panel" * }] * }); * * In some cases, the data needed to create the target class name may be provided via * another property on the class definition, and `defaultTypeProperty` allows this property * name to be specified. For the items config, the `defaultTypeProperty` value of * "defaultType" indicates that the "defaultType" property should be used if supplied: * * new Ext.container.Container({ * defaultType: "button", * items: [{ * text: "abc" * },{ * xtype: "panel" * }] * }); * * The auto dependency system can also handle the case where a class definition supplies * an entire default config to use when creating certain class instances. For container * items, this is configured with the `defaultsProperty` of "defaults": * * new Ext.container.Container({ * defaults: { * xtype: "button", * text: "abc" * }, * items: [{ * label: "xyz" * },{ * xtype: "panel" * }] * }); * * For other configs less dynamic than container items, or for cases where the default type * to looks up does not need to be dynamically modified, the `defaultType` dependency * property may be used to set the default type to use when no other type info is found * on the config value. * * In other cases, while the config value itself may need to be processed to determine * further dependencies, there may be other helper classes required by the config that * can not be found by inspecting the config value itself. In these cases, the `requires` * dependency config may be used to list other classes that should be required when a * config value is supplied * * * One subtlety of the auto dependency system is that the detection of requirements is * driven by the instantiation points, not the class definitions. Consider the following * example: * * Ext.define("A.container.Class", { * // @cmd-auto-dependency { aliasPrefix: "layout." } * // an optional layout config, "layout.hbox" will be the default * layout: "hbox" * }); * * Ext.create("A.container.Class", { * layout: "vbox" * }); * * In this example, the 'layout.hbox' class will never be required, even though it is * the default layout for the class, since there are no instances of the container class * that use the hbox layout, only vbox. * * @private */ /** * @cfg {String} defaultType * This config specifies the default type name to use when resolving a config value. * * Ext.define("A.button.Class", { * // @cmd-auto-dependency { defaultType: "Ext.menu.Menu" } * // an optional popup menu * menu: undefined * }); * * Ext.create("A.button.Class", { * // no xtype needed, as the defaultType will be applied * menu: { * items: [...] * } * }); * */ /** * @cfg {String} typeProperty * Specifies the property to inspect when looking for a type specifier. The default * value for this is 'type'. * * Ext.define("A.button.Class", { * // @cmd-auto-dependency { defaultType: "Ext.menu.Menu", typeProperty: 'mtype' } * // an optional popup menu * menu: undefined * }); * * Ext.create("A.button.Class", { * menu: { * // this overrides the default type with a custom value * mtype: 'custom-menu' * items: [...] * } * }); * */ /** * @cfg {String} defaultTypeProperty * Specifies a property / config on the class definition that will contain the default * value to use when resolving the class name. * * Ext.define("A.container.Class", { * // @cmd-auto-dependency { defaultTypeProperty: "defaultType" } * items: undefined * }); * * new A.container.Class({ * defaultType: "button", * items: [{ * // xtype not needed for this item, as "button" will be used * // as the default type * text: "abc" * },{ * xtype: "panel" * }] * }); */ /** * @cfg {String} aliasPrefix * This property specifies an alias prefix to prepend to the detected name value when * generating name matching candidates for the type lookup. * * Ext.define("A.container.Class", { * // @cmd-auto-dependency { aliasPrefix: "layout." } * // an optional layout config, "layout.hbox" will be the default * layout: "hbox" * }); * * Ext.create("A.container.Class", { * layout: "vbox" * }); */ /** * @cfg {String[]} requires * Specifies a list of other classes that need to be requires when a config is supplied, * but that are not detectable via the value of the config. * * Ext.define("An.application.Class", { * // @cmd-auto-dependency { aliasPrefix: "view.", requires: ["Ext.plugin.Viewport"]} * mainView: null * }); * * Ext.define("A.view.Class", { * alias: "view.main" * }); * * Ext.create("A.application.Class", { * mainView: "main" * }); */ /** * @cfg {String} directRef * Similar to requires, but specifies a single class to automatically be required * if a truthy value for this config is specified. * @private */ /** * @cfg {boolean} isKeyedObject * Indicates that when an object literal value is supplied, this value should be processed * as a key/value container where the values are the configs to inspect, similar to the * array form of config value. * * Ext.define('A.field.Class', { * // @cmd-auto-dependency { aliasPrefix: "trigger.", isKeyedObject: true} * triggers: null * }); * * Ext.create('A.field.Class', { * triggers: { * clear: 'clear', * search: { * type: 'search' * } * } * }); */ /** * @cfg {String} blame * Controls whether the instantiation point, the class definition, or both gain the * dependency when a value is supplied for this class config. Useful for controlling * file sort ordering. * * Supported values are `class`, `instance`, or `all`. The default value is `instance` * * Considering the following example: * * // A.js content * Ext.define('A.button.Class', { * // @cmd-auto-dependency { defaultType: "Ext.menu.Menu", blame: "instance" } * menu: undefined * }); * * // B.js content * Ext.create('A.button.Class', { * menu: {...} * }); * * A the default blame value of 'instance' indicates that B.js should gain the requirement * for Ext.menu.Menu. A blame value of 'class' would cause A.js to gain the requirement. * A blame value of 'all' would cause both A.js and B.js to gain the requirement. */