@vertekum/schema-builder 0.2.1 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,13 +1,14 @@
1
1
  # @vertekum/schema-builder
2
2
 
3
- ## 0.2.1
3
+ ## 0.3.1
4
4
 
5
5
  ### Patch Changes
6
6
 
7
+ - [`7f9db48`](https://github.com/moros-oxus/vertekum/commit/7f9db48344c672b90a97f3f4608bf7de893799bf) Thanks [@tschemmer](https://github.com/tschemmer)! - `<@name>` resolution gains key precedence: an import keyed `name` wins outright — its root, else its own `name` production — so sibling imports' public productions can no longer shadow or collide with a keyed module. The cross-import production search remains as the fallback when no key matches.
7
8
  - Updated dependencies []:
8
- - @vertekum/core@0.2.1
9
+ - @vertekum/core@0.3.1
9
10
 
10
- ## 0.2.0
11
+ ## 0.3.0
11
12
 
12
13
  ### Minor Changes
13
14
 
@@ -26,8 +27,8 @@
26
27
  - [`ef1ca4a`](https://github.com/moros-oxus/vertekum/commit/ef1ca4a0b696b799275c0f0acf7fde9414793f8d) Thanks [@tschemmer](https://github.com/tschemmer)! - Pick/omit lists take references as members — set algebra with sets as operands: `<@fullcolors ![<@fullcolors/saturated>, white]>` omits every saturated name plus white. Operands resolve with normal scope and privacy rules, keep member-by-member validation, and an open member reference is refused.
27
28
 
28
29
  - [`1de5e76`](https://github.com/moros-oxus/vertekum/commit/1de5e76929f9a1e17e6625cb3bfd36a3fd86ceec) Thanks [@tschemmer](https://github.com/tschemmer)! - The input/output pair is configurable on the extension — `schemaBuilderExtension({ source: './src/dfn', out: './src/schemas' })`. `source` becomes the default sweep for `schema build`, `lint`, and `fmt`; `out` redirects built schemas, mirroring `source`'s directory structure. `schema build` also takes a positional `[out]` for one invocation: a directory argument mirrors into it, a file argument lands directly in it.
29
- - Updated dependencies [[`84172d2`](https://github.com/moros-oxus/vertekum/commit/84172d2ca90320b60ec4c42730a69c056631a81c), [`82b695e`](https://github.com/moros-oxus/vertekum/commit/82b695e32cf1b7ee37f9d2f2f753aeff8ae3d15f)]:
30
- - @vertekum/core@0.2.0
30
+ - Updated dependencies [[`84172d2`](https://github.com/moros-oxus/vertekum/commit/84172d2ca90320b60ec4c42730a69c056631a81c)]:
31
+ - @vertekum/core@0.3.0
31
32
 
32
33
  ## 0.1.7
33
34
 
package/docs/modules.md CHANGED
@@ -38,8 +38,8 @@ use "./system/color.dfn" as system-color
38
38
 
39
39
  | Reference | Means |
40
40
  | -------------------- | ---------------------------------------------------------------------------- |
41
- | `<@key>` | the imported module's **root** — the whole subtree it grants. |
42
- | `<@name>` | the production `name`, searched across every import; unambiguous or an error. |
41
+ | `<@key>` | an import KEYED `key` **wins outright**: its root — else its own `key` production (the fragment idiom) — sibling imports' productions can never shadow or collide with it. |
42
+ | `<@name>` | no key matches: the production `name`, searched across every import; unambiguous or an error. |
43
43
  | `<@key/name>` | qualified: that import's production alone — the collision resolver. |
44
44
 
45
45
  When two imports both declare `<accent>`, the unqualified `<@accent>` refuses with the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vertekum/schema-builder",
3
- "version": "0.2.1",
3
+ "version": "0.3.1",
4
4
  "description": "Declare token vocabularies as grammar; build them into JSON Schema files",
5
5
  "license": "Apache-2.0",
6
6
  "keywords": [
@@ -37,13 +37,13 @@
37
37
  "./cli": "./src/cli.ts"
38
38
  },
39
39
  "peerDependencies": {
40
- "@vertekum/core": "0.2.1"
40
+ "@vertekum/core": "0.3.1"
41
41
  },
42
42
  "dependencies": {
43
43
  "zod": "^3.25.76"
44
44
  },
45
45
  "devDependencies": {
46
- "@vertekum/core": "0.2.1",
47
- "@vertekum/schema-vocabulary": "0.1.0"
46
+ "@vertekum/core": "0.3.1",
47
+ "@vertekum/schema-vocabulary": "0.3.0"
48
48
  }
49
49
  }
package/src/build.ts CHANGED
@@ -125,41 +125,51 @@ function target(
125
125
  };
126
126
  }
127
127
 
128
+ // 1. A module KEY match wins outright — a keyed module cannot be shadowed by (or
129
+ // collide with) sibling imports' productions. Its root; else its OWN production of
130
+ // the same name (the fragment-declares-its-own-name idiom); else the fragment hint.
131
+ const keyed = scope.module.imports.get(ref.name);
132
+ if (keyed) {
133
+ const keyedScope: Scope = {
134
+ module: keyed,
135
+ expanding: new Set(),
136
+ warn: scope.warn,
137
+ };
138
+ if (keyed.module.root) {
139
+ return {
140
+ node: keyed.module.root,
141
+ scope: keyedScope,
142
+ importedRoot: keyed,
143
+ };
144
+ }
145
+ const own = keyed.module.productions.get(ref.name);
146
+ if (own && !keyed.module.private.has(ref.name)) {
147
+ return { node: own, scope: keyedScope };
148
+ }
149
+ throw new DfnError(
150
+ `'${ref.name}' is imported, but it declares no root (a fragment) — reference one of its productions: ${productionsOf(ref.name, keyed)}`,
151
+ ref.line,
152
+ ref.column,
153
+ file,
154
+ );
155
+ }
156
+
157
+ // 2. No key match: the production searched across every import — unambiguous or say so.
128
158
  const hits: Array<{
129
159
  node: Node;
130
160
  scope: Scope;
131
161
  importedRoot?: ResolvedModule;
132
162
  }> = [];
133
- for (const [key, imported] of scope.module.imports) {
134
- const importedScope: Scope = {
135
- module: imported,
136
- expanding: new Set(),
137
- warn: scope.warn,
138
- };
163
+ for (const imported of scope.module.imports.values()) {
139
164
  const production = imported.module.productions.get(ref.name);
140
165
  if (production && !imported.module.private.has(ref.name)) {
141
- hits.push({ node: production, scope: importedScope });
142
- }
143
- if (key === ref.name && imported.module.root) {
144
166
  hits.push({
145
- node: imported.module.root,
146
- scope: importedScope,
147
- importedRoot: imported,
167
+ node: production,
168
+ scope: { module: imported, expanding: new Set(), warn: scope.warn },
148
169
  });
149
170
  }
150
171
  }
151
172
  if (hits.length === 0) {
152
- // The near-miss that reads as a missing `use`: the import EXISTS under this key, but
153
- // `<@key>` means its root and a fragment has none. Say so, and name the real forms.
154
- const fragment = scope.module.imports.get(ref.name);
155
- if (fragment) {
156
- throw new DfnError(
157
- `'${ref.name}' is imported, but it declares no root (a fragment) — reference one of its productions: ${productionsOf(ref.name, fragment)}`,
158
- ref.line,
159
- ref.column,
160
- file,
161
- );
162
- }
163
173
  throw new DfnError(
164
174
  `no import provides '<@${ref.name}>'`,
165
175
  ref.line,