@starodubenko/fsd-gen 1.3.0-0 → 1.4.1-0
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 +5 -0
- package/dist/lib/reverse/analyzeHelpers.d.ts +23 -2
- package/dist/lib/reverse/analyzeHelpers.d.ts.map +1 -1
- package/dist/lib/reverse/analyzeHelpers.js +97 -4
- package/dist/lib/reverse/buildHelpers.d.ts.map +1 -1
- package/dist/lib/reverse/buildHelpers.js +19 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -84,6 +84,11 @@ export default {
|
|
|
84
84
|
- **Multi-Root Support**: Analyze multiple directories at once
|
|
85
85
|
- **Automatic Conflict Resolution**: Numeric suffixes (User, User1, User2)
|
|
86
86
|
- **Folder Name Normalization**: `user-action` → `UserAction`, `user_profile` → `UserProfile`
|
|
87
|
+
|
|
88
|
+
**Plural/Singular Recognition**: Automatically detects both forms of names:
|
|
89
|
+
- Analyzing "User" will also find "Users"
|
|
90
|
+
- Analyzing "Users" will also find "User"
|
|
91
|
+
- Works with common patterns: Category/Categories, Box/Boxes, etc.
|
|
87
92
|
- **TypeScript Config**: Type-safe `preset.config.ts` with enums
|
|
88
93
|
|
|
89
94
|
|
|
@@ -9,7 +9,18 @@
|
|
|
9
9
|
*/
|
|
10
10
|
export declare function toPascalCase(str: string): string;
|
|
11
11
|
/**
|
|
12
|
-
*
|
|
12
|
+
* Simple pluralization - adds 's' or handles common patterns
|
|
13
|
+
* Examples: User -> Users, Category -> Categories, Box -> Boxes
|
|
14
|
+
*/
|
|
15
|
+
export declare function pluralize(word: string): string;
|
|
16
|
+
/**
|
|
17
|
+
* Simple singularization - removes 's' or handles common patterns
|
|
18
|
+
* Examples: Users -> User, Categories -> Category, Boxes -> Box
|
|
19
|
+
*/
|
|
20
|
+
export declare function singularize(word: string): string;
|
|
21
|
+
/**
|
|
22
|
+
* Generates naming variations for a given subject string.
|
|
23
|
+
* Now includes plural and singular forms.
|
|
13
24
|
*/
|
|
14
25
|
export declare function generateVariations(subject: string): {
|
|
15
26
|
pascal: string;
|
|
@@ -17,6 +28,16 @@ export declare function generateVariations(subject: string): {
|
|
|
17
28
|
lower: string;
|
|
18
29
|
upper: string;
|
|
19
30
|
kebab: string;
|
|
31
|
+
plural: string;
|
|
32
|
+
pluralCamel: string;
|
|
33
|
+
pluralLower: string;
|
|
34
|
+
pluralUpper: string;
|
|
35
|
+
pluralKebab: string;
|
|
36
|
+
singular: string;
|
|
37
|
+
singularCamel: string;
|
|
38
|
+
singularLower: string;
|
|
39
|
+
singularUpper: string;
|
|
40
|
+
singularKebab: string;
|
|
20
41
|
};
|
|
21
42
|
/**
|
|
22
43
|
* Identifies potential tokens in content based on variations
|
|
@@ -25,5 +46,5 @@ export declare function identifyTokens(content: string, variations: ReturnType<t
|
|
|
25
46
|
/**
|
|
26
47
|
* Resolves the absolute source root path
|
|
27
48
|
*/
|
|
28
|
-
export declare function resolveSourceRoot(presetDir: string, globalRoot?: string, layerRoot?: string): string;
|
|
49
|
+
export declare function resolveSourceRoot(presetDir: string, globalRoot?: string | string[], layerRoot?: string): string;
|
|
29
50
|
//# sourceMappingURL=analyzeHelpers.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analyzeHelpers.d.ts","sourceRoot":"","sources":["../../../src/lib/reverse/analyzeHelpers.ts"],"names":[],"mappings":"AAKA;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAahD;AAED
|
|
1
|
+
{"version":3,"file":"analyzeHelpers.d.ts","sourceRoot":"","sources":["../../../src/lib/reverse/analyzeHelpers.ts"],"names":[],"mappings":"AAKA;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAahD;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAiB9C;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAoBhD;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM;;;;;;;;;;;;;;;;EA4BjD;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,CAAC,OAAO,kBAAkB,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CA2DzH;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,SAAS,GAAE,MAAW,GAAG,MAAM,CASnH"}
|
|
@@ -22,7 +22,50 @@ export function toPascalCase(str) {
|
|
|
22
22
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
23
23
|
}
|
|
24
24
|
/**
|
|
25
|
-
*
|
|
25
|
+
* Simple pluralization - adds 's' or handles common patterns
|
|
26
|
+
* Examples: User -> Users, Category -> Categories, Box -> Boxes
|
|
27
|
+
*/
|
|
28
|
+
export function pluralize(word) {
|
|
29
|
+
if (!word)
|
|
30
|
+
return word;
|
|
31
|
+
// Check for special endings that need 'es' FIRST (like 'ss', 'x', 'ch', 'sh')
|
|
32
|
+
if (word.endsWith('ss') || word.endsWith('x') || word.endsWith('ch') || word.endsWith('sh')) {
|
|
33
|
+
return word + 'es'; // Class -> Classes, Box -> Boxes, Batch -> Batches
|
|
34
|
+
}
|
|
35
|
+
// Already plural (ends with 's' but not 'ss')
|
|
36
|
+
if (word.endsWith('s'))
|
|
37
|
+
return word;
|
|
38
|
+
// Common patterns
|
|
39
|
+
if (word.endsWith('y') && !['a', 'e', 'i', 'o', 'u'].includes(word[word.length - 2]?.toLowerCase())) {
|
|
40
|
+
return word.slice(0, -1) + 'ies'; // Category -> Categories
|
|
41
|
+
}
|
|
42
|
+
return word + 's'; // User -> Users
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Simple singularization - removes 's' or handles common patterns
|
|
46
|
+
* Examples: Users -> User, Categories -> Category, Boxes -> Box
|
|
47
|
+
*/
|
|
48
|
+
export function singularize(word) {
|
|
49
|
+
if (!word)
|
|
50
|
+
return word;
|
|
51
|
+
// Common patterns
|
|
52
|
+
if (word.endsWith('ies')) {
|
|
53
|
+
if (word.length > 4) {
|
|
54
|
+
return word.slice(0, -3) + 'y'; // Categories -> Category
|
|
55
|
+
}
|
|
56
|
+
// Short words like 'Ties' -> 'Tie' (remove 's') or handle as special
|
|
57
|
+
}
|
|
58
|
+
if (word.endsWith('xes') || word.endsWith('ches') || word.endsWith('shes') || word.endsWith('sses')) {
|
|
59
|
+
return word.slice(0, -2); // Boxes -> Box, Batches -> Batch
|
|
60
|
+
}
|
|
61
|
+
if (word.endsWith('s') && !word.endsWith('ss')) {
|
|
62
|
+
return word.slice(0, -1); // Users -> User
|
|
63
|
+
}
|
|
64
|
+
return word; // Already singular or special case
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Generates naming variations for a given subject string.
|
|
68
|
+
* Now includes plural and singular forms.
|
|
26
69
|
*/
|
|
27
70
|
export function generateVariations(subject) {
|
|
28
71
|
const pascal = subject.charAt(0).toUpperCase() + subject.slice(1);
|
|
@@ -31,7 +74,22 @@ export function generateVariations(subject) {
|
|
|
31
74
|
const upper = subject.toUpperCase();
|
|
32
75
|
// Simple kebab conversion (UserProfile -> user-profile)
|
|
33
76
|
const kebab = camel.replace(/[A-Z]/g, m => '-' + m.toLowerCase());
|
|
34
|
-
|
|
77
|
+
// Generate plural and singular forms
|
|
78
|
+
const plural = pluralize(pascal);
|
|
79
|
+
const singular = singularize(pascal);
|
|
80
|
+
const pluralCamel = plural.charAt(0).toLowerCase() + plural.slice(1);
|
|
81
|
+
const pluralLower = plural.toLowerCase();
|
|
82
|
+
const pluralUpper = plural.toUpperCase();
|
|
83
|
+
const pluralKebab = pluralCamel.replace(/[A-Z]/g, m => '-' + m.toLowerCase());
|
|
84
|
+
const singularCamel = singular.charAt(0).toLowerCase() + singular.slice(1);
|
|
85
|
+
const singularLower = singular.toLowerCase();
|
|
86
|
+
const singularUpper = singular.toUpperCase();
|
|
87
|
+
const singularKebab = singularCamel.replace(/[A-Z]/g, m => '-' + m.toLowerCase());
|
|
88
|
+
return {
|
|
89
|
+
pascal, camel, lower, upper, kebab,
|
|
90
|
+
plural, pluralCamel, pluralLower, pluralUpper, pluralKebab,
|
|
91
|
+
singular, singularCamel, singularLower, singularUpper, singularKebab
|
|
92
|
+
};
|
|
35
93
|
}
|
|
36
94
|
/**
|
|
37
95
|
* Identifies potential tokens in content based on variations
|
|
@@ -54,6 +112,38 @@ export function identifyTokens(content, variations) {
|
|
|
54
112
|
if (variations.kebab !== variations.camel && content.includes(variations.kebab)) {
|
|
55
113
|
tokens[variations.kebab] = EntityToken.ENTITY_NAME_KEBAB;
|
|
56
114
|
}
|
|
115
|
+
// Add plural variations if different from original
|
|
116
|
+
if (variations.plural !== variations.pascal && content.includes(variations.plural)) {
|
|
117
|
+
tokens[variations.plural] = EntityToken.ENTITY_NAME;
|
|
118
|
+
}
|
|
119
|
+
if (variations.pluralCamel !== variations.camel && content.includes(variations.pluralCamel)) {
|
|
120
|
+
tokens[variations.pluralCamel] = EntityToken.ENTITY_NAME_CAMEL;
|
|
121
|
+
}
|
|
122
|
+
if (variations.pluralLower !== variations.lower && content.includes(variations.pluralLower)) {
|
|
123
|
+
tokens[variations.pluralLower] = EntityToken.ENTITY_NAME_LOWER;
|
|
124
|
+
}
|
|
125
|
+
if (variations.pluralUpper !== variations.upper && content.includes(variations.pluralUpper)) {
|
|
126
|
+
tokens[variations.pluralUpper] = EntityToken.ENTITY_NAME_UPPER;
|
|
127
|
+
}
|
|
128
|
+
if (variations.pluralKebab !== variations.kebab && content.includes(variations.pluralKebab)) {
|
|
129
|
+
tokens[variations.pluralKebab] = EntityToken.ENTITY_NAME_KEBAB;
|
|
130
|
+
}
|
|
131
|
+
// Add singular variations if different from original and plural
|
|
132
|
+
if (variations.singular !== variations.pascal && variations.singular !== variations.plural && content.includes(variations.singular)) {
|
|
133
|
+
tokens[variations.singular] = EntityToken.ENTITY_NAME;
|
|
134
|
+
}
|
|
135
|
+
if (variations.singularCamel !== variations.camel && variations.singularCamel !== variations.pluralCamel && content.includes(variations.singularCamel)) {
|
|
136
|
+
tokens[variations.singularCamel] = EntityToken.ENTITY_NAME_CAMEL;
|
|
137
|
+
}
|
|
138
|
+
if (variations.singularLower !== variations.lower && variations.singularLower !== variations.pluralLower && content.includes(variations.singularLower)) {
|
|
139
|
+
tokens[variations.singularLower] = EntityToken.ENTITY_NAME_LOWER;
|
|
140
|
+
}
|
|
141
|
+
if (variations.singularUpper !== variations.upper && variations.singularUpper !== variations.pluralUpper && content.includes(variations.singularUpper)) {
|
|
142
|
+
tokens[variations.singularUpper] = EntityToken.ENTITY_NAME_UPPER;
|
|
143
|
+
}
|
|
144
|
+
if (variations.singularKebab !== variations.kebab && variations.singularKebab !== variations.pluralKebab && content.includes(variations.singularKebab)) {
|
|
145
|
+
tokens[variations.singularKebab] = EntityToken.ENTITY_NAME_KEBAB;
|
|
146
|
+
}
|
|
57
147
|
return tokens;
|
|
58
148
|
}
|
|
59
149
|
/**
|
|
@@ -62,7 +152,10 @@ export function identifyTokens(content, variations) {
|
|
|
62
152
|
export function resolveSourceRoot(presetDir, globalRoot, layerRoot = '') {
|
|
63
153
|
let basePath = presetDir;
|
|
64
154
|
if (globalRoot) {
|
|
65
|
-
|
|
155
|
+
// Handle array if user accidentally passed one
|
|
156
|
+
const root = Array.isArray(globalRoot) ? String(globalRoot[0]) : String(globalRoot);
|
|
157
|
+
basePath = resolve(presetDir, root);
|
|
66
158
|
}
|
|
67
|
-
|
|
159
|
+
// layerRoot should already be normalized to string by normalizeLayers, but we cast just in case
|
|
160
|
+
return resolve(basePath, String(layerRoot));
|
|
68
161
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"buildHelpers.d.ts","sourceRoot":"","sources":["../../../src/lib/reverse/buildHelpers.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,kBAAkB,EAAoB,0BAA0B,EAAE,MAAM,YAAY,CAAC;AAEpJ;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAUrF;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAiB/E;AAED;;GAEG;AACH,wBAAsB,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC;IACrE,YAAY,EAAE,kBAAkB,CAAC;IACjC,YAAY,EAAE,YAAY,CAAC;CAC9B,CAAC,CAID;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,YAAY,EAAE,kBAAkB,GAAG,0BAA0B,EAAE,
|
|
1
|
+
{"version":3,"file":"buildHelpers.d.ts","sourceRoot":"","sources":["../../../src/lib/reverse/buildHelpers.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,kBAAkB,EAAoB,0BAA0B,EAAE,MAAM,YAAY,CAAC;AAEpJ;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAUrF;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAiB/E;AAED;;GAEG;AACH,wBAAsB,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC;IACrE,YAAY,EAAE,kBAAkB,CAAC;IACjC,YAAY,EAAE,YAAY,CAAC;CAC9B,CAAC,CAID;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,YAAY,EAAE,kBAAkB,GAAG,0BAA0B,EAAE,CAiE9F;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,YAAY,GAAG,MAAM,CAcpE;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,oBAAoB,GAAG,MAAM,CAUjF;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,0BAA0B,EAAE,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CA2BlH;AAED;;GAEG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAiBlF;AAED;;GAEG;AACH,wBAAgB,4BAA4B,CACxC,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,gBAAgB,EAAE,EACzB,MAAM,EAAE,0BAA0B,EAAE,EACpC,WAAW,EAAE,MAAM,GACpB,MAAM,CAqCR"}
|
|
@@ -47,18 +47,24 @@ export async function loadReverseEnvironment(presetDir) {
|
|
|
47
47
|
*/
|
|
48
48
|
export function normalizeLayers(sourceConfig) {
|
|
49
49
|
const result = [];
|
|
50
|
-
|
|
50
|
+
// Helper to ensure we have a string root
|
|
51
|
+
const toS = (v) => String(v);
|
|
52
|
+
if (sourceConfig.layers && Array.isArray(sourceConfig.layers)) {
|
|
51
53
|
for (const layer of sourceConfig.layers) {
|
|
52
|
-
|
|
54
|
+
if (!layer || !layer.root)
|
|
55
|
+
continue;
|
|
56
|
+
// Flatten deeply nested arrays explicitly
|
|
57
|
+
const roots = (Array.isArray(layer.root) ? layer.root : [layer.root]).flat(Infinity);
|
|
53
58
|
if (roots.length === 1) {
|
|
54
|
-
result.push({ root: roots[0], targetLayer: layer.targetLayer });
|
|
59
|
+
result.push({ root: toS(roots[0]), targetLayer: layer.targetLayer });
|
|
55
60
|
}
|
|
56
|
-
else {
|
|
57
|
-
const basenames = roots.map(r => basename(r));
|
|
61
|
+
else if (roots.length > 1) {
|
|
62
|
+
const basenames = roots.map(r => basename(toS(r)));
|
|
58
63
|
const nameCount = new Map();
|
|
59
64
|
const nameIndices = new Map();
|
|
60
65
|
basenames.forEach(name => { nameCount.set(name, (nameCount.get(name) || 0) + 1); });
|
|
61
|
-
roots.forEach((
|
|
66
|
+
roots.forEach((rawRoot, index) => {
|
|
67
|
+
const root = toS(rawRoot);
|
|
62
68
|
const name = basenames[index];
|
|
63
69
|
const count = nameCount.get(name) || 1;
|
|
64
70
|
if (count > 1) {
|
|
@@ -75,17 +81,19 @@ export function normalizeLayers(sourceConfig) {
|
|
|
75
81
|
}
|
|
76
82
|
}
|
|
77
83
|
else if (sourceConfig.root) {
|
|
78
|
-
|
|
84
|
+
// Flatten deeply nested arrays explicitly
|
|
85
|
+
const roots = (Array.isArray(sourceConfig.root) ? sourceConfig.root : [sourceConfig.root]).flat(Infinity);
|
|
79
86
|
const targetLayer = sourceConfig.targetLayer || 'entity';
|
|
80
87
|
if (roots.length === 1) {
|
|
81
|
-
result.push({ root: roots[0], targetLayer });
|
|
88
|
+
result.push({ root: toS(roots[0]), targetLayer });
|
|
82
89
|
}
|
|
83
|
-
else {
|
|
84
|
-
const basenames = roots.map(r => basename(r));
|
|
90
|
+
else if (roots.length > 1) {
|
|
91
|
+
const basenames = roots.map(r => basename(toS(r)));
|
|
85
92
|
const nameCount = new Map();
|
|
86
93
|
const nameIndices = new Map();
|
|
87
94
|
basenames.forEach(name => { nameCount.set(name, (nameCount.get(name) || 0) + 1); });
|
|
88
|
-
roots.forEach((
|
|
95
|
+
roots.forEach((rawRoot, index) => {
|
|
96
|
+
const root = toS(rawRoot);
|
|
89
97
|
const name = basenames[index];
|
|
90
98
|
const count = nameCount.get(name) || 1;
|
|
91
99
|
if (count > 1) {
|
package/package.json
CHANGED