@stratixlabs/core 1.7.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/LICENSE +21 -0
- package/README.md +249 -0
- package/bin/substrata.js +521 -0
- package/index.js +3 -0
- package/package.json +49 -0
- package/scripts/detect-breaking-changes.js +155 -0
- package/scripts/figma-sync.js +738 -0
- package/scripts/generate-dts-from-tokens.js +77 -0
- package/scripts/generate-tokens.js +138 -0
- package/scripts/lint-code.js +186 -0
- package/scripts/lint-hardcoded.js +49 -0
- package/scripts/tokens-to-css.js +145 -0
- package/scripts/validate-tokens.js +113 -0
- package/src/base.css +41 -0
- package/src/components/components.css +279 -0
- package/src/consumption/plain.css +22 -0
- package/src/consumption/sass-example.scss +30 -0
- package/src/consumption/styled-components.js +28 -0
- package/src/consumption/tailwind.config.js +44 -0
- package/src/consumption/vanilla-extract.css.ts +28 -0
- package/src/substrata.css +11 -0
- package/src/tokens/breakpoints.css +7 -0
- package/src/tokens/colors.css +21 -0
- package/src/tokens/elevation.css +7 -0
- package/src/tokens/motion.css +7 -0
- package/src/tokens/opacity.css +7 -0
- package/src/tokens/radius-and-borders.css +9 -0
- package/src/tokens/semantic-aliases.css +12 -0
- package/src/tokens/spacing.css +10 -0
- package/src/tokens/typography.css +18 -0
- package/substrata.config.js +4 -0
- package/substrata.d.ts +123 -0
- package/tokens.json +348 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const { execSync } = require('child_process');
|
|
4
|
+
|
|
5
|
+
function isLeaf(node) {
|
|
6
|
+
if (!node || typeof node !== 'object' || Array.isArray(node)) return false;
|
|
7
|
+
const keys = Object.keys(node);
|
|
8
|
+
return keys.includes('value') && keys.includes('type') && keys.includes('originalVariable');
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function flattenTokens(obj, prefix, map) {
|
|
12
|
+
if (isLeaf(obj)) {
|
|
13
|
+
map[prefix] = {
|
|
14
|
+
value: obj.value,
|
|
15
|
+
type: obj.type,
|
|
16
|
+
originalVariable: obj.originalVariable,
|
|
17
|
+
};
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
if (!obj || typeof obj !== 'object') return;
|
|
21
|
+
for (const key of Object.keys(obj)) {
|
|
22
|
+
const nextPrefix = prefix ? prefix + '.' + key : key;
|
|
23
|
+
flattenTokens(obj[key], nextPrefix, map);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function loadCurrentTokens() {
|
|
28
|
+
const cwd = process.cwd();
|
|
29
|
+
const tokensPath = path.join(cwd, 'tokens.json');
|
|
30
|
+
if (!fs.existsSync(tokensPath)) {
|
|
31
|
+
console.error('❌ tokens.json not found. Run "npm run build:tokens" first.');
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
const content = fs.readFileSync(tokensPath, 'utf8');
|
|
35
|
+
return JSON.parse(content);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function loadBaseTokens() {
|
|
39
|
+
try {
|
|
40
|
+
const baseRefEnv = process.env.TOKENS_BASE_REF || '';
|
|
41
|
+
const baseRef =
|
|
42
|
+
baseRefEnv.trim().length > 0
|
|
43
|
+
? baseRefEnv.trim()
|
|
44
|
+
: 'origin/main';
|
|
45
|
+
|
|
46
|
+
execSync('git fetch origin --quiet', { stdio: 'ignore' });
|
|
47
|
+
|
|
48
|
+
let baseCommit = baseRef;
|
|
49
|
+
try {
|
|
50
|
+
const mergeBase = execSync(`git merge-base HEAD ${baseRef}`, { encoding: 'utf8' })
|
|
51
|
+
.toString()
|
|
52
|
+
.trim();
|
|
53
|
+
if (mergeBase) {
|
|
54
|
+
baseCommit = mergeBase;
|
|
55
|
+
}
|
|
56
|
+
} catch (error) {}
|
|
57
|
+
|
|
58
|
+
const output = execSync(`git show ${baseCommit}:tokens.json`, { encoding: 'utf8' });
|
|
59
|
+
return JSON.parse(output);
|
|
60
|
+
} catch (error) {
|
|
61
|
+
console.log('ℹ️ No base tokens.json found for breaking change detection; skipping.');
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function main() {
|
|
67
|
+
const currentTokens = loadCurrentTokens();
|
|
68
|
+
const baseTokens = loadBaseTokens();
|
|
69
|
+
|
|
70
|
+
if (!baseTokens) {
|
|
71
|
+
console.log('ℹ️ Skipped breaking-change detection (no base tokens.json).');
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const currentMap = {};
|
|
76
|
+
const baseMap = {};
|
|
77
|
+
|
|
78
|
+
flattenTokens(currentTokens, '', currentMap);
|
|
79
|
+
flattenTokens(baseTokens, '', baseMap);
|
|
80
|
+
|
|
81
|
+
const removed = [];
|
|
82
|
+
const added = [];
|
|
83
|
+
const typeChanged = [];
|
|
84
|
+
const valueChanged = [];
|
|
85
|
+
|
|
86
|
+
for (const key of Object.keys(baseMap)) {
|
|
87
|
+
if (!Object.prototype.hasOwnProperty.call(currentMap, key)) {
|
|
88
|
+
removed.push({ path: key, from: baseMap[key] });
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
for (const key of Object.keys(currentMap)) {
|
|
93
|
+
if (!Object.prototype.hasOwnProperty.call(baseMap, key)) {
|
|
94
|
+
added.push({ path: key, to: currentMap[key] });
|
|
95
|
+
} else {
|
|
96
|
+
const before = baseMap[key];
|
|
97
|
+
const after = currentMap[key];
|
|
98
|
+
if (before.type !== after.type) {
|
|
99
|
+
typeChanged.push({ path: key, from: before.type, to: after.type });
|
|
100
|
+
} else if (before.value !== after.value) {
|
|
101
|
+
valueChanged.push({ path: key, from: before.value, to: after.value, type: after.type });
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (removed.length === 0 && typeChanged.length === 0 && valueChanged.length === 0) {
|
|
107
|
+
console.log('✅ No token changes detected compared to base tokens.json.');
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
console.log('🔎 Token changes compared to base tokens.json:');
|
|
112
|
+
|
|
113
|
+
if (removed.length > 0) {
|
|
114
|
+
console.log('');
|
|
115
|
+
console.log(`Removed tokens (${removed.length}):`);
|
|
116
|
+
for (const item of removed) {
|
|
117
|
+
console.log(` - ${item.path} [${item.from.type}] (value: ${item.from.value})`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (typeChanged.length > 0) {
|
|
122
|
+
console.log('');
|
|
123
|
+
console.log(`Tokens with type changes (${typeChanged.length}):`);
|
|
124
|
+
for (const item of typeChanged) {
|
|
125
|
+
console.log(` - ${item.path}: ${item.from} → ${item.to}`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (valueChanged.length > 0) {
|
|
130
|
+
console.log('');
|
|
131
|
+
console.log(`Tokens with value changes (${valueChanged.length}):`);
|
|
132
|
+
for (const item of valueChanged) {
|
|
133
|
+
console.log(` - ${item.path} [${item.type}]: ${item.from} → ${item.to}`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const failOnValueChanges = process.env.TOKENS_FAIL_ON_VALUE_CHANGES === '1';
|
|
138
|
+
|
|
139
|
+
if (removed.length > 0 || typeChanged.length > 0 || (failOnValueChanges && valueChanged.length > 0)) {
|
|
140
|
+
console.error('');
|
|
141
|
+
console.error('❌ Breaking token changes detected.');
|
|
142
|
+
console.error(' Removed tokens and type changes require explicit review and a suitable release plan.');
|
|
143
|
+
if (failOnValueChanges && valueChanged.length > 0) {
|
|
144
|
+
console.error(' Value changes are configured to be treated as breaking by TOKENS_FAIL_ON_VALUE_CHANGES=1.');
|
|
145
|
+
}
|
|
146
|
+
process.exit(1);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
console.log('');
|
|
150
|
+
console.log('⚠️ No structural breaking changes detected, but some values changed.');
|
|
151
|
+
console.log(' Review the value changes above and rely on visual regression tests when needed.');
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
main();
|
|
155
|
+
|