" style="<%= checkForStyle(property) %>">
-
<%= property.path.join(".") %>
-
<%= property.name %>
- <% if(property.attributes && JSON.stringify(property.attributes)!=="{}") { %>
-
+ <% _.each(allTokens, function(token) { %>
+
" style="<%= checkForStyle(token) %>">
+
<%= token.path.join(".") %>
+
<%= token.name %>
+ <% if(token.attributes && JSON.stringify(token.attributes)!=="{}") { %>
+
<% } %>
-
<%= property.value %><% if(property.attributes.category === 'content' && property.attributes.type === 'icon') { %><%= property.value %><% } %>
- <% if(property.attributes && JSON.stringify(property.attributes)!=="{}") { %>
-
<%= JSON.stringify(property.attributes) %>
+
<%= token.$value ?? token.value %><% if(token.attributes.category === 'content' && token.attributes.type === 'icon') { %><%= token.$value ?? token.value %><% } %>
+ <% if(token.attributes && JSON.stringify(token.attributes)!=="{}") { %>
+
<%= JSON.stringify(token.attributes) %>
<% } %>
<% }); %>
diff --git a/lib/common/transforms.js b/lib/common/transforms.js
index 87221bac5..04d606eb9 100644
--- a/lib/common/transforms.js
+++ b/lib/common/transforms.js
@@ -93,7 +93,7 @@ function isContent(token) {
* @returns {string}
*/
function wrapValueWith(character, token) {
- return `${character}${token.value}${character}`;
+ return `${character}${token.$value ?? token.value}${character}`;
}
/**
@@ -180,7 +180,7 @@ export default {
type: 'attribute',
matcher: isColor,
transformer: function (token) {
- var color = Color(token.value);
+ const color = Color(token.$value ?? token.value);
return {
hex: color.toHex(),
rgb: color.toRgb(),
@@ -323,7 +323,7 @@ export default {
'name/ti/constant': {
type: 'name',
transformer: function (token, options) {
- var path = token.path.slice(1);
+ const path = token.path.slice(1);
return snakeCase([options.prefix].concat(path).join(' ')).toUpperCase();
},
},
@@ -366,7 +366,7 @@ export default {
type: 'value',
matcher: isColor,
transformer: function (token) {
- return Color(token.value).toRgbString();
+ return Color(token.$value ?? token.value).toRgbString();
},
},
@@ -386,7 +386,7 @@ export default {
type: 'value',
matcher: isColor,
transformer: function (token) {
- return Color(token.value).toHslString();
+ return Color(token.$value ?? token.value).toHslString();
},
},
@@ -406,9 +406,9 @@ export default {
type: 'value',
matcher: isColor,
transformer: function (token) {
- var color = Color(token.value);
- var o = color.toHsl();
- var vals = `${Math.round(o.h)} ${Math.round(o.s * 100)}% ${Math.round(o.l * 100)}%`;
+ const color = Color(token.$value ?? token.value);
+ const o = color.toHsl();
+ const vals = `${Math.round(o.h)} ${Math.round(o.s * 100)}% ${Math.round(o.l * 100)}%`;
if (color.getAlpha() === 1) {
return `hsl(${vals})`;
} else {
@@ -432,7 +432,7 @@ export default {
type: 'value',
matcher: isColor,
transformer: function (token) {
- return Color(token.value).toHexString();
+ return Color(token.$value ?? token.value).toHexString();
},
},
@@ -451,7 +451,7 @@ export default {
type: 'value',
matcher: isColor,
transformer: function (token) {
- return Color(token.value).toHex8String();
+ return Color(token.$value ?? token.value).toHex8String();
},
},
@@ -470,7 +470,7 @@ export default {
type: 'value',
matcher: isColor,
transformer: function (token) {
- var str = Color(token.value).toHex8();
+ const str = Color(token.$value ?? token.value).toHex8();
return '#' + str.slice(6) + str.slice(0, 6);
},
},
@@ -490,7 +490,7 @@ export default {
type: 'value',
matcher: isColor,
transformer: function (token) {
- var str = Color(token.value).toHex8();
+ const str = Color(token.$value ?? token.value).toHex8();
return 'Color(0x' + str.slice(6) + str.slice(0, 6) + ')';
},
},
@@ -510,7 +510,7 @@ export default {
type: 'value',
matcher: isColor,
transformer: function (token) {
- var rgb = Color(token.value).toRgb();
+ const rgb = Color(token.$value ?? token.value).toRgb();
return (
'[UIColor colorWithRed:' +
(rgb.r / 255).toFixed(3) +
@@ -543,7 +543,7 @@ export default {
type: 'value',
matcher: isColor,
transformer: function (token) {
- const { r, g, b, a } = Color(token.value).toRgb();
+ const { r, g, b, a } = Color(token.$value ?? token.value).toRgb();
const rFixed = (r / 255.0).toFixed(3);
const gFixed = (g / 255.0).toFixed(3);
const bFixed = (b / 255.0).toFixed(3);
@@ -566,7 +566,7 @@ export default {
type: 'value',
matcher: isColor,
transformer: function (token) {
- const { r, g, b, a } = Color(token.value).toRgb();
+ const { r, g, b, a } = Color(token.$value ?? token.value).toRgb();
const rFixed = (r / 255.0).toFixed(3);
const gFixed = (g / 255.0).toFixed(3);
const bFixed = (b / 255.0).toFixed(3);
@@ -590,7 +590,7 @@ export default {
type: 'value',
matcher: isColor,
transformer: function (token) {
- var color = Color(token.value);
+ const color = Color(token.$value ?? token.value);
if (color.getAlpha() === 1) {
return color.toHexString();
} else {
@@ -646,8 +646,8 @@ export default {
type: 'value',
matcher: isFontSize,
transformer: function (token) {
- const val = parseFloat(token.value);
- if (isNaN(val)) throwSizeError(token.name, token.value, 'sp');
+ const val = parseFloat(token.$value ?? token.value);
+ if (isNaN(val)) throwSizeError(token.name, token.$value ?? token.value, 'sp');
return val.toFixed(2) + 'sp';
},
},
@@ -667,8 +667,8 @@ export default {
type: 'value',
matcher: isNotFontSize,
transformer: function (token) {
- const val = parseFloat(token.value);
- if (isNaN(val)) throwSizeError(token.name, token.value, 'dp');
+ const val = parseFloat(token.$value ?? token.value);
+ if (isNaN(val)) throwSizeError(token.name, token.$value ?? token.value, 'dp');
return val.toFixed(2) + 'dp';
},
},
@@ -693,14 +693,15 @@ export default {
type: 'value',
matcher: isSize,
transformer: function (token, options) {
- var val = parseFloat(token.value);
- if (isNaN(val)) throwSizeError(token.name, token.value, 'object');
+ const value = token.$value ?? token.value;
+ const parsedVal = parseFloat(value);
+ if (isNaN(parsedVal)) throwSizeError(token.name, value, 'object');
return {
- original: token.value,
- number: val,
- decimal: val / 100,
- scale: val * getBasePxFontSize(options),
+ original: value,
+ number: parsedVal,
+ decimal: parsedVal / 100,
+ scale: parsedVal * getBasePxFontSize(options),
};
},
},
@@ -720,10 +721,11 @@ export default {
type: 'value',
matcher: isFontSize,
transformer: function (token, options) {
- const val = parseFloat(token.value);
+ const value = token.$value ?? token.value;
+ const parsedVal = parseFloat(value);
const baseFont = getBasePxFontSize(options);
- if (isNaN(val)) throwSizeError(token.name, token.value, 'sp');
- return (val * baseFont).toFixed(2) + 'sp';
+ if (isNaN(parsedVal)) throwSizeError(token.name, value, 'sp');
+ return (parsedVal * baseFont).toFixed(2) + 'sp';
},
},
@@ -742,10 +744,11 @@ export default {
type: 'value',
matcher: isNotFontSize,
transformer: function (token, options) {
- const val = parseFloat(token.value);
+ const value = token.$value ?? token.value;
+ const parsedVal = parseFloat(value);
const baseFont = getBasePxFontSize(options);
- if (isNaN(val)) throwSizeError(token.name, token.value, 'dp');
- return (val * baseFont).toFixed(2) + 'dp';
+ if (isNaN(parsedVal)) throwSizeError(token.name, value, 'dp');
+ return (parsedVal * baseFont).toFixed(2) + 'dp';
},
},
@@ -764,9 +767,10 @@ export default {
type: 'value',
matcher: isSize,
transformer: function (token) {
- const val = parseFloat(token.value);
- if (isNaN(val)) throwSizeError(token.name, token.value, 'px');
- return val + 'px';
+ const value = token.$value ?? token.value;
+ const parsedVal = parseFloat(value);
+ if (isNaN(parsedVal)) throwSizeError(token.name, value, 'px');
+ return parsedVal + 'px';
},
},
@@ -785,9 +789,9 @@ export default {
type: 'value',
matcher: isSize,
transformer: function (token) {
- const val = parseFloat(token.value);
- if (isNaN(val)) throwSizeError(token.name, token.value, 'rem');
- return val + 'rem';
+ const parsedVal = parseFloat(token.$value ?? token.value);
+ if (isNaN(parsedVal)) throwSizeError(token.name, token.$value ?? token.value, 'rem');
+ return parsedVal + 'rem';
},
},
@@ -806,10 +810,11 @@ export default {
type: 'value',
matcher: isSize,
transformer: function (token, options) {
- const val = parseFloat(token.value);
+ const value = token.$value ?? token.value;
+ const parsedVal = parseFloat(value);
const baseFont = getBasePxFontSize(options);
- if (isNaN(val)) throwSizeError(token.name, token.value, 'pt');
- return (val * baseFont).toFixed(2) + 'f';
+ if (isNaN(parsedVal)) throwSizeError(token.name, value, 'pt');
+ return (parsedVal * baseFont).toFixed(2) + 'f';
},
},
@@ -828,10 +833,11 @@ export default {
type: 'value',
matcher: isFontSize,
transformer: function (token, options) {
- const val = parseFloat(token.value);
+ const value = token.$value ?? token.value;
+ const parsedVal = parseFloat(value);
const baseFont = getBasePxFontSize(options);
- if (isNaN(val)) throwSizeError(token.name, token.value, 'sp');
- return (val * baseFont).toFixed(2) + '.sp';
+ if (isNaN(parsedVal)) throwSizeError(token.name, value, 'sp');
+ return (parsedVal * baseFont).toFixed(2) + '.sp';
},
},
@@ -850,10 +856,11 @@ export default {
type: 'value',
matcher: isNotFontSize,
transformer: function (token, options) {
- const val = parseFloat(token.value);
+ const value = token.$value ?? token.value;
+ const parsedVal = parseFloat(value);
const baseFont = getBasePxFontSize(options);
- if (isNaN(val)) throwSizeError(token.name, token.value, 'dp');
- return (val * baseFont).toFixed(2) + '.dp';
+ if (isNaN(parsedVal)) throwSizeError(token.name, value, 'dp');
+ return (parsedVal * baseFont).toFixed(2) + '.dp';
},
},
@@ -872,9 +879,10 @@ export default {
type: 'value',
matcher: isFontSize,
transformer: function (token) {
- const val = parseFloat(token.value);
- if (isNaN(val)) throwSizeError(token.name, token.value, 'em');
- return val + '.em';
+ const value = token.$value ?? token.value;
+ const parsedVal = parseFloat(value);
+ if (isNaN(parsedVal)) throwSizeError(token.name, value, 'em');
+ return parsedVal + '.em';
},
},
@@ -892,10 +900,11 @@ export default {
type: 'value',
matcher: isSize,
transformer: function (token, options) {
- const val = parseFloat(token.value);
+ const value = token.$value ?? token.value;
+ const parsedVal = parseFloat(value);
const baseFont = getBasePxFontSize(options);
- if (isNaN(val)) throwSizeError(token.name, token.value, 'CGFloat');
- return `CGFloat(${(val * baseFont).toFixed(2)})`;
+ if (isNaN(parsedVal)) throwSizeError(token.name, value, 'CGFloat');
+ return `CGFloat(${(parsedVal * baseFont).toFixed(2)})`;
},
},
@@ -914,10 +923,11 @@ export default {
type: 'value',
matcher: isSize,
transformer: function (token, options) {
- const val = parseFloat(token.value);
+ const value = token.$value ?? token.value;
+ const parsedVal = parseFloat(value);
const baseFont = getBasePxFontSize(options);
- if (isNaN(val)) throwSizeError(token.name, token.value, 'px');
- return (val * baseFont).toFixed(0) + 'px';
+ if (isNaN(parsedVal)) throwSizeError(token.name, value, 'px');
+ return (parsedVal * baseFont).toFixed(0) + 'px';
},
},
@@ -938,17 +948,18 @@ export default {
matcher: isSize,
transformer: (token, options) => {
const baseFont = getBasePxFontSize(options);
- const floatVal = parseFloat(token.value);
+ const value = token.$value ?? token.value;
+ const parsedVal = parseFloat(value);
- if (isNaN(floatVal)) {
- throwSizeError(token.name, token.value, 'rem');
+ if (isNaN(parsedVal)) {
+ throwSizeError(token.name, value, 'rem');
}
- if (floatVal === 0) {
+ if (parsedVal === 0) {
return '0';
}
- return `${floatVal / baseFont}rem`;
+ return `${parsedVal / baseFont}rem`;
},
},
@@ -969,7 +980,7 @@ export default {
return token.attributes?.category === 'content' && token.attributes.type === 'icon';
},
transformer: function (token) {
- return token.value.replace(
+ return (token.$value ?? token.value).replace(
UNICODE_PATTERN,
/**
* @param {string} match
@@ -1091,7 +1102,7 @@ export default {
return token.attributes?.category === 'time';
},
transformer: function (token) {
- return (parseFloat(token.value) / 1000).toFixed(2) + 's';
+ return (parseFloat(token.$value ?? token.value) / 1000).toFixed(2) + 's';
},
},
@@ -1110,7 +1121,7 @@ export default {
type: 'value',
matcher: isAsset,
transformer: function (token) {
- return convertToBase64(token.value);
+ return convertToBase64(token.$value ?? token.value);
},
},
@@ -1129,7 +1140,7 @@ export default {
type: 'value',
matcher: isAsset,
transformer: function (token) {
- return join(process.cwd(), token.value);
+ return join(process?.cwd() ?? '/', token.$value ?? token.value);
},
},
@@ -1181,7 +1192,9 @@ export default {
type: 'value',
matcher: isColor,
transformer: function (token) {
- var str = Color(token.value).toHex8().toUpperCase();
+ const str = Color(token.$value ?? token.value)
+ .toHex8()
+ .toUpperCase();
return `Color(0x${str.slice(6)}${str.slice(0, 6)})`;
},
},
@@ -1251,7 +1264,7 @@ export default {
matcher: isSize,
transformer: function (token, options) {
const baseFont = getBasePxFontSize(options);
- return (parseFloat(token.value) * baseFont).toFixed(2);
+ return (parseFloat(token.$value ?? token.value) * baseFont).toFixed(2);
},
},
};
diff --git a/lib/filterTokens.js b/lib/filterTokens.js
index 2f716ee8c..b365327e2 100644
--- a/lib/filterTokens.js
+++ b/lib/filterTokens.js
@@ -33,20 +33,21 @@ import isPlainObject from 'is-plain-obj';
function filterTokenObject(tokens, filter) {
// Use reduce to generate a new object with the unwanted tokens filtered
// out
- return Object.entries(tokens ?? []).reduce((acc, [key, value]) => {
- // If the value is not an object, we don't know what it is. We return it as-is.
- if (!isPlainObject(value)) {
+ return Object.entries(tokens ?? []).reduce((acc, [key, token]) => {
+ const tokenValue = token.$value ?? token.value;
+ // If the token is not an object, we don't know what it is. We return it as-is.
+ if (!isPlainObject(token)) {
return acc;
- // If the value has a `value` member we know it's a property, pass it to
+ // If the token has a `value` member we know it's a property, pass it to
// the filter function and either include it in the final `acc` object or
// exclude it (by returning the `acc` object without it added).
- } else if (typeof value.value !== 'undefined') {
- return filter(/** @type {Token} */ (value)) ? { ...acc, [key]: value } : acc;
+ } else if (typeof tokenValue !== 'undefined') {
+ return filter(/** @type {Token} */ (token)) ? { ...acc, [key]: token } : acc;
// If we got here we have an object that is not a property. We'll assume
// it's an object containing multiple tokens and recursively filter it
// using the `filterTokenObject` function.
} else {
- const filtered = filterTokenObject(value, filter);
+ const filtered = filterTokenObject(token, filter);
// If the filtered object is not empty then add it to the final `acc`
// object. If it is empty then every property inside of it was filtered
// out, then exclude it entirely from the final `acc` object.
diff --git a/lib/transform/object.js b/lib/transform/object.js
index 604d49246..7eac1f2b9 100644
--- a/lib/transform/object.js
+++ b/lib/transform/object.js
@@ -47,7 +47,7 @@ export default function transformObject(
transformedObj = {},
) {
for (const name in obj) {
- if (!obj.hasOwnProperty(name)) {
+ if (!Object.hasOwn(obj, name)) {
continue;
}
@@ -60,7 +60,7 @@ export default function transformObject(
// value: "#ababab"
// ...
// }
- if (isObj && 'value' in objProp) {
+ if (isObj && (Object.hasOwn(objProp, '$value') || Object.hasOwn(objProp, 'value'))) {
const pathName = getName(path);
const alreadyTransformed = transformedPropRefs.indexOf(pathName) !== -1;
@@ -74,26 +74,26 @@ export default function transformObject(
// Note: tokenSetup won't re-run if property has already been setup
// it is safe to run this multiple times on the same property.
- const setupProperty = tokenSetup(/** @type {Token|TransformedToken} */ (objProp), name, path);
+ const token = tokenSetup(/** @type {Token|TransformedToken} */ (objProp), name, path);
const deferProp = () => {
// If property path isn't in the deferred array, add it now.
if (deferredPropValueTransforms.indexOf(pathName) === -1) {
deferredPropValueTransforms.push(pathName);
}
- transformedObj[name] = setupProperty;
+ transformedObj[name] = token;
path.pop();
};
// If property has a reference, defer its transformations until later
- if (usesReferences(setupProperty.value, options)) {
+ if (usesReferences(token.$value ?? token.value, options)) {
deferProp();
continue;
}
// If we got here, the property hasn't been transformed yet and
// does not use a value reference. Transform the property now and assign it.
- const transformedToken = transformToken(setupProperty, options);
+ const transformedToken = transformToken(token, options);
// If a value transform returns undefined, it means the transform wants it to be deferred
// e.g. due to a ref existing in a sibling prop that the transform relies on.
// Example: { value: "#fff", darken: "{darken-amount}" }
diff --git a/lib/transform/token.js b/lib/transform/token.js
index 84e7405a7..f4697b0df 100644
--- a/lib/transform/token.js
+++ b/lib/transform/token.js
@@ -21,15 +21,15 @@ import usesReferences from '../utils/references/usesReferences.js';
*/
/**
- * Applies all transforms to a property. This is a pure function,
- * it returns a new property object rather than mutating it inline.
+ * Applies all transforms to a token. This is a pure function,
+ * it returns a new token object rather than mutating it inline.
* @private
- * @param {Token} property
+ * @param {Token} token
* @param {PlatformConfig} options
* @returns {Token|undefined} - A new property object with transforms applied.
*/
-export default function transformProperty(property, options) {
- const to_ret = structuredClone(property);
+export default function transformProperty(token, options) {
+ const to_ret = structuredClone(token);
const transforms = /** @type {Omit
[]} */ (options.transforms) || [];
@@ -45,15 +45,19 @@ export default function transformProperty(property, options) {
}
// Don't try to transform the value if it is referencing another value
// Only try to transform if the value is not a string or if it has '{}'
- if (transform.type === 'value' && !usesReferences(property.value, options)) {
+ if (transform.type === 'value' && !usesReferences(token.$value ?? token.value, options)) {
// Only transform non-referenced values (from original)
// and transitive transforms if the value has been resolved
- if (!usesReferences(property.original.value, options) || transform.transitive) {
+ if (!usesReferences(token.original.value, options) || transform.transitive) {
const transformedValue = transform.transformer(to_ret, options);
if (transformedValue === undefined) {
return undefined;
}
- to_ret.value = transformedValue;
+ if (token.$value !== undefined) {
+ to_ret.$value = transformedValue;
+ } else {
+ to_ret.value = transformedValue;
+ }
}
}
diff --git a/lib/utils/combineJSON.js b/lib/utils/combineJSON.js
index 538e7457d..6bc1364e2 100644
--- a/lib/utils/combineJSON.js
+++ b/lib/utils/combineJSON.js
@@ -108,7 +108,7 @@ export default async function combineJSON(
if (file_content) {
// Add some side data on each property to make filtering easier
traverseObj(file_content, (obj) => {
- if (obj.hasOwnProperty('value') && !obj.filePath) {
+ if ((Object.hasOwn(obj, '$value') || Object.hasOwn(obj, 'value')) && !obj.filePath) {
obj.filePath = filePath;
obj.isSource = source;
diff --git a/lib/utils/flattenTokens.js b/lib/utils/flattenTokens.js
index 344a36997..041e4108c 100644
--- a/lib/utils/flattenTokens.js
+++ b/lib/utils/flattenTokens.js
@@ -29,9 +29,12 @@ import isPlainObject from 'is-plain-obj';
*/
export default function flattenTokens(tokens, to_ret = []) {
for (let name in tokens) {
- if (tokens.hasOwnProperty(name)) {
+ if (Object.hasOwn(tokens, name)) {
// TODO: this is a bit fragile and arbitrary to stop when we get to a 'value' property.
- if (isPlainObject(tokens[name]) && 'value' in tokens[name]) {
+ if (
+ isPlainObject(tokens[name]) &&
+ (Object.hasOwn(tokens[name], '$value') || Object.hasOwn(tokens[name], 'value'))
+ ) {
to_ret.push(/** @type {Token} */ (tokens[name]));
} else if (isPlainObject(tokens[name])) {
flattenTokens(tokens[name], to_ret);
diff --git a/lib/utils/preprocess.js b/lib/utils/preprocess.js
index 5b4d2809f..869a04053 100644
--- a/lib/utils/preprocess.js
+++ b/lib/utils/preprocess.js
@@ -11,20 +11,56 @@
* and limitations under the License.
*/
+import isPlainObject from 'is-plain-obj';
+
/**
- * Run all registered preprocessors on the dictionary,
- * returning the preprocessed dictionary in each step.
- *
* @typedef {import('../../types/DesignToken.d.ts').DesignTokens} DesignTokens
* @typedef {import('../../types/Preprocessor.d.ts').Preprocessor} Preprocessor
* @typedef {import('../../types/Preprocessor.d.ts').preprocessor} preprocessor
+ */
+
+/**
+ * @param {DesignTokens} tokens
+ * @returns
+ */
+function typeW3CDelegate(tokens) {
+ const clone = structuredClone(tokens);
+
+ /**
+ * @param {DesignTokens} slice
+ * @param {string} [_type]
+ */
+ const recurse = (slice, _type) => {
+ let type = _type; // keep track of type through the stack
+ Object.values(slice).forEach((prop) => {
+ if (isPlainObject(prop)) {
+ if (typeof prop.$type === 'string') {
+ type = prop.$type;
+ }
+ // prop is a design token, but no $type prop currently,
+ // so we add it if we know what the type is from our ancestor tree
+ if ((prop.$value || prop.value) && !prop.$type && type) {
+ prop.$type = type;
+ }
+ recurse(prop, type);
+ }
+ });
+ };
+
+ recurse(clone);
+ return clone;
+}
+
+/**
+ * Run all registered preprocessors on the dictionary,
+ * returning the preprocessed dictionary in each step.
*
* @param {DesignTokens} tokens
* @param {Record} [preprocessorObj]
* @returns {Promise}
*/
export async function preprocess(tokens, preprocessorObj = {}) {
- let processedTokens = tokens;
+ let processedTokens = typeW3CDelegate(tokens);
const preprocessors = Object.values(preprocessorObj);
if (preprocessors.length > 0) {
diff --git a/lib/utils/references/getReferences.js b/lib/utils/references/getReferences.js
index 1fe61b367..80da9fe9e 100644
--- a/lib/utils/references/getReferences.js
+++ b/lib/utils/references/getReferences.js
@@ -70,7 +70,7 @@ export default function _getReferences(
*/
function findReference(match, variable) {
// remove 'value' to access the whole token object
- variable = variable.trim().replace('.value', '');
+ variable = variable.trim().replace('.value', '').replace('.$value', '');
// Find what the value is referencing
const pathName = getPathFromName(variable, opts.separator ?? defaults.separator);
@@ -98,7 +98,7 @@ export default function _getReferences(
if (typeof value === 'string') {
// function inside .replace runs multiple times if there are multiple matches
- // TODO: we don't need the replace's return value, considering using something else here
+ // TODO: we don't need the replace's return value, consider using something else here
value.replace(regex, findReference);
}
@@ -107,12 +107,14 @@ export default function _getReferences(
// function which iterates over the object to see if there is a reference
if (typeof value === 'object') {
for (const key in value) {
- if (value.hasOwnProperty(key) && typeof value[key] === 'string') {
- value[key].replace(regex, findReference);
- }
- // if it is an object, we go further down the rabbit hole
- if (value.hasOwnProperty(key) && typeof value[key] === 'object') {
- _getReferences(value[key], tokens, opts, references);
+ if (Object.hasOwn(value, key)) {
+ if (typeof value[key] === 'string') {
+ value[key].replace(regex, findReference);
+ }
+ // if it is an object, we go further down the rabbit hole
+ if (typeof value[key] === 'object') {
+ _getReferences(value[key], tokens, opts, references);
+ }
}
}
}
diff --git a/lib/utils/references/resolveReferences.js b/lib/utils/references/resolveReferences.js
index 7ad17c5db..e2ca7bbe8 100644
--- a/lib/utils/references/resolveReferences.js
+++ b/lib/utils/references/resolveReferences.js
@@ -87,11 +87,16 @@ export function _resolveReferences(
// Find what the value is referencing
const pathName = getPathFromName(variable, separator);
- const refHasValue = pathName[pathName.length - 1] === 'value';
+ const refHasValue = ['value', '$value'].includes(pathName[pathName.length - 1]);
+ // FIXME: shouldn't these two "refHasValue" conditions be reversed??
if (refHasValue && ignorePaths.indexOf(variable) !== -1) {
return '';
- } else if (!refHasValue && ignorePaths.indexOf(`${variable}.value`) !== -1) {
+ } else if (
+ !refHasValue &&
+ (ignorePaths.indexOf(`${variable}.value`) !== -1 ||
+ ignorePaths.indexOf(`${variable}.$value`) !== -1)
+ ) {
return '';
}
@@ -104,8 +109,8 @@ export function _resolveReferences(
// we should take the '.value' of the reference
// per the W3C draft spec where references do not have .value
// https://design-tokens.github.io/community-group/format/#aliases-references
- if (!refHasValue && ref && ref.hasOwnProperty('value')) {
- ref = ref.value;
+ if (!refHasValue && ref && (Object.hasOwn(ref, '$value') || Object.hasOwn(ref, 'value'))) {
+ ref = ref.$value ?? ref.value;
}
if (typeof ref !== 'undefined') {
@@ -117,7 +122,7 @@ export function _resolveReferences(
const reference = to_ret.slice(1, -1);
// Compare to found circular references
- if (foundCirc.hasOwnProperty(reference)) {
+ if (Object.hasOwn(foundCirc, reference)) {
// If the current reference is a member of a circular reference, do nothing
} else if (stack.indexOf(reference) !== -1) {
// If the current stack already contains the current reference, we found a new circular reference
diff --git a/lib/utils/references/usesReferences.js b/lib/utils/references/usesReferences.js
index a16942254..db0155827 100644
--- a/lib/utils/references/usesReferences.js
+++ b/lib/utils/references/usesReferences.js
@@ -34,7 +34,7 @@ export default function usesReferences(value, regexOrOptions = {}) {
// if any element passes the regex test,
// the whole thing should be true
for (const key in value) {
- if (value.hasOwnProperty(key)) {
+ if (Object.hasOwn(value, key)) {
const element = value[key];
let reference = usesReferences(element, regexOrOptions);
if (reference) {
diff --git a/lib/utils/resolveObject.js b/lib/utils/resolveObject.js
index c9c948df0..6041ad1d3 100644
--- a/lib/utils/resolveObject.js
+++ b/lib/utils/resolveObject.js
@@ -58,7 +58,7 @@ function traverseObj(slice, fullObj, opts, current_context, foundCirc) {
if (!Object.hasOwn(slice, key)) {
continue;
}
- const value = slice[key];
+ const prop = slice[key];
// We want to check for ignoredKeys, this is to
// skip over attributes that should not be
@@ -68,12 +68,11 @@ function traverseObj(slice, fullObj, opts, current_context, foundCirc) {
}
current_context.push(key);
- if (typeof value === 'object') {
- traverseObj(value, fullObj, opts, current_context, foundCirc);
- } else if (typeof value === 'string') {
- let val = /** @type {string} */ (value);
- if (val.indexOf('{') > -1) {
- const ref = _resolveReferences(val, fullObj, {
+ if (typeof prop === 'object') {
+ traverseObj(prop, fullObj, opts, current_context, foundCirc);
+ } else if (typeof prop === 'string') {
+ if (/** @type {string} */ (prop).indexOf('{') > -1) {
+ const ref = _resolveReferences(prop, fullObj, {
...opts,
current_context,
foundCirc,
diff --git a/package-lock.json b/package-lock.json
index 0497c0005..ef0a122c0 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -42,6 +42,7 @@
"docsify-cli": "^4.4.3",
"eslint": "^8.7.0",
"eslint-config-react-app": "^7.0.1",
+ "eslint-plugin-mocha": "^10.2.0",
"fs-extra": "^10.0.0",
"hanbi": "^1.0.1",
"husky": "^8.0.3",
@@ -8620,6 +8621,22 @@
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
"dev": true
},
+ "node_modules/eslint-plugin-mocha": {
+ "version": "10.2.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-mocha/-/eslint-plugin-mocha-10.2.0.tgz",
+ "integrity": "sha512-ZhdxzSZnd1P9LqDPF0DBcFLpRIGdh1zkF2JHnQklKQOvrQtT73kdP5K9V2mzvbLR+cCAO9OI48NXK/Ax9/ciCQ==",
+ "dev": true,
+ "dependencies": {
+ "eslint-utils": "^3.0.0",
+ "rambda": "^7.4.0"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "eslint": ">=7.0.0"
+ }
+ },
"node_modules/eslint-plugin-react": {
"version": "7.33.2",
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.33.2.tgz",
@@ -8729,6 +8746,33 @@
"node": ">=8.0.0"
}
},
+ "node_modules/eslint-utils": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz",
+ "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==",
+ "dev": true,
+ "dependencies": {
+ "eslint-visitor-keys": "^2.0.0"
+ },
+ "engines": {
+ "node": "^10.0.0 || ^12.0.0 || >= 14.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/mysticatea"
+ },
+ "peerDependencies": {
+ "eslint": ">=5"
+ }
+ },
+ "node_modules/eslint-utils/node_modules/eslint-visitor-keys": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
+ "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/eslint-visitor-keys": {
"version": "3.4.3",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
@@ -14112,6 +14156,12 @@
"node": ">= 12.0.0"
}
},
+ "node_modules/rambda": {
+ "version": "7.5.0",
+ "resolved": "https://registry.npmjs.org/rambda/-/rambda-7.5.0.tgz",
+ "integrity": "sha512-y/M9weqWAH4iopRd7EHDEQQvpFPHj1AA3oHozE9tfITHUtTR7Z9PSlIRRG2l1GuW7sefC1cXFfIcF+cgnShdBA==",
+ "dev": true
+ },
"node_modules/randombytes": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
diff --git a/package.json b/package.json
index a9b965312..74ac753e4 100644
--- a/package.json
+++ b/package.json
@@ -101,13 +101,13 @@
"@bundled-es-modules/deepmerge": "^4.3.1",
"@bundled-es-modules/glob": "^10.3.13",
"@bundled-es-modules/memfs": "^4.2.3",
- "path-unified": "^0.1.0",
"chalk": "^5.3.0",
"change-case": "^5.3.0",
"commander": "^8.3.0",
"is-plain-obj": "^4.1.0",
"json5": "^2.2.2",
"lodash-es": "^4.17.21",
+ "path-unified": "^0.1.0",
"tinycolor2": "^1.6.0"
},
"devDependencies": {
@@ -127,6 +127,7 @@
"docsify-cli": "^4.4.3",
"eslint": "^8.7.0",
"eslint-config-react-app": "^7.0.1",
+ "eslint-plugin-mocha": "^10.2.0",
"fs-extra": "^10.0.0",
"hanbi": "^1.0.1",
"husky": "^8.0.3",
diff --git a/types/DesignToken.d.ts b/types/DesignToken.d.ts
index 25b70cda3..4e5dd18b2 100644
--- a/types/DesignToken.d.ts
+++ b/types/DesignToken.d.ts
@@ -16,7 +16,11 @@
* Make sure to also change it there when this type changes!
*/
export interface DesignToken {
- value: any;
+ value?: any;
+ $value?: any;
+ type?: string;
+ $type?: string;
+ $description?: string;
name?: string;
comment?: string;
themeable?: boolean;