scss-variable-extractor 1.6.5 → 1.6.6
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/MULTI-APP-GUIDE.md +262 -0
- package/README.md +269 -1
- package/bin/cli.js +195 -0
- package/multi-app.example.json +44 -0
- package/package.json +2 -1
- package/src/multi-app-analyzer.js +612 -0
- package/test/multi-app-analyzer.test.js +216 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
const {
|
|
2
|
+
analyzeMultiAppDependencies,
|
|
3
|
+
detectCrossAppDependencies,
|
|
4
|
+
identifySharedStyles,
|
|
5
|
+
buildDependencyGraph,
|
|
6
|
+
} = require('../src/multi-app-analyzer');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
|
|
9
|
+
describe('Multi-App Analyzer', () => {
|
|
10
|
+
describe('analyzeMultiAppDependencies', () => {
|
|
11
|
+
test('should analyze multiple apps', async () => {
|
|
12
|
+
const config = {
|
|
13
|
+
apps: [
|
|
14
|
+
{
|
|
15
|
+
name: 'app-a',
|
|
16
|
+
path: path.join(__dirname, 'fixtures/apps/subapp/src/app/component-a'),
|
|
17
|
+
type: 'app',
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
name: 'app-b',
|
|
21
|
+
path: path.join(__dirname, 'fixtures/apps/subapp/src/app/component-b'),
|
|
22
|
+
type: 'app',
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
sharedLibraries: [],
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const analysis = await analyzeMultiAppDependencies(config);
|
|
29
|
+
|
|
30
|
+
expect(analysis).toHaveProperty('apps');
|
|
31
|
+
expect(analysis).toHaveProperty('dependencies');
|
|
32
|
+
expect(analysis).toHaveProperty('sharedStyles');
|
|
33
|
+
expect(analysis).toHaveProperty('recommendations');
|
|
34
|
+
expect(analysis.apps.size).toBe(2);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('should detect app details', async () => {
|
|
38
|
+
const config = {
|
|
39
|
+
apps: [
|
|
40
|
+
{
|
|
41
|
+
name: 'test-app',
|
|
42
|
+
path: path.join(__dirname, 'fixtures/apps/subapp/src/app/component-a'),
|
|
43
|
+
type: 'app',
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
sharedLibraries: [],
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const analysis = await analyzeMultiAppDependencies(config);
|
|
50
|
+
const app = analysis.apps.get('test-app');
|
|
51
|
+
|
|
52
|
+
expect(app).toBeDefined();
|
|
53
|
+
expect(app.scssFiles.length).toBeGreaterThan(0);
|
|
54
|
+
expect(app.classes.size).toBeGreaterThan(0);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test('should identify shared libraries', async () => {
|
|
58
|
+
const config = {
|
|
59
|
+
apps: [
|
|
60
|
+
{
|
|
61
|
+
name: 'app-a',
|
|
62
|
+
path: path.join(__dirname, 'fixtures/apps/subapp/src/app/component-a'),
|
|
63
|
+
type: 'app',
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
sharedLibraries: [
|
|
67
|
+
{
|
|
68
|
+
name: 'shared-lib',
|
|
69
|
+
path: path.join(__dirname, 'fixtures/libs/styles'),
|
|
70
|
+
type: 'library',
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const analysis = await analyzeMultiAppDependencies(config);
|
|
76
|
+
const lib = analysis.apps.get('shared-lib');
|
|
77
|
+
|
|
78
|
+
expect(lib).toBeDefined();
|
|
79
|
+
expect(lib.type).toBe('library');
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
describe('identifySharedStyles', () => {
|
|
84
|
+
test('should identify shared CSS classes', () => {
|
|
85
|
+
const apps = new Map();
|
|
86
|
+
apps.set('app-1', {
|
|
87
|
+
classes: new Set(['btn', 'card', 'header']),
|
|
88
|
+
variables: new Set([]),
|
|
89
|
+
mixins: new Set([]),
|
|
90
|
+
});
|
|
91
|
+
apps.set('app-2', {
|
|
92
|
+
classes: new Set(['btn', 'footer', 'header']),
|
|
93
|
+
variables: new Set([]),
|
|
94
|
+
mixins: new Set([]),
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const sharedStyles = identifySharedStyles(apps);
|
|
98
|
+
|
|
99
|
+
expect(sharedStyles.size).toBeGreaterThan(0);
|
|
100
|
+
expect(sharedStyles.has('btn')).toBe(true);
|
|
101
|
+
expect(sharedStyles.has('header')).toBe(true);
|
|
102
|
+
|
|
103
|
+
const btnStyle = sharedStyles.get('btn');
|
|
104
|
+
expect(btnStyle.usedIn).toContain('app-1');
|
|
105
|
+
expect(btnStyle.usedIn).toContain('app-2');
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test('should identify shared variables', () => {
|
|
109
|
+
const apps = new Map();
|
|
110
|
+
apps.set('app-1', {
|
|
111
|
+
classes: new Set([]),
|
|
112
|
+
variables: new Set(['primary-color', 'spacing-md']),
|
|
113
|
+
mixins: new Set([]),
|
|
114
|
+
});
|
|
115
|
+
apps.set('app-2', {
|
|
116
|
+
classes: new Set([]),
|
|
117
|
+
variables: new Set(['primary-color', 'accent-color']),
|
|
118
|
+
mixins: new Set([]),
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
const sharedStyles = identifySharedStyles(apps);
|
|
122
|
+
|
|
123
|
+
expect(sharedStyles.has('$primary-color')).toBe(true);
|
|
124
|
+
const varStyle = sharedStyles.get('$primary-color');
|
|
125
|
+
expect(varStyle.type).toBe('variable');
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test('should not mark single-use items as shared', () => {
|
|
129
|
+
const apps = new Map();
|
|
130
|
+
apps.set('app-1', {
|
|
131
|
+
classes: new Set(['unique-class']),
|
|
132
|
+
variables: new Set([]),
|
|
133
|
+
mixins: new Set([]),
|
|
134
|
+
});
|
|
135
|
+
apps.set('app-2', {
|
|
136
|
+
classes: new Set(['other-class']),
|
|
137
|
+
variables: new Set([]),
|
|
138
|
+
mixins: new Set([]),
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
const sharedStyles = identifySharedStyles(apps);
|
|
142
|
+
|
|
143
|
+
expect(sharedStyles.size).toBe(0);
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
describe('buildDependencyGraph', () => {
|
|
148
|
+
test('should build dependency graph', () => {
|
|
149
|
+
const dependencies = [
|
|
150
|
+
{ from: 'app-a', to: 'lib-shared', type: 'style-import', strength: 'strong' },
|
|
151
|
+
{ from: 'app-b', to: 'lib-shared', type: 'style-import', strength: 'strong' },
|
|
152
|
+
{ from: 'app-a', to: 'app-b', type: 'shared-classes', strength: 'weak' },
|
|
153
|
+
];
|
|
154
|
+
|
|
155
|
+
const graph = buildDependencyGraph(dependencies);
|
|
156
|
+
|
|
157
|
+
expect(graph['app-a']).toBeDefined();
|
|
158
|
+
expect(graph['app-a'].dependsOn.length).toBe(2);
|
|
159
|
+
expect(graph['lib-shared']).toBeDefined();
|
|
160
|
+
expect(graph['lib-shared'].dependedBy.length).toBe(2);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
test('should track both directions', () => {
|
|
164
|
+
const dependencies = [
|
|
165
|
+
{ from: 'app-a', to: 'lib-b', type: 'style-import', strength: 'strong' },
|
|
166
|
+
];
|
|
167
|
+
|
|
168
|
+
const graph = buildDependencyGraph(dependencies);
|
|
169
|
+
|
|
170
|
+
expect(graph['app-a'].dependsOn).toHaveLength(1);
|
|
171
|
+
expect(graph['lib-b'].dependedBy).toHaveLength(1);
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
describe('detectCrossAppDependencies', () => {
|
|
176
|
+
test('should detect shared classes between apps', () => {
|
|
177
|
+
const apps = new Map();
|
|
178
|
+
apps.set('app-1', {
|
|
179
|
+
classes: new Set(['btn', 'card']),
|
|
180
|
+
externalReferences: [],
|
|
181
|
+
});
|
|
182
|
+
apps.set('app-2', {
|
|
183
|
+
classes: new Set(['btn', 'footer']),
|
|
184
|
+
externalReferences: [],
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
const dependencies = detectCrossAppDependencies(apps);
|
|
188
|
+
const sharedClassDeps = dependencies.filter(d => d.type === 'shared-classes');
|
|
189
|
+
|
|
190
|
+
expect(sharedClassDeps.length).toBeGreaterThan(0);
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
test('should detect external references', () => {
|
|
194
|
+
const apps = new Map();
|
|
195
|
+
apps.set('app-a', {
|
|
196
|
+
classes: new Set([]),
|
|
197
|
+
externalReferences: [
|
|
198
|
+
{
|
|
199
|
+
from: 'styles.scss',
|
|
200
|
+
to: '../../lib-shared/variables',
|
|
201
|
+
type: '@use',
|
|
202
|
+
},
|
|
203
|
+
],
|
|
204
|
+
});
|
|
205
|
+
apps.set('lib-shared', {
|
|
206
|
+
classes: new Set([]),
|
|
207
|
+
externalReferences: [],
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
const dependencies = detectCrossAppDependencies(apps);
|
|
211
|
+
const importDeps = dependencies.filter(d => d.type === 'style-import');
|
|
212
|
+
|
|
213
|
+
expect(importDeps.length).toBeGreaterThan(0);
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
});
|