@dialpad/dialtone-css 8.78.0-next.1 → 8.78.0-next.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.
- package/README.md +0 -2
- package/lib/build/js/dialtone_migrate_flex_to_stack/examples-edge-cases.vue +26 -0
- package/lib/build/js/dialtone_migrate_flex_to_stack/index.mjs +56 -15
- package/lib/build/js/dialtone_migrate_tshirt_to_numeric/index.mjs +233 -0
- package/lib/build/js/dialtone_migrate_tshirt_to_numeric/test.mjs +336 -0
- package/lib/build/js/dialtone_migration_helper/configs/physical-to-logical.mjs +86 -0
- package/lib/build/js/dialtone_migration_helper/configs/size-to-layout.mjs +212 -0
- package/lib/build/js/dialtone_migration_helper/configs/space-to-spacing.mjs +48 -0
- package/lib/build/js/dialtone_migration_helper/configs/stack-gap-to-spacing.mjs +88 -0
- package/lib/build/js/dialtone_migration_helper/configs/utility-class-to-token-stops.mjs +135 -0
- package/lib/build/js/dialtone_migration_helper/tests/size-to-layout-test-examples.vue +275 -0
- package/lib/build/js/dialtone_migration_helper/tests/space-to-spacing-test-examples.vue +193 -0
- package/lib/build/less/components/avatar.less +8 -19
- package/lib/build/less/components/badge.less +22 -20
- package/lib/build/less/components/banner.less +5 -5
- package/lib/build/less/components/breadcrumbs.less +4 -4
- package/lib/build/less/components/button.less +39 -39
- package/lib/build/less/components/card.less +4 -4
- package/lib/build/less/components/chip.less +41 -51
- package/lib/build/less/components/codeblock.less +2 -2
- package/lib/build/less/components/collapsible.less +2 -2
- package/lib/build/less/components/combobox-multi-select.less +8 -8
- package/lib/build/less/components/combobox-with-popover.less +1 -1
- package/lib/build/less/components/combobox.less +5 -5
- package/lib/build/less/components/datepicker.less +6 -6
- package/lib/build/less/components/description-list.less +14 -3
- package/lib/build/less/components/dropdown.less +4 -4
- package/lib/build/less/components/emoji-picker.less +35 -35
- package/lib/build/less/components/empty-state.less +5 -5
- package/lib/build/less/components/filter-pill.less +5 -5
- package/lib/build/less/components/forms.less +10 -10
- package/lib/build/less/components/image-viewer.less +2 -2
- package/lib/build/less/components/input.less +17 -22
- package/lib/build/less/components/item-layout.less +8 -8
- package/lib/build/less/components/keyboard-shortcut.less +3 -3
- package/lib/build/less/components/link.less +5 -5
- package/lib/build/less/components/list-group.less +1 -1
- package/lib/build/less/components/list-item-group.less +1 -1
- package/lib/build/less/components/list-item.less +9 -9
- package/lib/build/less/components/modal.less +20 -20
- package/lib/build/less/components/notice.less +11 -11
- package/lib/build/less/components/pagination.less +5 -5
- package/lib/build/less/components/popover.less +5 -5
- package/lib/build/less/components/radio-checkbox.less +11 -10
- package/lib/build/less/components/rich-text-editor.less +13 -13
- package/lib/build/less/components/scrollbar.less +2 -2
- package/lib/build/less/components/segmented-control.less +6 -6
- package/lib/build/less/components/selects.less +18 -13
- package/lib/build/less/components/skeleton.less +4 -4
- package/lib/build/less/components/stack.less +24 -69
- package/lib/build/less/components/table.less +6 -7
- package/lib/build/less/components/tabs.less +24 -24
- package/lib/build/less/components/toast.less +16 -16
- package/lib/build/less/components/toggle.less +23 -23
- package/lib/build/less/components/tooltip.less +27 -27
- package/lib/build/less/dialtone-reset.less +3 -3
- package/lib/build/less/dialtone-transitions.less +4 -4
- package/lib/build/less/dialtone.less +2 -2
- package/lib/build/less/recipes/attachment_carousel.less +13 -13
- package/lib/build/less/recipes/callbar_button.less +1 -1
- package/lib/build/less/recipes/callbar_button_with_dropdown.less +7 -7
- package/lib/build/less/recipes/callbar_button_with_popover.less +8 -8
- package/lib/build/less/recipes/callbox.less +6 -6
- package/lib/build/less/recipes/contact_info.less +9 -9
- package/lib/build/less/recipes/editor.less +12 -12
- package/lib/build/less/recipes/emoji_row.less +8 -8
- package/lib/build/less/recipes/feed_item_pill.less +13 -13
- package/lib/build/less/recipes/feed_item_row.less +10 -10
- package/lib/build/less/recipes/grouped_chip.less +2 -2
- package/lib/build/less/recipes/ivr_node.less +13 -13
- package/lib/build/less/recipes/leftbar_row.less +23 -23
- package/lib/build/less/recipes/message_input.less +16 -16
- package/lib/build/less/recipes/settings_menu_button.less +10 -10
- package/lib/build/less/recipes/time_pill.less +1 -1
- package/lib/build/less/recipes/top_banner_info.less +8 -8
- package/lib/build/less/recipes/unread_pill.less +2 -2
- package/lib/build/less/themes/default.less +1 -1
- package/lib/build/less/utilities/backgrounds.less +3 -3
- package/lib/build/less/utilities/effects.less +20 -20
- package/lib/build/less/utilities/flex.less +11 -11
- package/lib/build/less/utilities/layout.less +4 -4
- package/lib/build/less/utilities/sizing.less +172 -0
- package/lib/build/less/utilities/spacing.less +49 -49
- package/lib/build/less/utilities/typography.less +2 -2
- package/lib/build/less/variables/sizes.less +8 -8
- package/lib/dist/dialtone-default-theme.css +5220 -1117
- package/lib/dist/dialtone-default-theme.min.css +1 -1
- package/lib/dist/dialtone-docs.json +1 -1
- package/lib/dist/dialtone.css +5203 -1117
- package/lib/dist/dialtone.min.css +1 -1
- package/lib/dist/js/dialtone_migrate_flex_to_stack/examples-edge-cases.vue +26 -0
- package/lib/dist/js/dialtone_migrate_flex_to_stack/index.mjs +56 -15
- package/lib/dist/js/dialtone_migrate_tshirt_to_numeric/index.mjs +233 -0
- package/lib/dist/js/dialtone_migrate_tshirt_to_numeric/test.mjs +336 -0
- package/lib/dist/js/dialtone_migration_helper/configs/physical-to-logical.mjs +86 -0
- package/lib/dist/js/dialtone_migration_helper/configs/size-to-layout.mjs +212 -0
- package/lib/dist/js/dialtone_migration_helper/configs/space-to-spacing.mjs +48 -0
- package/lib/dist/js/dialtone_migration_helper/configs/stack-gap-to-spacing.mjs +88 -0
- package/lib/dist/js/dialtone_migration_helper/configs/utility-class-to-token-stops.mjs +135 -0
- package/lib/dist/js/dialtone_migration_helper/tests/size-to-layout-test-examples.vue +275 -0
- package/lib/dist/js/dialtone_migration_helper/tests/space-to-spacing-test-examples.vue +193 -0
- package/lib/dist/tokens/tokens-base-dark.css +17 -0
- package/lib/dist/tokens/tokens-base-light.css +17 -0
- package/lib/dist/tokens/tokens-debug-base.css +17 -0
- package/lib/dist/tokens-docs.json +1 -1
- package/package.json +3 -3
- package/lib/build/js/dialtone_migration_helper/configs/space-to-size.mjs +0 -15
- package/lib/dist/js/dialtone_migration_helper/configs/space-to-size.mjs +0 -15
package/README.md
CHANGED
|
@@ -292,6 +292,32 @@
|
|
|
292
292
|
|
|
293
293
|
<!-- TEST 47: Empty element with flex - Should convert -->
|
|
294
294
|
<div class="d-d-flex d-fd-column"></div>
|
|
295
|
+
|
|
296
|
+
<!-- ============================================ -->
|
|
297
|
+
<!-- NEW TESTS: NEW-FORMAT GAP UTILITIES (d-g-*) -->
|
|
298
|
+
<!-- ============================================ -->
|
|
299
|
+
|
|
300
|
+
<!-- TEST 48: New-format gap d-g-100 (8px) - Should convert to gap="100" -->
|
|
301
|
+
<div class="d-d-flex d-ai-center d-g-100">
|
|
302
|
+
<span>New gap format 8px</span>
|
|
303
|
+
</div>
|
|
304
|
+
|
|
305
|
+
<!-- TEST 49: New-format gap d-g-200 (16px) - Should convert to gap="200" -->
|
|
306
|
+
<div class="d-d-flex d-fd-row d-g-200">
|
|
307
|
+
<span>Item 1</span>
|
|
308
|
+
<span>Item 2</span>
|
|
309
|
+
</div>
|
|
310
|
+
|
|
311
|
+
<!-- TEST 50: New-format gap d-g-400 (32px) with other props - Should convert -->
|
|
312
|
+
<div class="d-d-flex d-fd-column d-ai-center d-jc-between d-g-400">
|
|
313
|
+
<span>Full props with new gap</span>
|
|
314
|
+
<span>Example</span>
|
|
315
|
+
</div>
|
|
316
|
+
|
|
317
|
+
<!-- TEST 51: New-format gap d-g-50 (4px) - Should convert to gap="50" -->
|
|
318
|
+
<div class="d-d-flex d-ai-center d-g-50">
|
|
319
|
+
<span>Small gap 4px</span>
|
|
320
|
+
</div>
|
|
295
321
|
</template>
|
|
296
322
|
|
|
297
323
|
<script setup>
|
|
@@ -142,23 +142,64 @@ const FLEX_TO_PROP = {
|
|
|
142
142
|
'd-fd-row-reverse': { prop: 'direction', value: 'row-reverse' },
|
|
143
143
|
'd-fd-column-reverse': { prop: 'direction', value: 'column-reverse' },
|
|
144
144
|
|
|
145
|
-
// Gap mappings (d-g* → gap prop)
|
|
145
|
+
// Gap mappings (d-g* → gap prop) — old pixel-based utilities
|
|
146
146
|
'd-g0': { prop: 'gap', value: '0' },
|
|
147
|
-
'd-
|
|
148
|
-
'd-
|
|
149
|
-
'd-
|
|
150
|
-
'd-
|
|
151
|
-
'd-
|
|
152
|
-
'd-
|
|
153
|
-
|
|
154
|
-
|
|
147
|
+
'd-g1': { prop: 'gap', value: '1' },
|
|
148
|
+
'd-g2': { prop: 'gap', value: '25' },
|
|
149
|
+
'd-g4': { prop: 'gap', value: '50' },
|
|
150
|
+
'd-g6': { prop: 'gap', value: '75' },
|
|
151
|
+
'd-g8': { prop: 'gap', value: '100' },
|
|
152
|
+
'd-g10': { prop: 'gap', value: '125' },
|
|
153
|
+
'd-g12': { prop: 'gap', value: '150' },
|
|
154
|
+
'd-g14': { prop: 'gap', value: '175' },
|
|
155
|
+
'd-g16': { prop: 'gap', value: '200' },
|
|
156
|
+
'd-g20': { prop: 'gap', value: '250' },
|
|
157
|
+
'd-g24': { prop: 'gap', value: '300' },
|
|
158
|
+
'd-g32': { prop: 'gap', value: '400' },
|
|
159
|
+
'd-g48': { prop: 'gap', value: '600' },
|
|
160
|
+
'd-g64': { prop: 'gap', value: '800' },
|
|
161
|
+
|
|
162
|
+
// Gap mappings (d-g-* → gap prop) — new token-stop-based utilities
|
|
163
|
+
'd-g-0': { prop: 'gap', value: '0' },
|
|
164
|
+
'd-g-1': { prop: 'gap', value: '1' },
|
|
165
|
+
'd-g-25': { prop: 'gap', value: '25' },
|
|
166
|
+
'd-g-50': { prop: 'gap', value: '50' },
|
|
167
|
+
'd-g-75': { prop: 'gap', value: '75' },
|
|
168
|
+
'd-g-100': { prop: 'gap', value: '100' },
|
|
169
|
+
'd-g-125': { prop: 'gap', value: '125' },
|
|
170
|
+
'd-g-150': { prop: 'gap', value: '150' },
|
|
171
|
+
'd-g-175': { prop: 'gap', value: '175' },
|
|
172
|
+
'd-g-200': { prop: 'gap', value: '200' },
|
|
173
|
+
'd-g-250': { prop: 'gap', value: '250' },
|
|
174
|
+
'd-g-300': { prop: 'gap', value: '300' },
|
|
175
|
+
'd-g-350': { prop: 'gap', value: '350' },
|
|
176
|
+
'd-g-400': { prop: 'gap', value: '400' },
|
|
177
|
+
'd-g-450': { prop: 'gap', value: '450' },
|
|
178
|
+
'd-g-500': { prop: 'gap', value: '500' },
|
|
179
|
+
'd-g-525': { prop: 'gap', value: '525' },
|
|
180
|
+
'd-g-550': { prop: 'gap', value: '550' },
|
|
181
|
+
'd-g-600': { prop: 'gap', value: '600' },
|
|
182
|
+
'd-g-650': { prop: 'gap', value: '650' },
|
|
183
|
+
'd-g-700': { prop: 'gap', value: '700' },
|
|
184
|
+
'd-g-750': { prop: 'gap', value: '750' },
|
|
185
|
+
'd-g-800': { prop: 'gap', value: '800' },
|
|
186
|
+
|
|
187
|
+
// Grid-gap mappings (d-gg* → gap prop) — deprecated utilities
|
|
155
188
|
'd-gg0': { prop: 'gap', value: '0' },
|
|
156
|
-
'd-
|
|
157
|
-
'd-
|
|
158
|
-
'd-
|
|
159
|
-
'd-
|
|
160
|
-
'd-
|
|
161
|
-
'd-
|
|
189
|
+
'd-gg1': { prop: 'gap', value: '1' },
|
|
190
|
+
'd-gg2': { prop: 'gap', value: '25' },
|
|
191
|
+
'd-gg4': { prop: 'gap', value: '50' },
|
|
192
|
+
'd-gg6': { prop: 'gap', value: '75' },
|
|
193
|
+
'd-gg8': { prop: 'gap', value: '100' },
|
|
194
|
+
'd-gg10': { prop: 'gap', value: '125' },
|
|
195
|
+
'd-gg12': { prop: 'gap', value: '150' },
|
|
196
|
+
'd-gg14': { prop: 'gap', value: '175' },
|
|
197
|
+
'd-gg16': { prop: 'gap', value: '200' },
|
|
198
|
+
'd-gg20': { prop: 'gap', value: '250' },
|
|
199
|
+
'd-gg24': { prop: 'gap', value: '300' },
|
|
200
|
+
'd-gg32': { prop: 'gap', value: '400' },
|
|
201
|
+
'd-gg48': { prop: 'gap', value: '600' },
|
|
202
|
+
'd-gg64': { prop: 'gap', value: '800' },
|
|
162
203
|
};
|
|
163
204
|
|
|
164
205
|
// Classes to remove (redundant on dt-stack)
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @fileoverview Migration script to convert t-shirt size props to numeric scale on Dialtone components.
|
|
5
|
+
*
|
|
6
|
+
* Transforms: size="sm" → :size="200", label-size="xs" → :label-size="100", speed="md" → :speed="300"
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* npx dialtone-migrate-tshirt-to-numeric [options]
|
|
10
|
+
*
|
|
11
|
+
* Options:
|
|
12
|
+
* --cwd <path> Working directory (default: current directory)
|
|
13
|
+
* --dry-run Show changes without applying them
|
|
14
|
+
* --yes Apply all changes without prompting
|
|
15
|
+
* --help Show help
|
|
16
|
+
*
|
|
17
|
+
* Examples:
|
|
18
|
+
* npx dialtone-migrate-tshirt-to-numeric
|
|
19
|
+
* npx dialtone-migrate-tshirt-to-numeric --dry-run
|
|
20
|
+
* npx dialtone-migrate-tshirt-to-numeric --cwd ./src
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import fs from 'fs/promises';
|
|
24
|
+
import { realpathSync } from 'node:fs';
|
|
25
|
+
import path from 'path';
|
|
26
|
+
import readline from 'readline';
|
|
27
|
+
import { fileURLToPath } from 'node:url';
|
|
28
|
+
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
// Mapping
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
|
|
33
|
+
const SIZE_MAP = {
|
|
34
|
+
xs: '100',
|
|
35
|
+
sm: '200',
|
|
36
|
+
md: '300',
|
|
37
|
+
lg: '400',
|
|
38
|
+
xl: '500',
|
|
39
|
+
'2xl': '600',
|
|
40
|
+
'3xl': '700',
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const TSHIRT_VALUES = Object.keys(SIZE_MAP).join('|');
|
|
44
|
+
|
|
45
|
+
// Match any prop ending in size/Size/speed/Speed with a t-shirt value.
|
|
46
|
+
// In the replacer, check that the character before the match is NOT a colon (v-bind).
|
|
47
|
+
const PROP_REGEX = new RegExp(
|
|
48
|
+
`([\\w-]*(?:[Ss]ize|[Ss]peed))="(${TSHIRT_VALUES})"`,
|
|
49
|
+
'g',
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
// Props that end in "size" but are NOT component scale sizes — exclude from migration
|
|
53
|
+
const EXCLUDED_PROPS = ['button-width-size', 'buttonWidthSize', 'background-size', 'backgroundSize', 'font-size', 'fontSize'];
|
|
54
|
+
|
|
55
|
+
// Only match on Dialtone component tags
|
|
56
|
+
// Use [\s\S] instead of [^>] to match across newlines in multiline tags
|
|
57
|
+
const DT_TAG_PATTERN = /<(dt-[\w-]+|Dt\w+)\b[\s\S]*?>/g;
|
|
58
|
+
|
|
59
|
+
// ---------------------------------------------------------------------------
|
|
60
|
+
// File finder
|
|
61
|
+
// ---------------------------------------------------------------------------
|
|
62
|
+
|
|
63
|
+
async function findFiles (dir, extensions, ignore = []) {
|
|
64
|
+
const results = [];
|
|
65
|
+
|
|
66
|
+
async function walk (currentDir) {
|
|
67
|
+
try {
|
|
68
|
+
const entries = await fs.readdir(currentDir, { withFileTypes: true });
|
|
69
|
+
for (const entry of entries) {
|
|
70
|
+
const fullPath = path.join(currentDir, entry.name);
|
|
71
|
+
if (ignore.some(ig => fullPath.includes(ig))) continue;
|
|
72
|
+
if (entry.isDirectory()) {
|
|
73
|
+
await walk(fullPath);
|
|
74
|
+
} else if (entry.isFile()) {
|
|
75
|
+
if (extensions.some(ext => entry.name.endsWith(ext))) {
|
|
76
|
+
results.push(fullPath);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
} catch {
|
|
81
|
+
// Skip unreadable directories
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
await walk(dir);
|
|
86
|
+
return results;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// ---------------------------------------------------------------------------
|
|
90
|
+
// Transform logic
|
|
91
|
+
// ---------------------------------------------------------------------------
|
|
92
|
+
|
|
93
|
+
function transformContent (content) {
|
|
94
|
+
let transformed = content;
|
|
95
|
+
let count = 0;
|
|
96
|
+
|
|
97
|
+
// Replace t-shirt sizes only within Dialtone component tags
|
|
98
|
+
transformed = transformed.replace(DT_TAG_PATTERN, (tag) => {
|
|
99
|
+
PROP_REGEX.lastIndex = 0;
|
|
100
|
+
return tag.replace(PROP_REGEX, (match, propName, tshirt, offset, fullTag) => {
|
|
101
|
+
// Skip if preceded by ':' (already a v-bind expression)
|
|
102
|
+
if (offset > 0 && fullTag[offset - 1] === ':') return match;
|
|
103
|
+
// Skip excluded prop names (not component scale sizes)
|
|
104
|
+
if (EXCLUDED_PROPS.includes(propName)) return match;
|
|
105
|
+
if (SIZE_MAP[tshirt]) {
|
|
106
|
+
count++;
|
|
107
|
+
return `:${propName}="${SIZE_MAP[tshirt]}"`;
|
|
108
|
+
}
|
|
109
|
+
return match;
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
return { transformed, count };
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export { transformContent, SIZE_MAP };
|
|
117
|
+
|
|
118
|
+
// ---------------------------------------------------------------------------
|
|
119
|
+
// CLI
|
|
120
|
+
// ---------------------------------------------------------------------------
|
|
121
|
+
|
|
122
|
+
function printHelp () {
|
|
123
|
+
console.log(`
|
|
124
|
+
Usage: npx dialtone-migrate-tshirt-to-numeric [options]
|
|
125
|
+
|
|
126
|
+
Converts t-shirt size props to numeric scale on Dialtone components.
|
|
127
|
+
|
|
128
|
+
size="sm" → :size="200"
|
|
129
|
+
label-size="xs" → :label-size="100"
|
|
130
|
+
speed="md" → :speed="300"
|
|
131
|
+
|
|
132
|
+
Options:
|
|
133
|
+
--cwd <path> Working directory (default: current directory)
|
|
134
|
+
--dry-run Show changes without applying them
|
|
135
|
+
--yes Apply all changes without prompting
|
|
136
|
+
--help Show help
|
|
137
|
+
|
|
138
|
+
Size mapping:
|
|
139
|
+
xs → 100 sm → 200 md → 300 lg → 400 xl → 500
|
|
140
|
+
2xl → 600 3xl → 700
|
|
141
|
+
`);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async function prompt (question) {
|
|
145
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
146
|
+
return new Promise(resolve => {
|
|
147
|
+
rl.question(question, answer => {
|
|
148
|
+
rl.close();
|
|
149
|
+
resolve(answer.trim().toLowerCase());
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// eslint-disable-next-line complexity
|
|
155
|
+
async function main () {
|
|
156
|
+
const args = process.argv.slice(2);
|
|
157
|
+
|
|
158
|
+
if (args.includes('--help')) {
|
|
159
|
+
printHelp();
|
|
160
|
+
process.exit(0);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const dryRun = args.includes('--dry-run');
|
|
164
|
+
const autoYes = args.includes('--yes');
|
|
165
|
+
const cwdIndex = args.indexOf('--cwd');
|
|
166
|
+
const cwd = cwdIndex !== -1 && args[cwdIndex + 1]
|
|
167
|
+
? path.resolve(args[cwdIndex + 1])
|
|
168
|
+
: process.cwd();
|
|
169
|
+
|
|
170
|
+
console.log(`\nScanning ${cwd} for t-shirt size usage on Dialtone components...\n`);
|
|
171
|
+
|
|
172
|
+
const extensions = ['.vue', '.md', '.html', '.js', '.ts', '.jsx', '.tsx'];
|
|
173
|
+
const ignore = ['node_modules', 'dist', '.git', '.vuepress/public'];
|
|
174
|
+
const files = await findFiles(cwd, extensions, ignore);
|
|
175
|
+
|
|
176
|
+
const changes = [];
|
|
177
|
+
|
|
178
|
+
for (const file of files) {
|
|
179
|
+
const content = await fs.readFile(file, 'utf8');
|
|
180
|
+
const { transformed, count } = transformContent(content);
|
|
181
|
+
if (count > 0) {
|
|
182
|
+
changes.push({ file, content, transformed, count });
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (changes.length === 0) {
|
|
187
|
+
console.log('No t-shirt size usage found. Nothing to migrate.');
|
|
188
|
+
process.exit(0);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
console.log(`Found ${changes.reduce((sum, c) => sum + c.count, 0)} t-shirt size references across ${changes.length} files:\n`);
|
|
192
|
+
|
|
193
|
+
for (const { file, count } of changes) {
|
|
194
|
+
const rel = path.relative(cwd, file);
|
|
195
|
+
console.log(` ${rel} (${count} change${count > 1 ? 's' : ''})`);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (dryRun) {
|
|
199
|
+
console.log('\n--dry-run: No files were modified.\n');
|
|
200
|
+
process.exit(0);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (!autoYes) {
|
|
204
|
+
const answer = await prompt('\nApply changes? (y/N) ');
|
|
205
|
+
if (answer !== 'y' && answer !== 'yes') {
|
|
206
|
+
console.log('Cancelled.');
|
|
207
|
+
process.exit(0);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
for (const { file, transformed } of changes) {
|
|
212
|
+
await fs.writeFile(file, transformed, 'utf8');
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
console.log(`\nMigrated ${changes.reduce((sum, c) => sum + c.count, 0)} references across ${changes.length} files.\n`);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Only run CLI when executed directly (not when imported for testing).
|
|
219
|
+
// Uses realpathSync to resolve symlinks from npx/npm bin shims.
|
|
220
|
+
const isDirectRun = (() => {
|
|
221
|
+
try {
|
|
222
|
+
return realpathSync(process.argv[1]) === fileURLToPath(import.meta.url);
|
|
223
|
+
} catch {
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
})();
|
|
227
|
+
|
|
228
|
+
if (isDirectRun) {
|
|
229
|
+
main().catch(err => {
|
|
230
|
+
console.error(err);
|
|
231
|
+
process.exit(1);
|
|
232
|
+
});
|
|
233
|
+
}
|