Repository navigation
Element libraries: a namespace's components folder holds its .apiext files, and optionally a published contract (parsley.taglib) #78
Description
An element library for each namespace: which elements it publishes, what each one is, and what templates call it. It's read by the runtime (to resolve and render tags), by tooling (completion, hover, validation, the element reference), and listed by ng itself, so documentation pages stop assembling their own element lists.
This is the implementation side of undur/apiext-format#6 ("a shortcut is not a property of the element… lands as implementation in ng-objects"), and grows out of #63.
A namespace's folder is its library
Following WO's convention that an element's API file lives with the components:
ng/<namespace>/
components/
NGString.apiext one per element: class, bindings, docs
TABadge.apiext a template-only component's too: no class
TABadge.html templates, as before
parsley.taglib optional: the library's published contract
tag-aliases.properties what templates call them: tag = element name
-
components/<Name>.apiextdeclares an element. Its root is the only place the element's class is stated, fully qualified (settling Root <wo class> uses the simple class name (Obj-C relic) — reconcile with FQN class references undur/apiext-format#11):<wo class="ng.appserver.templating.elements.NGString" …>
A template-only component's has no
class.scope(Element/component use scope (access control / visibility) undur/apiext-format#8) andreusable(Add a how-to for launching ng-testapp #7) would be attributes here too. Which runtime an element is for (ng, WO, or both through a common element API) isn't declared yet: nothing needs it until the first library serves both runtimes. -
components/parsley.taglibis optional: the library's published contract.- Without a taglib, a library publishes every element: each
.apiext, and its element classes. That's how every library works today, so nothing changes for them. - With a taglib, it publishes exactly what it lists; everything else is internal. Internal elements still render, but tooling doesn't offer them (completion, the element reference), and using another library's internal element gets a warning in the editor. A listed element needn't have an
.apiext(it's then undocumented).
<taglib> <element name="AjaxUpdateLink"/> <element name="AjaxUpdateContainer"/> </taglib>
This is the library-level answer to Element visibility: hide non-reusable and out-of-scope elements from completion and the Element Reference undur/parslips#9 and Component reusability (can it be used as a tag?) undur/apiext-format#7: a library declares its surface once instead of marking each element. It's how Java modules work: no
module-info, everything is exported. - Without a taglib, a library publishes every element: each
-
tag-aliases.propertiesholds tags only,tag = element name, resolved recursively. One line renames or redirects a tag without declaring anything.
The same layout carries over to WO: src/main/components/ already holds a framework's .apiext files (built into Resources/), and a parsley.taglib there would be built into Resources/parsley.taglib with no build change.
How ng loads them
Each plugin's namespace is loaded as the plugin is initialized, in plugin order: every .apiext in its components folder (in directories and jars, named after the file), each with its class and API, marked published or internal by the taglib if there is one, then the namespace's tags.
- A later namespace's tag overrides an earlier one's. Plugins are initialized after the plugins they
require()(on master, 77cf7cb); unrelated ones keep the classpath order. A plugin can rename the tags of one it builds on, and the application, loaded last, has the last word. Within one namespace, two files that disagree keep the first, with a warning. - This is still one global map, so an application's tag also applies inside a framework's own templates. That's what Tag-alias resolution is global at runtime — a framework's templates should resolve tags in their own context, not the application's #63 is about, and it stays open: element replacements should apply everywhere, tag shortcuts only within their library.
- A published class must exist and be an
NGElement, otherwise startup fails rather than the first render. When two namespaces publish the same element name, the first stands, with a warning. declaredElements()lists every element, each with its namespace, class, API and whether it's published;publishedElements()lists what documentation and tooling offer.tagsFor(name)gives an element's tags.- Tooling can't run
requires(), so the editor orders namespaces by the project's dependency graph: a library's tags override those of the libraries it depends on, and the application's override everyone's. A plugin that requires another compiles against it, so the two orders agree. A jar that depends on another without declaringrequires()is the gap. Two libraries with no dependency between them that define the same tag get a warning in the editor.
Finding undeclared elements: NGSearchClassPathForElements
To align with WO, an element nobody publishes is searched for on the classpath by simple name:
- an index of class names from the classpath; only the candidates matching the requested name are loaded, and they must be an
NGElement; - two matches are refused with both names, rather than guessed between;
- in development, each element found this way logs a warning once.
For now both the search and the warning are hardcoded on (a FIXME in NGApplication): the properties that should configure them aren't available when the element manager is constructed.
Done on the branch
- Readers for
.apiextandparsley.taglibin ng-template-parser (ng.appserver.templating.definitions:NGElementAPI,NGTaglib), dependency-free (on master, 09eb1f2). - ng-appserver's
.apiextfiles inng/ng/components/, naming their classes in full (on master, 701cf22 and faeebc9). - Loading per namespace in plugin order, with the registry listable.
- ng-appserver's elements in
ng/ng/, replacingNGCorePlugin.elements(); code registration of elements (Elements,NGPlugin.elements(), package probing) is gone. - The classpath search, with the dev-mode warning (on master, 693d0f1).
- ng-testapp's own library in
ng/app/(its templates moved tong/app/components; two documented components, one template-only, an alias for an element it doesn't declare, and a compatibility alias), tested in-process. - The optional taglib contract, tested with and without a taglib.
- The website's element reference generated from the registry.
- Plugins initialized in dependency order (
requires()), on master.
Before merging
- Document the layout in ng-objects' docs.
- Parslips reads ng's element libraries, reusing
NGElementAPIfrom ng-template-parser. The convention (ng/*/components/<Name>.apiext) is checked first; the class-based lookups stay behind it (beside the class, a WO jar'sResources/, next to the.api, the bundled copies), since released ng and wonder-slim ship their files that way. Until then, tags declared only by an app's or library's namespace show as unknown in the editor; the framework's own tags are fine. The same change removes the editor's bundled copies of ng's definitions (Remove the bundled copies of ng's element definitions (apiext/ng) undur/parslips#15).
Later
- A dev endpoint listing the registry (
/ng/dev/elements), with the element-definition work. - Scoped tag resolution (Tag-alias resolution is global at runtime — a framework's templates should resolve tags in their own context, not the application's #63).
- Parsley/wonder-slim adopting the layout or the format.
scopeandreusable(Component reusability (can it be used as a tag?) undur/apiext-format#7, Add a parent pom #8), cross-binding constraints inNGElementAPI, declaring an element's runtime when the first library serves both.
Activity
Tag libraries: elements and tags declared in ng/<namespace>/parsley.t…
Tag libraries: elements and tags declared in ng/<namespace>/parsley.t…
Tag libraries: element libraries in ng/<namespace>/, described by .ap…
Tag libraries: element libraries in ng/<namespace>/, described by .ap…
[-]Tag libraries: a parsley.taglib registry of elements and tags[/-][+]Element libraries: each namespace publishes elements/<Name>.apiext and tag-aliases.properties[/+]Element libraries: each namespace publishes elements/<Name>.apiext an…
[-]Element libraries: each namespace publishes elements/<Name>.apiext and tag-aliases.properties[/-][+]Element libraries: a namespace's components folder holds its .apiext files, and optionally a published contract (parsley.taglib)[/+]Element libraries: a namespace's components folder holds its .apiext …
Element libraries: a namespace's components folder holds its .apiext …
Element libraries: a namespace's components folder holds its .apiext …
Element libraries: a namespace's components folder holds its .apiext …
Element libraries: a namespace's components folder holds its .apiext …
Element libraries: a namespace's components folder holds its .apiext …
Element libraries: a namespace's components folder holds its .apiext …
Element libraries: a namespace's components folder holds its .apiext …
hugithordarson commentedon Oct 14, 2026
Parked (October 2026)
How elements are declared, found and named is central to how the framework works, and the design kept moving while this was worked on. It's parked until it has had time to settle. Nothing is waiting for it: editor support is deferred, no library needs to hide elements yet, and the global-tags question (#63) is separate.
What landed on master (the parts that settled):
require()(77cf7cb)..apiextandparsley.taglib, in ng-template-parser (09eb1f2)..apiextfiles inng/ng/components/(701cf22), naming their classes in full (faeebc9).What's parked on the
taglibbranch (one commit): loading each namespace's elements and tags, the optional taglib contract, per-namespace tag alias files, and removing code registration (NGPlugin.elements()deprecated).Open questions to settle first, from two reviews of the branch:
scopeattribute in the.apiextmight be the smaller answer.parsley.taglibholds no tags, while the file that does is a properties file..apiext, or found on the classpath. Fewer would be simpler to explain.NGResourceLoaderalready knows where a namespace's resources live, so listing a namespace's components folder should go through it, not through the element manager.The website's element reference reads ng-appserver's files directly in the meantime.