@adobe/design-data-spec 4.4.0 → 4.5.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.
Files changed (36) hide show
  1. package/conformance/manifest-extensions/README.md +47 -31
  2. package/conformance/manifest-extensions/base/dataset.json +36 -3
  3. package/conformance/manifest-extensions/invalid/implementations-remove-no-match/expected-errors.json +10 -0
  4. package/conformance/manifest-extensions/invalid/implementations-remove-no-match/extensions/implementations/button.json +9 -0
  5. package/conformance/manifest-extensions/invalid/implementations-remove-no-match/manifest.json +5 -0
  6. package/conformance/manifest-extensions/invalid/implementations-row-missing-reference/expected-errors.json +7 -0
  7. package/conformance/manifest-extensions/invalid/implementations-row-missing-reference/extensions/implementations/button.json +9 -0
  8. package/conformance/manifest-extensions/invalid/implementations-row-missing-reference/manifest.json +5 -0
  9. package/conformance/manifest-extensions/invalid/implementations-unknown-component/expected-errors.json +10 -0
  10. package/conformance/manifest-extensions/invalid/implementations-unknown-component/extensions/implementations/nope.json +12 -0
  11. package/conformance/manifest-extensions/invalid/implementations-unknown-component/manifest.json +5 -0
  12. package/conformance/manifest-extensions/invalid/implementations-unknown-op/expected-errors.json +7 -0
  13. package/conformance/manifest-extensions/invalid/implementations-unknown-op/extensions/implementations/button.json +9 -0
  14. package/conformance/manifest-extensions/invalid/implementations-unknown-op/manifest.json +5 -0
  15. package/conformance/manifest-extensions/valid/implementations-later-file-wins/expected.json +19 -0
  16. package/conformance/manifest-extensions/valid/implementations-later-file-wins/extensions/implementations/a-button.json +11 -0
  17. package/conformance/manifest-extensions/valid/implementations-later-file-wins/extensions/implementations/b-button.json +11 -0
  18. package/conformance/manifest-extensions/valid/implementations-later-file-wins/manifest.json +5 -0
  19. package/conformance/manifest-extensions/valid/implementations-other-owner-untouched/expected.json +26 -0
  20. package/conformance/manifest-extensions/valid/implementations-other-owner-untouched/extensions/implementations/button.json +18 -0
  21. package/conformance/manifest-extensions/valid/implementations-other-owner-untouched/manifest.json +5 -0
  22. package/conformance/manifest-extensions/valid/implementations-platform-local-component/expected.json +12 -0
  23. package/conformance/manifest-extensions/valid/implementations-platform-local-component/extensions/components/tab-bar-ios.json +9 -0
  24. package/conformance/manifest-extensions/valid/implementations-platform-local-component/extensions/implementations/tab-bar-ios.json +10 -0
  25. package/conformance/manifest-extensions/valid/implementations-platform-local-component/manifest.json +5 -0
  26. package/conformance/manifest-extensions/valid/implementations-remove-row/expected.json +11 -0
  27. package/conformance/manifest-extensions/valid/implementations-remove-row/extensions/implementations/button.json +10 -0
  28. package/conformance/manifest-extensions/valid/implementations-remove-row/manifest.json +5 -0
  29. package/conformance/manifest-extensions/valid/implementations-upsert-foundation-row/expected.json +19 -0
  30. package/conformance/manifest-extensions/valid/implementations-upsert-foundation-row/extensions/implementations/button.json +12 -0
  31. package/conformance/manifest-extensions/valid/implementations-upsert-foundation-row/manifest.json +5 -0
  32. package/package.json +2 -1
  33. package/schemas/component.schema.json +8 -3
  34. package/schemas/implementation-mapping.schema.json +69 -0
  35. package/spec/component-format.md +40 -19
  36. package/spec/manifest.md +71 -19
@@ -4,12 +4,14 @@ Exercises the Layer 2 platform manifest's `extensions/` **directory** loader
4
4
  (`packages/design-data-spec/spec/manifest.md#extensions-directory`): a sibling
5
5
  `extensions/` tree (or a custom name via the manifest's `extensionsDir` field)
6
6
  whose category subdirectories (`tokens/`, `components/`, `fields/`,
7
- `guidelines/`, `mode-sets/`, `platform-extensions/`, `relationships/`) are discovered,
7
+ `guidelines/`, `mode-sets/`, `platform-extensions/`, `relationships/`,
8
+ `implementations/`) are discovered,
8
9
  glob+merged in sorted path order, per-fragment schema-validated, and spliced
9
10
  into the manifest before it's applied to the graph.
10
11
 
11
12
  * `base/dataset.json` — a shared minimal seed graph (three foundation tokens:
12
- `btn-bg`, `btn-fg`, `chk-bg` on the `button`/`checkbox` components), loaded
13
+ `btn-bg`, `btn-fg`, `chk-bg` on the `button`/`checkbox` components, plus a
14
+ foundation `button` component with two default `implementations` rows), loaded
13
15
  once per case before that case's `manifest.json` is applied via
14
16
  `apply_configured`.
15
17
  * `valid/<case>/` — `manifest.json` + an `extensions/` (or custom-named) tree
@@ -19,13 +21,18 @@ into the manifest before it's applied to the graph.
19
21
  {
20
22
  "relationships": {
21
23
  "byUuid": { "<uuid>": { "count": 1, "value": "8px" } },
22
- "absent": ["<uuid>"]
24
+ "absent": ["<uuid>"],
23
25
  },
24
26
  "components": { "present": ["tab-bar-ios"] },
25
27
  "fields": { "present": ["hapticStyle"] },
26
28
  "modeSets": { "present": ["interfaceLevel"] },
27
29
  "guidelines": { "present": ["ios-haptics"] },
28
- "tokens": { "orderByUuid": ["<uuid-1>", "<uuid-2>"] }
30
+ "tokens": { "orderByUuid": ["<uuid-1>", "<uuid-2>"] },
31
+ "implementations": {
32
+ "<component>": [
33
+ /* exact merged rows */
34
+ ],
35
+ },
29
36
  }
30
37
  ```
31
38
  A case that only needs to prove "loads clean" uses `{}`.
@@ -37,30 +44,39 @@ Rust SDK drives these fixtures in `sdk/core/src/lib.rs` via the
37
44
  `manifest_extensions_conformance` (pass/fail + error message matching) and
38
45
  `manifest_extensions_behavior` (post-apply graph predicates) test modules.
39
46
 
40
- | Case | Intent |
41
- | ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
42
- | `valid/injects-component` | A `components/` fragment is injected as a Platform-layer component. |
43
- | `valid/injects-platform-extension` | A `platform-extensions/` fragment is injected. |
44
- | `valid/injects-field` | A `fields/` fragment is injected. |
45
- | `valid/injects-guideline` | A `guidelines/` fragment is injected. |
46
- | `valid/injects-mode-set` | A `mode-sets/` fragment is injected (declare-or-replace by name). |
47
- | `valid/injects-relationship` | A `relationships/` plain-add fragment is injected. |
48
- | `valid/override-relationship-by-uuid` | An `op: "override"` entry replaces a plain add sharing its `uuid`, regardless of file sort order. |
49
- | `valid/plain-add-uuid-collision-append` | Two plain adds sharing a `uuid` both append rather than one overwriting the other. |
50
- | `valid/remove-relationship-by-uuid` | An `op: "remove"` entry deletes the matching relationship. |
51
- | `valid/tokens-sorted-order` | Multiple `tokens/*.tokens.json` files concatenate in sorted path order. |
52
- | `valid/later-file-wins` | Two `components/` fragments declaring the same component name dedupe to one, later file wins. |
53
- | `valid/custom-extensions-dir` | The manifest's `extensionsDir` field is honored in place of the default `extensions/` name. |
54
- | `valid/override-entry-skips-schema-validation` | An `op: "override"` relationship entry is not schema-validated against `relationship.schema.json` (only plain adds are). |
55
- | `invalid/override-missing-uuid` | An `op: "override"` relationship entry missing `uuid` fails loudly. |
56
- | `invalid/unknown-term-id` | A `platform-extensions/` fragment referencing a non-existent `termId` fails loudly. |
57
- | `invalid/component-missing-name` | A `components/` fragment missing `name` fails Layer 1 fragment schema validation. |
58
- | `invalid/field-invalid-kind` | A `fields/` fragment with an invalid `kind` enum value fails schema validation. |
59
- | `invalid/guideline-invalid-category` | A `guidelines/` fragment with an invalid `category` enum value fails schema validation. |
60
- | `invalid/mode-set-missing-default` | A `mode-sets/` fragment missing the required `default` field fails Layer 1 fragment schema validation. |
61
- | `invalid/platform-extension-missing-extends` | A `platform-extensions/` fragment missing `extends` fails schema validation. |
62
- | `invalid/token-invalid-uuid` | A `tokens/*.tokens.json` fragment with a malformed `uuid` fails schema validation. |
63
- | `invalid/relationship-missing-scope` | A `relationships/` plain-add fragment missing `scope` fails schema validation. |
64
- | `invalid/extensions-dir-parent-traversal` | `extensionsDir` containing a `..` component is rejected. |
65
- | `invalid/extensions-dir-absolute-path` | An absolute `extensionsDir` path is rejected. |
66
- | `invalid/unknown-subdir-name` | An unrecognized subdirectory directly under `extensions/` (not one of the six known categories) is rejected by name. |
47
+ | Case | Intent |
48
+ | ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------- |
49
+ | `valid/injects-component` | A `components/` fragment is injected as a Platform-layer component. |
50
+ | `valid/injects-platform-extension` | A `platform-extensions/` fragment is injected. |
51
+ | `valid/injects-field` | A `fields/` fragment is injected. |
52
+ | `valid/injects-guideline` | A `guidelines/` fragment is injected. |
53
+ | `valid/injects-mode-set` | A `mode-sets/` fragment is injected (declare-or-replace by name). |
54
+ | `valid/injects-relationship` | A `relationships/` plain-add fragment is injected. |
55
+ | `valid/override-relationship-by-uuid` | An `op: "override"` entry replaces a plain add sharing its `uuid`, regardless of file sort order. |
56
+ | `valid/plain-add-uuid-collision-append` | Two plain adds sharing a `uuid` both append rather than one overwriting the other. |
57
+ | `valid/remove-relationship-by-uuid` | An `op: "remove"` entry deletes the matching relationship. |
58
+ | `valid/tokens-sorted-order` | Multiple `tokens/*.tokens.json` files concatenate in sorted path order. |
59
+ | `valid/later-file-wins` | Two `components/` fragments declaring the same component name dedupe to one, later file wins. |
60
+ | `valid/custom-extensions-dir` | The manifest's `extensionsDir` field is honored in place of the default `extensions/` name. |
61
+ | `valid/override-entry-skips-schema-validation` | An `op: "override"` relationship entry is not schema-validated against `relationship.schema.json` (only plain adds are). |
62
+ | `valid/implementations-upsert-foundation-row` | An `implementations/` row replaces the foundation default sharing its `importPath` and is stamped with the manifest `platform`. |
63
+ | `valid/implementations-later-file-wins` | Two fragments upserting the same stamped row dedupe by `implementation` id; the later file wins. |
64
+ | `valid/implementations-remove-row` | An `op: "remove"` selector deletes the matching foundation default. |
65
+ | `valid/implementations-platform-local-component` | A fragment may target a component added by the same manifest's `components/`. |
66
+ | `valid/implementations-other-owner-untouched` | A row stamped with a different `implementation` id is never matched by another owner's upsert. |
67
+ | `invalid/override-missing-uuid` | An `op: "override"` relationship entry missing `uuid` fails loudly. |
68
+ | `invalid/unknown-term-id` | A `platform-extensions/` fragment referencing a non-existent `termId` fails loudly. |
69
+ | `invalid/component-missing-name` | A `components/` fragment missing `name` fails Layer 1 fragment schema validation. |
70
+ | `invalid/field-invalid-kind` | A `fields/` fragment with an invalid `kind` enum value fails schema validation. |
71
+ | `invalid/guideline-invalid-category` | A `guidelines/` fragment with an invalid `category` enum value fails schema validation. |
72
+ | `invalid/mode-set-missing-default` | A `mode-sets/` fragment missing the required `default` field fails Layer 1 fragment schema validation. |
73
+ | `invalid/platform-extension-missing-extends` | A `platform-extensions/` fragment missing `extends` fails schema validation. |
74
+ | `invalid/token-invalid-uuid` | A `tokens/*.tokens.json` fragment with a malformed `uuid` fails schema validation. |
75
+ | `invalid/relationship-missing-scope` | A `relationships/` plain-add fragment missing `scope` fails schema validation. |
76
+ | `invalid/extensions-dir-parent-traversal` | `extensionsDir` containing a `..` component is rejected. |
77
+ | `invalid/extensions-dir-absolute-path` | An absolute `extensionsDir` path is rejected. |
78
+ | `invalid/unknown-subdir-name` | An unrecognized subdirectory directly under `extensions/` (not one of the known categories) is rejected by name. |
79
+ | `invalid/implementations-unknown-component` | An `implementations/` fragment naming a component absent from the catalog fails loudly. |
80
+ | `invalid/implementations-remove-no-match` | An `op: "remove"` matching no owned row fails loudly. |
81
+ | `invalid/implementations-row-missing-reference` | A row with neither `package` nor `importPath` fails schema validation. |
82
+ | `invalid/implementations-unknown-op` | An `op` other than `"remove"` fails schema validation. |
@@ -2,21 +2,54 @@
2
2
  "tokens": [
3
3
  {
4
4
  "key": "btn-bg",
5
- "name": { "property": "background-color", "component": "button" },
5
+ "name": {
6
+ "property": "background-color",
7
+ "component": "button"
8
+ },
6
9
  "value": "#aaa",
7
10
  "uuid": "u-btn-bg"
8
11
  },
9
12
  {
10
13
  "key": "btn-fg",
11
- "name": { "property": "color", "component": "button" },
14
+ "name": {
15
+ "property": "color",
16
+ "component": "button"
17
+ },
12
18
  "value": "#111",
13
19
  "uuid": "u-btn-fg"
14
20
  },
15
21
  {
16
22
  "key": "chk-bg",
17
- "name": { "property": "background-color", "component": "checkbox" },
23
+ "name": {
24
+ "property": "background-color",
25
+ "component": "checkbox"
26
+ },
18
27
  "value": "#bbb",
19
28
  "uuid": "u-chk-bg"
20
29
  }
30
+ ],
31
+ "components": [
32
+ {
33
+ "$id": "https://example.com/components/button",
34
+ "name": "button",
35
+ "displayName": "Button",
36
+ "meta": {
37
+ "category": "actions",
38
+ "documentationUrl": "https://example.com"
39
+ },
40
+ "implementations": [
41
+ {
42
+ "platform": "web",
43
+ "componentName": "Button",
44
+ "package": "@react-spectrum/s2"
45
+ },
46
+ {
47
+ "platform": "web",
48
+ "componentName": "Button",
49
+ "importPath": "@adobe/spectrum-wc/components/button",
50
+ "notes": "Spectrum 2 web component custom element: swc-button."
51
+ }
52
+ ]
53
+ }
21
54
  ]
22
55
  }
@@ -0,0 +1,10 @@
1
+ {
2
+ "errors": [
3
+ {
4
+ "message_pattern": "matched no implementation row"
5
+ },
6
+ {
7
+ "message_pattern": "\"web-components\""
8
+ }
9
+ ]
10
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "component": "button",
3
+ "implementations": [
4
+ {
5
+ "op": "remove",
6
+ "componentName": "Missing"
7
+ }
8
+ ]
9
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "specVersion": "1.0.0-draft",
3
+ "foundationVersion": "1.0.0",
4
+ "platform": "web-components"
5
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "errors": [
3
+ {
4
+ "message_pattern": "implementation-mapping.schema.json"
5
+ }
6
+ ]
7
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "component": "button",
3
+ "implementations": [
4
+ {
5
+ "platform": "web",
6
+ "componentName": "Button"
7
+ }
8
+ ]
9
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "specVersion": "1.0.0-draft",
3
+ "foundationVersion": "1.0.0",
4
+ "platform": "web-components"
5
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "errors": [
3
+ {
4
+ "message_pattern": "\"nope\""
5
+ },
6
+ {
7
+ "message_pattern": "does not exist in the component catalog"
8
+ }
9
+ ]
10
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "component": "nope",
3
+ "implementations": [
4
+ {
5
+ "platform": "web",
6
+ "componentName": "Button",
7
+ "package": "@adobe/spectrum-wc",
8
+ "importPath": "@adobe/spectrum-wc/components/button",
9
+ "notes": "Custom element: swc-button."
10
+ }
11
+ ]
12
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "specVersion": "1.0.0-draft",
3
+ "foundationVersion": "1.0.0",
4
+ "platform": "web-components"
5
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "errors": [
3
+ {
4
+ "message_pattern": "implementation-mapping.schema.json"
5
+ }
6
+ ]
7
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "component": "button",
3
+ "implementations": [
4
+ {
5
+ "op": "replace",
6
+ "componentName": "Button"
7
+ }
8
+ ]
9
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "specVersion": "1.0.0-draft",
3
+ "foundationVersion": "1.0.0",
4
+ "platform": "web-components"
5
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "implementations": {
3
+ "button": [
4
+ {
5
+ "platform": "web",
6
+ "componentName": "Button",
7
+ "package": "@react-spectrum/s2",
8
+ "notes": "second",
9
+ "implementation": "react-spectrum"
10
+ },
11
+ {
12
+ "platform": "web",
13
+ "componentName": "Button",
14
+ "importPath": "@adobe/spectrum-wc/components/button",
15
+ "notes": "Spectrum 2 web component custom element: swc-button."
16
+ }
17
+ ]
18
+ }
19
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "component": "button",
3
+ "implementations": [
4
+ {
5
+ "platform": "web",
6
+ "componentName": "Button",
7
+ "package": "@react-spectrum/s2",
8
+ "notes": "first"
9
+ }
10
+ ]
11
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "component": "button",
3
+ "implementations": [
4
+ {
5
+ "platform": "web",
6
+ "componentName": "Button",
7
+ "package": "@react-spectrum/s2",
8
+ "notes": "second"
9
+ }
10
+ ]
11
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "specVersion": "1.0.0-draft",
3
+ "foundationVersion": "1.0.0",
4
+ "platform": "react-spectrum"
5
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "implementations": {
3
+ "button": [
4
+ {
5
+ "platform": "web",
6
+ "componentName": "Button",
7
+ "package": "@react-spectrum/s2",
8
+ "implementation": "react-spectrum",
9
+ "notes": "declared by another owner"
10
+ },
11
+ {
12
+ "platform": "web",
13
+ "componentName": "Button",
14
+ "importPath": "@adobe/spectrum-wc/components/button",
15
+ "notes": "Spectrum 2 web component custom element: swc-button."
16
+ },
17
+ {
18
+ "platform": "web",
19
+ "componentName": "Button",
20
+ "package": "@react-spectrum/s2",
21
+ "implementation": "web-components",
22
+ "notes": "web-components row with the same package"
23
+ }
24
+ ]
25
+ }
26
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "component": "button",
3
+ "implementations": [
4
+ {
5
+ "platform": "web",
6
+ "componentName": "Button",
7
+ "package": "@react-spectrum/s2",
8
+ "implementation": "react-spectrum",
9
+ "notes": "declared by another owner"
10
+ },
11
+ {
12
+ "platform": "web",
13
+ "componentName": "Button",
14
+ "package": "@react-spectrum/s2",
15
+ "notes": "web-components row with the same package"
16
+ }
17
+ ]
18
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "specVersion": "1.0.0-draft",
3
+ "foundationVersion": "1.0.0",
4
+ "platform": "web-components"
5
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "implementations": {
3
+ "tab-bar-ios": [
4
+ {
5
+ "platform": "ios",
6
+ "componentName": "SpectrumTabBar",
7
+ "package": "SpectrumUI",
8
+ "implementation": "ios"
9
+ }
10
+ ]
11
+ }
12
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "$id": "https://example.com/components/tab-bar-ios",
3
+ "name": "tab-bar-ios",
4
+ "displayName": "Tab Bar (iOS)",
5
+ "meta": {
6
+ "category": "navigation",
7
+ "documentationUrl": "https://example.com"
8
+ }
9
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "component": "tab-bar-ios",
3
+ "implementations": [
4
+ {
5
+ "platform": "ios",
6
+ "componentName": "SpectrumTabBar",
7
+ "package": "SpectrumUI"
8
+ }
9
+ ]
10
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "specVersion": "1.0.0-draft",
3
+ "foundationVersion": "1.0.0",
4
+ "platform": "ios"
5
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "implementations": {
3
+ "button": [
4
+ {
5
+ "platform": "web",
6
+ "componentName": "Button",
7
+ "package": "@react-spectrum/s2"
8
+ }
9
+ ]
10
+ }
11
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "component": "button",
3
+ "implementations": [
4
+ {
5
+ "op": "remove",
6
+ "componentName": "Button",
7
+ "importPath": "@adobe/spectrum-wc/components/button"
8
+ }
9
+ ]
10
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "specVersion": "1.0.0-draft",
3
+ "foundationVersion": "1.0.0",
4
+ "platform": "web-components"
5
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "implementations": {
3
+ "button": [
4
+ {
5
+ "platform": "web",
6
+ "componentName": "Button",
7
+ "package": "@react-spectrum/s2"
8
+ },
9
+ {
10
+ "platform": "web",
11
+ "componentName": "Button",
12
+ "package": "@adobe/spectrum-wc",
13
+ "importPath": "@adobe/spectrum-wc/components/button",
14
+ "notes": "Custom element: swc-button.",
15
+ "implementation": "web-components"
16
+ }
17
+ ]
18
+ }
19
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "component": "button",
3
+ "implementations": [
4
+ {
5
+ "platform": "web",
6
+ "componentName": "Button",
7
+ "package": "@adobe/spectrum-wc",
8
+ "importPath": "@adobe/spectrum-wc/components/button",
9
+ "notes": "Custom element: swc-button."
10
+ }
11
+ ]
12
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "specVersion": "1.0.0-draft",
3
+ "foundationVersion": "1.0.0",
4
+ "platform": "web-components"
5
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/design-data-spec",
3
- "version": "4.4.0",
3
+ "version": "4.5.0",
4
4
  "description": "Design Data Specification — prose, JSON Schemas, rule catalog, and conformance fixtures for the design data format",
5
5
  "type": "module",
6
6
  "repository": {
@@ -26,6 +26,7 @@
26
26
  "./schemas/manifest.schema.json": "./schemas/manifest.schema.json",
27
27
  "./schemas/value-types/color.schema.json": "./schemas/value-types/color.schema.json",
28
28
  "./schemas/component.schema.json": "./schemas/component.schema.json",
29
+ "./schemas/implementation-mapping.schema.json": "./schemas/implementation-mapping.schema.json",
29
30
  "./schemas/anatomy-part.schema.json": "./schemas/anatomy-part.schema.json",
30
31
  "./schemas/state-declaration.schema.json": "./schemas/state-declaration.schema.json",
31
32
  "./rules/rules.yaml": "./rules/rules.yaml",
@@ -42,7 +42,7 @@
42
42
  "items": {
43
43
  "$ref": "#/$defs/implementation"
44
44
  },
45
- "description": "Known platform implementations of this component. Foundation-owned canonical mappings; platform-specific overrides are not supported yet. See spec/component-format.md#implementations."
45
+ "description": "Known platform implementations of this component. Foundation data carries canonical default mappings; a platform manifest refines its own rows through extensions/implementations/. See spec/component-format.md#implementations."
46
46
  },
47
47
  "options": {
48
48
  "$ref": "#/$defs/optionsMap"
@@ -115,6 +115,11 @@
115
115
  "minLength": 1,
116
116
  "description": "Target platform identifier (for example, 'web', 'ios', or 'android'). Multiple implementations may target the same platform when they have distinct packages."
117
117
  },
118
+ "implementation": {
119
+ "type": "string",
120
+ "pattern": "^[a-z][a-z0-9-]*$",
121
+ "description": "Implementation project id from the platform-implementations registry (for example, 'react-spectrum' or 'web-components'). Distinguishes implementations that share a `platform` device target. A platform manifest stamps its own `platform` id here when merging extensions/implementations/ entries that omit it."
122
+ },
118
123
  "componentName": {
119
124
  "type": "string",
120
125
  "minLength": 1,
@@ -123,7 +128,7 @@
123
128
  "importPath": {
124
129
  "type": "string",
125
130
  "minLength": 1,
126
- "description": "Importable module or source path for a platform implementation."
131
+ "description": "Importable module or source path for a platform implementation. When `package` is also set, this SHOULD be a subpath export of that package."
127
132
  },
128
133
  "package": {
129
134
  "type": "string",
@@ -135,7 +140,7 @@
135
140
  "description": "Optional implementation-specific usage or mapping notes."
136
141
  }
137
142
  },
138
- "oneOf": [
143
+ "anyOf": [
139
144
  {
140
145
  "required": ["importPath"]
141
146
  },
@@ -0,0 +1,69 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://opensource.adobe.com/spectrum-design-data/schemas/v0/implementation-mapping.schema.json",
4
+ "title": "Platform implementation mapping",
5
+ "description": "Layer 1 schema for one platform manifest extensions/implementations/ fragment: the platform's own implementation rows for a single component, merged into that component's `implementations` array. See spec/manifest.md#extensionsimplementations.",
6
+ "type": "object",
7
+ "required": ["component", "implementations"],
8
+ "properties": {
9
+ "$schema": {
10
+ "type": "string",
11
+ "format": "uri"
12
+ },
13
+ "component": {
14
+ "type": "string",
15
+ "pattern": "^[a-z][a-z0-9-]*$",
16
+ "description": "Name of the component (foundation or platform-local) whose `implementations` this fragment refines."
17
+ },
18
+ "implementations": {
19
+ "type": "array",
20
+ "minItems": 1,
21
+ "items": {
22
+ "if": {
23
+ "required": ["op"]
24
+ },
25
+ "then": {
26
+ "$ref": "#/$defs/removeEntry"
27
+ },
28
+ "else": {
29
+ "$ref": "component.schema.json#/$defs/implementation"
30
+ }
31
+ },
32
+ "description": "Rows to upsert (implementation objects) or remove (`op: \"remove\"` selectors), applied in order."
33
+ }
34
+ },
35
+ "additionalProperties": false,
36
+ "$defs": {
37
+ "removeEntry": {
38
+ "type": "object",
39
+ "description": "Removes every existing row of the component whose fields equal each selector field given here.",
40
+ "required": ["op", "componentName"],
41
+ "properties": {
42
+ "op": {
43
+ "const": "remove"
44
+ },
45
+ "platform": {
46
+ "type": "string",
47
+ "minLength": 1
48
+ },
49
+ "implementation": {
50
+ "type": "string",
51
+ "pattern": "^[a-z][a-z0-9-]*$"
52
+ },
53
+ "componentName": {
54
+ "type": "string",
55
+ "minLength": 1
56
+ },
57
+ "package": {
58
+ "type": "string",
59
+ "minLength": 1
60
+ },
61
+ "importPath": {
62
+ "type": "string",
63
+ "minLength": 1
64
+ }
65
+ },
66
+ "additionalProperties": false
67
+ }
68
+ }
69
+ }
@@ -84,30 +84,40 @@ where `{name}` matches the component's `name` field.
84
84
  ## Implementations
85
85
 
86
86
  `implementations` declares the published platform implementations corresponding
87
- to this design-system component. It is foundation-owned canonical data so
88
- consumers, including Code Connect generators, can resolve an implementation
89
- without platform-specific configuration.
87
+ to this design-system component, so consumers such as Code Connect generators
88
+ can resolve an implementation without platform-specific configuration.
90
89
 
91
- Each entry **MUST** contain `platform` and `componentName`, plus exactly one
92
- implementation reference: `package` for a published package or `importPath`
93
- for an importable module/source path. `notes` is optional context for consumers.
94
- Multiple entries MAY use the same `platform` when their packages distinguish
95
- different implementations for that platform.
90
+ Each entry **MUST** contain `platform` and `componentName`, plus at least one
91
+ implementation reference: `package` for a published package and/or
92
+ `importPath` for an importable module or source path. When both are present,
93
+ `importPath` **SHOULD** be a subpath export of `package`. `notes` is optional
94
+ context for consumers.
95
+
96
+ `platform` is a device target (`web`, `ios`, `android`), so several
97
+ implementations may share it. The optional `implementation` field names the
98
+ implementation project from the platform-implementations registry (for example
99
+ `react-spectrum` or `web-components`) to tell them apart.
96
100
 
97
101
  ```json
98
102
  "implementations": [
99
103
  {
100
104
  "platform": "web",
105
+ "implementation": "web-components",
101
106
  "componentName": "Button",
102
- "package": "@spectrum-web-components/button"
107
+ "package": "@adobe/spectrum-wc",
108
+ "importPath": "@adobe/spectrum-wc/components/button"
103
109
  }
104
110
  ]
105
111
  ```
106
112
 
107
- Platform manifests do not override `implementations` in this version. A future
108
- extension may add platform-owned overrides after consumers need them; until
109
- then, component declarations remain the canonical source of implementation
110
- names and references.
113
+ ### Layering
114
+
115
+ Foundation component data carries canonical **default** rows. A platform
116
+ manifest refines its own rows through
117
+ [`extensions/implementations/`](manifest.md#extensionsimplementations):
118
+ upserting rows it owns and removing stale ones, without restating the rest of
119
+ the component. Rows owned by another implementation are left untouched, so the
120
+ merged catalog still lists every implementation of the component.
111
121
 
112
122
  ## Options
113
123
 
@@ -426,13 +436,18 @@ A complete button component declaration:
426
436
  }
427
437
  ],
428
438
  "anatomy": [
429
- { "name": "icon", "description": "Leading icon." },
439
+ { "name": "icon", "description": "Leading icon." },
430
440
  { "name": "label", "description": "Button text.", "required": true }
431
441
  ],
432
442
  "states": [
433
- { "name": "hover", "trigger": "interaction", "precedence": 50 },
434
- { "name": "focus", "trigger": "interaction", "precedence": 60, "layered": true },
435
- { "name": "disabled", "trigger": "prop", "precedence": 100 }
443
+ { "name": "hover", "trigger": "interaction", "precedence": 50 },
444
+ {
445
+ "name": "focus",
446
+ "trigger": "interaction",
447
+ "precedence": 60,
448
+ "layered": true
449
+ },
450
+ { "name": "disabled", "trigger": "prop", "precedence": 100 }
436
451
  ],
437
452
  "lifecycle": {
438
453
  "introduced": "1.0.0-draft"
@@ -448,7 +463,10 @@ A complete button component declaration:
448
463
  {
449
464
  "name": "button",
450
465
  "displayName": "Button",
451
- "meta": { "category": "actions", "documentationUrl": "https://spectrum.adobe.com/page/button/" },
466
+ "meta": {
467
+ "category": "actions",
468
+ "documentationUrl": "https://spectrum.adobe.com/page/button/"
469
+ },
452
470
  "accessibility": {
453
471
  "role": "button",
454
472
  "intents": ["trigger"],
@@ -479,7 +497,10 @@ A complete button component declaration:
479
497
  {
480
498
  "name": "button",
481
499
  "displayName": "Button",
482
- "meta": { "category": "actions", "documentationUrl": "https://spectrum.adobe.com/page/button/" },
500
+ "meta": {
501
+ "category": "actions",
502
+ "documentationUrl": "https://spectrum.adobe.com/page/button/"
503
+ },
483
504
  "documentBlocks": [
484
505
  {
485
506
  "type": "purpose",
package/spec/manifest.md CHANGED
@@ -8,25 +8,26 @@ This document defines the **platform manifest**: how a platform implementation r
8
8
 
9
9
  The manifest supports a fixed, enumerated set of operations against the foundation — it does **not** allow overriding, aliasing, or removing arbitrary foundation artifacts. Support is concentrated on tokens; translations and schemas have no manifest-level override mechanism at all.
10
10
 
11
- | Operation | Supported? | Field | Applies to |
12
- | --------------------------------------------------- | ------------------------------------------------------------ | --------------------------------- | --------------------- |
13
- | Remove / exclude | Yes | `exclude` | Tokens only |
14
- | Include / whitelist | Yes | `include` | Tokens only |
15
- | Override value (type-preserving) | Yes | `overrides[].value` | Tokens only |
16
- | Override → re-alias | Yes | `overrides[].$ref` | Tokens only |
17
- | Add new tokens (may alias via `$ref`) | Yes | `extensions/tokens/` | Tokens |
18
- | Add / replace components | Yes | `extensions/components/` | Components |
19
- | Add / replace field declarations | Yes | `extensions/fields/` | Fields |
20
- | Add / replace guideline documents | Yes | `extensions/guidelines/` | Guidelines |
21
- | Declare / replace platform-local mode set | Yes | `extensions/mode-sets/` | Mode sets |
22
- | Add / remove / retarget a single mode; remove a set | Yes | `extensions/mode-sets/` (`op`) | Mode sets |
23
- | Add relationships/CTRs; override/remove by `uuid` | Yes | `extensions/relationships/` | Relationships (CTRs) |
24
- | Add / remove naming exceptions | Yes | `namingExceptions` | Naming validation |
25
- | Annotate existing terminology (cannot add new ids) | Yes | `extensions/platform-extensions/` | Existing registry ids |
26
- | Restrict allowed mode-set values | Yes | `modeSetRestrictions` | Mode sets |
27
- | Reformat name serialization | Schema-declared only, not yet applied by the reference SDK | `formatting` | Token name strings |
28
- | Override/remove/alias translations | No | — | — |
29
- | Override Layer-1 schemas | No (decided; see [spike](manifest-schema-override-spike.md)) | — | — |
11
+ | Operation | Supported? | Field | Applies to |
12
+ | --------------------------------------------------- | ------------------------------------------------------------ | --------------------------------- | --------------------------- |
13
+ | Remove / exclude | Yes | `exclude` | Tokens only |
14
+ | Include / whitelist | Yes | `include` | Tokens only |
15
+ | Override value (type-preserving) | Yes | `overrides[].value` | Tokens only |
16
+ | Override → re-alias | Yes | `overrides[].$ref` | Tokens only |
17
+ | Add new tokens (may alias via `$ref`) | Yes | `extensions/tokens/` | Tokens |
18
+ | Add / replace components | Yes | `extensions/components/` | Components |
19
+ | Add / replace field declarations | Yes | `extensions/fields/` | Fields |
20
+ | Add / replace guideline documents | Yes | `extensions/guidelines/` | Guidelines |
21
+ | Declare / replace platform-local mode set | Yes | `extensions/mode-sets/` | Mode sets |
22
+ | Add / remove / retarget a single mode; remove a set | Yes | `extensions/mode-sets/` (`op`) | Mode sets |
23
+ | Add relationships/CTRs; override/remove by `uuid` | Yes | `extensions/relationships/` | Relationships (CTRs) |
24
+ | Add / remove naming exceptions | Yes | `namingExceptions` | Naming validation |
25
+ | Annotate existing terminology (cannot add new ids) | Yes | `extensions/platform-extensions/` | Existing registry ids |
26
+ | Upsert / remove own implementation mappings | Yes | `extensions/implementations/` | Component `implementations` |
27
+ | Restrict allowed mode-set values | Yes | `modeSetRestrictions` | Mode sets |
28
+ | Reformat name serialization | Schema-declared only, not yet applied by the reference SDK | `formatting` | Token name strings |
29
+ | Override/remove/alias translations | No | — | — |
30
+ | Override Layer-1 schemas | No (decided; see [spike](manifest-schema-override-spike.md)) | — | — |
30
31
 
31
32
  ## Manifest document
32
33
 
@@ -112,6 +113,7 @@ extensions/
112
113
  relationships/ <component>.json one CTR set per file
113
114
  guidelines/ <topic>.json one guideline per file
114
115
  platform-extensions/ <platform>-<registry>.json
116
+ implementations/ <component>.json one component's implementation rows
115
117
  ```
116
118
 
117
119
  **NORMATIVE:** Each subdirectory is discovered by a recursive glob for `*.json` files (for
@@ -131,6 +133,9 @@ or empty subdirectory contributes nothing and is not an error.
131
133
  injected into the corresponding catalog **add-or-replace by name** (for
132
134
  `platform-extensions/`, by `termId`; see below). When two files declare the same name,
133
135
  **the entry from the later file (in sorted path order) wins.**
136
+ * **`implementations/`** — one component's implementation rows per file, merged row-by-row into
137
+ that component's existing `implementations` array (never whole-array replaced). See
138
+ [`extensions/implementations/`](#extensionsimplementations) below.
134
139
 
135
140
  **NORMATIVE:** Each fragment file **MUST** validate against its category's real JSON schema at
136
141
  load time — this is enforced reference-SDK behavior, not an aspirational goal:
@@ -142,6 +147,8 @@ load time — this is enforced reference-SDK behavior, not an aspirational goal:
142
147
  * **`relationships/`** → `relationship.schema.json` (see the Add/Override/remove rules above)
143
148
  * **`platform-extensions/`** → `platform-extension.json` (see
144
149
  [`extensions/platform-extensions/`](#extensionsplatform-extensions) below)
150
+ * **`implementations/`** → `implementation-mapping.schema.json` (see
151
+ [`extensions/implementations/`](#extensionsimplementations) below)
145
152
  * **`tokens/`** → `cascade-file.schema.json` (see above)
146
153
 
147
154
  A fragment that fails validation against its category schema is a manifest error — the
@@ -214,6 +221,51 @@ example, platform-specific state names). **NORMATIVE:** each file **MUST** valid
214
221
  foundation registry — this mechanism annotates existing ids, it does **NOT** introduce new
215
222
  ones.
216
223
 
224
+ #### `extensions/implementations/`
225
+
226
+ Platform-owned refinements of a component's
227
+ [`implementations`](component-format.md#implementations) — the packages, import paths, and
228
+ exported names a platform ships. Foundation component data carries canonical **default** rows;
229
+ a platform manifest refines only its own rows, so the implementation team that owns the
230
+ manifest also owns its mappings, and they travel with the manifest when it is ejected.
231
+
232
+ ```json
233
+ {
234
+ "component": "button",
235
+ "implementations": [
236
+ {
237
+ "platform": "web",
238
+ "componentName": "Button",
239
+ "package": "@react-spectrum/s2"
240
+ },
241
+ {
242
+ "op": "remove",
243
+ "componentName": "Button",
244
+ "package": "@react-spectrum/button"
245
+ }
246
+ ]
247
+ }
248
+ ```
249
+
250
+ **NORMATIVE:** each file **MUST** validate against `implementation-mapping.schema.json`.
251
+ `component` **MUST** name a component in the catalog after `extensions/components/` has been
252
+ applied (so a fragment may target a platform-local component); an unknown component is a
253
+ manifest error. Entries apply in array order, files in sorted path order.
254
+
255
+ **NORMATIVE:** each entry has an *owner*: its own `implementation` field if present, otherwise
256
+ the manifest's top-level `platform` id. A manifest only matches rows its owner owns — rows
257
+ stamped with the same `implementation` id, or unstamped foundation defaults. Rows stamped with
258
+ a different `implementation` id are never modified or removed.
259
+
260
+ * **Upsert** (no `op`) — a full implementation row. When the entry omits `implementation` and
261
+ the manifest declares `platform`, the stored row is stamped with that id. The row replaces the
262
+ first existing row with the same `platform` and `componentName` that either carries the same
263
+ `implementation` id, or is an unstamped foundation default sharing a `package` or `importPath`
264
+ value. Otherwise it is appended.
265
+ * **Remove** (`"op": "remove"`) — a selector of `componentName` plus any of `platform`,
266
+ `implementation`, `package`, `importPath`. It deletes every owned row whose values equal every
267
+ selector field given. A remove that matches no owned row is a manifest error.
268
+
217
269
  ### `namingExceptions`
218
270
 
219
271
  Platform-local overlay on the base naming-exceptions set used by naming validation.