@terrazzo/parser 0.0.2 → 0.0.3

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 (2) hide show
  1. package/package.json +1 -1
  2. package/parse/index.js +29 -18
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@terrazzo/parser",
3
3
  "description": "Parser/validator for the Design Tokens Community Group (DTCG) standard.",
4
- "version": "0.0.2",
4
+ "version": "0.0.3",
5
5
  "author": {
6
6
  "name": "Drew Powers",
7
7
  "email": "drew@pow.rs"
package/parse/index.js CHANGED
@@ -56,38 +56,41 @@ export default async function parse(input, { logger = new Logger(), skipLint = f
56
56
  // 2. Walk AST once to validate tokens
57
57
  const startValidation = performance.now();
58
58
  logger.debug({ group: 'parser', task: 'validate', message: 'Start tokens validation' });
59
- let last$Type;
60
- let last$TypePath = '';
59
+ const $typeInheritance = {};
61
60
  traverse(ast, {
62
61
  enter(node, parent, path) {
63
- // reset last$Type if not in a direct ancestor tree
64
- if (!last$TypePath || !path.join('.').startsWith(last$TypePath)) {
65
- last$Type = undefined;
66
- }
67
-
68
62
  if (node.type === 'Member' && node.value.type === 'Object' && node.value.members) {
69
63
  const members = getObjMembers(node.value);
70
64
 
71
- // keep track of closest-scoped $type
72
- // note: this is only reliable in a synchronous, single-pass traversal;
73
- // otherwise we’d have to do something more complicated
65
+ // keep track of $types
74
66
  if (members.$type && members.$type.type === 'String' && !members.$value) {
75
- last$Type = node.value.members.find((m) => m.name.value === '$type');
76
- last$TypePath = path.join('.');
67
+ $typeInheritance[path.join('.') || '.'] = node.value.members.find((m) => m.name.value === '$type');
77
68
  }
78
69
 
79
70
  if (members.$value) {
80
71
  const extensions = members.$extensions ? getObjMembers(members.$extensions) : undefined;
81
72
  const sourceNode = structuredClone(node);
82
- if (last$Type && !members.$type) {
83
- sourceNode.value = injectObjMembers(sourceNode.value, [last$Type]);
73
+ const id = path.join('.');
74
+
75
+ // get parent type by taking the closest-scoped $type (length === closer)
76
+ let parent$type;
77
+ let longestPath = '';
78
+ for (const [k, v] of Object.entries($typeInheritance)) {
79
+ if (k === '.' || id.startsWith(k)) {
80
+ if (k.length > longestPath.length) {
81
+ parent$type = v;
82
+ longestPath = k;
83
+ }
84
+ }
85
+ }
86
+ if (parent$type && !members.$type) {
87
+ sourceNode.value = injectObjMembers(sourceNode.value, [parent$type]);
84
88
  }
85
89
  validate(sourceNode, { ast, logger });
86
90
 
87
- const id = path.join('.');
88
91
  const group = { id: splitID(id).group, tokens: [] };
89
- if (last$Type) {
90
- group.$type = last$Type.value.value;
92
+ if (parent$type) {
93
+ group.$type = parent$type.value.value;
91
94
  }
92
95
  // note: this will also include sibling tokens, so be selective about only accessing group-specific properties
93
96
  const groupMembers = getObjMembers(parent);
@@ -98,7 +101,7 @@ export default async function parse(input, { logger = new Logger(), skipLint = f
98
101
  group.$extensions = evaluate(groupMembers.$extensions);
99
102
  }
100
103
  const token = {
101
- $type: members.$type?.value ?? last$Type?.value.value,
104
+ $type: members.$type?.value ?? parent$type?.value.value,
102
105
  $value: evaluate(members.$value),
103
106
  id,
104
107
  mode: {},
@@ -130,6 +133,14 @@ export default async function parse(input, { logger = new Logger(), skipLint = f
130
133
  logger.warn({ message: `Group ${id} has "value". Did you mean "$value"?`, node, ast });
131
134
  }
132
135
  }
136
+
137
+ // edge case: if $type appears at root of tokens.json, collect it
138
+ if (node.type === 'Document' && node.body.type === 'Object' && node.body.members) {
139
+ const members = getObjMembers(node.body);
140
+ if (members.$type && members.$type.type === 'String' && !members.$value) {
141
+ $typeInheritance['.'] = node.body.members.find((m) => m.name.value === '$type');
142
+ }
143
+ }
133
144
  },
134
145
  });
135
146
  logger.debug({