@rivm-loculus-tools/common-frontend 0.0.0-stage → 0.1.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/CHANGELOG.md +29 -0
- package/LICENSE +288 -0
- package/eslint.config.js +16 -0
- package/package.json +160 -3
- package/scripts/update-exports.js +9 -0
- package/src/.gitkeep +0 -0
- package/src/@types/vite-env.d.ts +3 -0
- package/src/classes/services/FastaDataEventBusService/FastaDataEventBusService.ts +22 -0
- package/src/classes/services/FastaDataEventBusService/index.ts +1 -0
- package/src/components/FastaDataImportDialog/FastaDataImportDialog.tsx +364 -0
- package/src/components/FastaDataImportDialog/index.ts +1 -0
- package/src/components/FastaDataImportDialogLoader/FastaDataImportDialogLoader.tsx +124 -0
- package/src/components/FastaDataImportDialogLoader/index.ts +1 -0
- package/src/components/FastaNotLoaded/FastaNotLoaded.tsx +62 -0
- package/src/components/FastaNotLoaded/index.ts +1 -0
- package/src/components/FastaPageHeaderCenterComponent/FastaPageHeaderCenterComponent.tsx +50 -0
- package/src/components/FastaPageHeaderCenterComponent/index.ts +1 -0
- package/src/components/FastaPageHeaderImportButton/FastaPageHeaderImportButton.tsx +28 -0
- package/src/components/FastaPageHeaderImportButton/index.ts +1 -0
- package/src/components/FastaPageHeaderResetButton/FastaPageHeaderResetButton.tsx +50 -0
- package/src/components/FastaPageHeaderResetButton/index.ts +1 -0
- package/src/components/I18nWrapper/I18nWrapper.tsx +39 -0
- package/src/components/I18nWrapper/index.ts +1 -0
- package/src/components/PageHeaderDocumentationButton/PageHeaderDocumentationButton.tsx +25 -0
- package/src/components/PageHeaderDocumentationButton/index.ts +1 -0
- package/src/components/SearchBar/SearchBar.tsx +207 -0
- package/src/components/SearchBar/SearchBarNodeCounter.tsx +23 -0
- package/src/components/SearchBar/index.ts +1 -0
- package/src/components/page/ErrorPageCommon.tsx +51 -0
- package/src/components/page/PageCommon.tsx +76 -0
- package/src/components/page/PageCommonContext.ts +15 -0
- package/src/components/page/PageCommonContextProvider.tsx +30 -0
- package/src/components/page/PageCommonDrawer.tsx +35 -0
- package/src/components/page/PageCommonHeader.tsx +99 -0
- package/src/components/page/index.ts +6 -0
- package/src/constants/page.ts +1 -0
- package/src/index.ts +21 -0
- package/src/models/fasta.ts +22 -0
- package/src/models/metadata.ts +10 -0
- package/src/stores/fastaDataStore/fastaDataStore.ts +103 -0
- package/src/stores/fastaDataStore/index.ts +1 -0
- package/src/theme/fonts/RobotoFlex.woff2 +0 -0
- package/src/theme/index.ts +1 -0
- package/src/theme/theme.ts +368 -0
- package/src/utils/DownloadUtil/DownloadUtil.ts +10 -0
- package/src/utils/DownloadUtil/index.ts +1 -0
- package/src/utils/ErrorUtil/ErrorUtil.ts +5 -0
- package/src/utils/ErrorUtil/index.ts +1 -0
- package/src/utils/FastaUtil/FastaUtil.ts +109 -0
- package/src/utils/FastaUtil/index.ts +1 -0
- package/src/utils/MetaDataUtil/MetaDataUtil.ts +292 -0
- package/src/utils/MetaDataUtil/index.ts +1 -0
- package/src/utils/UrlUtil/UrlUtil.ts +35 -0
- package/src/utils/UrlUtil/index.ts +1 -0
- package/tsconfig.build.json +14 -0
- package/tsconfig.json +32 -0
- package/vite.config.ts +137 -0
- package/README.md +0 -3
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
MetadataParseResult,
|
|
3
|
+
MetadataRecord,
|
|
4
|
+
} from '../../models/metadata';
|
|
5
|
+
|
|
6
|
+
export class MetaDataUtil {
|
|
7
|
+
/**
|
|
8
|
+
* Compute the best matching metadata column for the given FASTA sequence IDs.
|
|
9
|
+
*/
|
|
10
|
+
public static findBestMatchingColumn = (
|
|
11
|
+
columns: string[],
|
|
12
|
+
recordsByColumn: Record<string, Record<string, MetadataRecord>>,
|
|
13
|
+
fastaIds: string[],
|
|
14
|
+
): { bestColumn: null | string; matchCounts: Record<string, number> } => {
|
|
15
|
+
const matchCounts = MetaDataUtil.getMatchCounts(columns, recordsByColumn, fastaIds);
|
|
16
|
+
let bestColumn: null | string = null;
|
|
17
|
+
let maxMatches = -1;
|
|
18
|
+
|
|
19
|
+
if (!columns || !recordsByColumn || !fastaIds || fastaIds.length === 0) {
|
|
20
|
+
return { bestColumn: null, matchCounts: {} };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const prioritizedKeys = [
|
|
24
|
+
'displayname',
|
|
25
|
+
'accessionversion',
|
|
26
|
+
'accession',
|
|
27
|
+
'sampleid',
|
|
28
|
+
'sample_id',
|
|
29
|
+
'id',
|
|
30
|
+
'strain',
|
|
31
|
+
'isolate',
|
|
32
|
+
'submissionid',
|
|
33
|
+
'name',
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
for (const col of columns) {
|
|
37
|
+
const colDict = recordsByColumn[col];
|
|
38
|
+
if (!colDict) {
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const count = matchCounts[col];
|
|
43
|
+
|
|
44
|
+
if (count > maxMatches) {
|
|
45
|
+
maxMatches = count;
|
|
46
|
+
bestColumn = col;
|
|
47
|
+
} else if (count === maxMatches && count > 0 && bestColumn) {
|
|
48
|
+
// Tie-breaker: prioritize standard ID column names
|
|
49
|
+
const currentPriority = prioritizedKeys.indexOf(bestColumn.toLowerCase());
|
|
50
|
+
const newPriority = prioritizedKeys.indexOf(col.toLowerCase());
|
|
51
|
+
if (newPriority !== -1 && (currentPriority === -1 || newPriority < currentPriority)) {
|
|
52
|
+
bestColumn = col;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (maxMatches <= 0) {
|
|
58
|
+
// If no matches, default to highest priority known ID column if available
|
|
59
|
+
for (const pKey of prioritizedKeys) {
|
|
60
|
+
const found = columns.find((c) => c.toLowerCase() === pKey);
|
|
61
|
+
if (found) {
|
|
62
|
+
bestColumn = found;
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (!bestColumn && columns.length > 0) {
|
|
67
|
+
bestColumn = columns[0];
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return { bestColumn, matchCounts };
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
public static getHighlightedIds(query: string, allAccessions: string[], metadataRecords: MetadataRecord[], matchKeyField: null | string = null): string[] {
|
|
75
|
+
const trimmed = query.trim().toLowerCase();
|
|
76
|
+
if (!trimmed) {
|
|
77
|
+
return [];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const matched = new Set<string>();
|
|
81
|
+
|
|
82
|
+
// Check accession names / fasta IDs
|
|
83
|
+
allAccessions.forEach((acc) => {
|
|
84
|
+
if (acc.toLowerCase().includes(trimmed)) {
|
|
85
|
+
matched.add(acc);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// Check metadata records
|
|
90
|
+
metadataRecords.forEach((rec) => {
|
|
91
|
+
const acc =
|
|
92
|
+
(matchKeyField && rec[matchKeyField]) ||
|
|
93
|
+
rec.displayName ||
|
|
94
|
+
rec.accessionVersion ||
|
|
95
|
+
rec.accession ||
|
|
96
|
+
rec.id ||
|
|
97
|
+
rec.submissionId ||
|
|
98
|
+
rec.SampleID;
|
|
99
|
+
if (acc) {
|
|
100
|
+
const matchesAnyField = Object.values(rec).some(
|
|
101
|
+
(val) => typeof val === 'string' && val.toLowerCase().includes(trimmed),
|
|
102
|
+
);
|
|
103
|
+
if (matchesAnyField) {
|
|
104
|
+
matched.add(acc);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
return Array.from(matched);
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Count FASTA sequence ID matches for each metadata column.
|
|
113
|
+
*/
|
|
114
|
+
public static getMatchCounts = (
|
|
115
|
+
columns: string[],
|
|
116
|
+
recordsByColumn: Record<string, Record<string, MetadataRecord>>,
|
|
117
|
+
fastaIds: string[],
|
|
118
|
+
): Record<string, number> => {
|
|
119
|
+
const matchCounts: Record<string, number> = {};
|
|
120
|
+
|
|
121
|
+
if (!columns || !recordsByColumn || !fastaIds || fastaIds.length === 0) {
|
|
122
|
+
return matchCounts;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
for (const col of columns) {
|
|
126
|
+
const colDict = recordsByColumn[col];
|
|
127
|
+
if (!colDict) {
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
let count = 0;
|
|
132
|
+
for (const id of fastaIds) {
|
|
133
|
+
if (colDict[id]) {
|
|
134
|
+
count++;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
matchCounts[col] = count;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return matchCounts;
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Retrieve metadata record for a given sample/node name with fallback strategies.
|
|
145
|
+
*/
|
|
146
|
+
public static readonly getRecordForSample = (
|
|
147
|
+
metadata: MetadataParseResult | null | undefined,
|
|
148
|
+
sampleId: string,
|
|
149
|
+
matchKeyField?: null | string,
|
|
150
|
+
): MetadataRecord | undefined => {
|
|
151
|
+
if (!metadata || !sampleId) {
|
|
152
|
+
return undefined;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// 1. If matchKeyField is specified and exists, check it first
|
|
156
|
+
if (matchKeyField && metadata.recordsByColumn?.[matchKeyField]) {
|
|
157
|
+
const rec = metadata.recordsByColumn[matchKeyField][sampleId];
|
|
158
|
+
if (rec) {
|
|
159
|
+
return rec;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// 2. Check displayName
|
|
164
|
+
if (metadata.recordsByColumn?.['displayName']?.[sampleId]) {
|
|
165
|
+
return metadata.recordsByColumn['displayName'][sampleId];
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// 3. Check accessionVersion
|
|
169
|
+
if (metadata.recordsByColumn?.['accessionVersion']?.[sampleId]) {
|
|
170
|
+
return metadata.recordsByColumn['accessionVersion'][sampleId];
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// 4. Check accession maps
|
|
174
|
+
if (metadata.recordsByAccession[sampleId]) {
|
|
175
|
+
return metadata.recordsByAccession[sampleId];
|
|
176
|
+
}
|
|
177
|
+
if (metadata.recordsBySampleId[sampleId]) {
|
|
178
|
+
return metadata.recordsBySampleId[sampleId];
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// 5. Broad search through all indexed columns
|
|
182
|
+
if (metadata.recordsByColumn) {
|
|
183
|
+
for (const col of Object.keys(metadata.recordsByColumn)) {
|
|
184
|
+
const match = metadata.recordsByColumn[col][sampleId];
|
|
185
|
+
if (match) {
|
|
186
|
+
return match;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return undefined;
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
public static parseTsv = (content: string): MetadataParseResult => {
|
|
195
|
+
if (!content?.trim()) {
|
|
196
|
+
return {
|
|
197
|
+
columns: [],
|
|
198
|
+
error: 'Empty metadata content provided.',
|
|
199
|
+
records: [],
|
|
200
|
+
recordsByAccession: {},
|
|
201
|
+
recordsByColumn: {},
|
|
202
|
+
recordsBySampleId: {},
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const lines = content.trim().split('\n');
|
|
207
|
+
if (lines.length < 2) {
|
|
208
|
+
return {
|
|
209
|
+
columns: [],
|
|
210
|
+
error: 'Metadata file must contain at least a header row and one data row.',
|
|
211
|
+
records: [],
|
|
212
|
+
recordsByAccession: {},
|
|
213
|
+
recordsByColumn: {},
|
|
214
|
+
recordsBySampleId: {},
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Parse header row
|
|
219
|
+
const headerLine = lines[0].trim();
|
|
220
|
+
let delimiter = '\t';
|
|
221
|
+
if (!headerLine.includes('\t') && headerLine.includes(',')) {
|
|
222
|
+
delimiter = ',';
|
|
223
|
+
}
|
|
224
|
+
const columns = headerLine
|
|
225
|
+
.split(delimiter)
|
|
226
|
+
.map((col) => col.trim().replace(/^["']|["']$/g, ''))
|
|
227
|
+
.filter((c) => c.length > 0);
|
|
228
|
+
|
|
229
|
+
const records: MetadataRecord[] = [];
|
|
230
|
+
const recordsByAccession: Record<string, MetadataRecord> = {};
|
|
231
|
+
const recordsBySampleId: Record<string, MetadataRecord> = {};
|
|
232
|
+
const recordsByColumn: Record<string, Record<string, MetadataRecord>> = {};
|
|
233
|
+
|
|
234
|
+
columns.forEach((col) => {
|
|
235
|
+
recordsByColumn[col] = {};
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
for (let i = 1; i < lines.length; i++) {
|
|
239
|
+
const line = lines[i].trim();
|
|
240
|
+
if (!line) {
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const values = line.split(delimiter).map((val) => val.trim().replace(/^["']|["']$/g, ''));
|
|
245
|
+
const record: MetadataRecord = {};
|
|
246
|
+
|
|
247
|
+
columns.forEach((col, colIdx) => {
|
|
248
|
+
const val = values[colIdx] ?? '';
|
|
249
|
+
record[col] = val;
|
|
250
|
+
if (val) {
|
|
251
|
+
recordsByColumn[col][val] = record;
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
records.push(record);
|
|
256
|
+
|
|
257
|
+
// Index by potential ID keys
|
|
258
|
+
const accessionKey =
|
|
259
|
+
record.displayName ||
|
|
260
|
+
record.accessionVersion ||
|
|
261
|
+
record.accession ||
|
|
262
|
+
record.id ||
|
|
263
|
+
record.submissionId ||
|
|
264
|
+
record.strain ||
|
|
265
|
+
record.isolate ||
|
|
266
|
+
record.name;
|
|
267
|
+
|
|
268
|
+
if (accessionKey) {
|
|
269
|
+
recordsByAccession[accessionKey] = record;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const sampleIdKey =
|
|
273
|
+
record.SampleID ||
|
|
274
|
+
record.sampleId ||
|
|
275
|
+
record.sample_id ||
|
|
276
|
+
record.specimenCollectorSampleId ||
|
|
277
|
+
record.id;
|
|
278
|
+
|
|
279
|
+
if (sampleIdKey) {
|
|
280
|
+
recordsBySampleId[sampleIdKey] = record;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
return {
|
|
285
|
+
columns,
|
|
286
|
+
records,
|
|
287
|
+
recordsByAccession,
|
|
288
|
+
recordsByColumn,
|
|
289
|
+
recordsBySampleId,
|
|
290
|
+
};
|
|
291
|
+
};
|
|
292
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './MetaDataUtil';
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { WindowService } from '@gen-epix/ui-core/classes/services/WindowService';
|
|
2
|
+
|
|
3
|
+
export class UrlUtil {
|
|
4
|
+
public static clearSearchParams(): void {
|
|
5
|
+
const url = new URL(WindowService.getInstance().window.location.href);
|
|
6
|
+
url.search = '';
|
|
7
|
+
WindowService.getInstance().window.history.replaceState(null, '', url.toString());
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
public static fastaLabelFromUrl(url: string): string {
|
|
11
|
+
try {
|
|
12
|
+
const urlObject = new URL(url);
|
|
13
|
+
const segment = urlObject.pathname.split('/').filter(Boolean).pop();
|
|
14
|
+
if (segment) {
|
|
15
|
+
return decodeURIComponent(segment);
|
|
16
|
+
}
|
|
17
|
+
return `${urlObject.hostname}.fasta`;
|
|
18
|
+
} catch {
|
|
19
|
+
return 'remote-alignment.fasta';
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
public static validateFastaUrl(url: string): string {
|
|
24
|
+
let urlObject: URL;
|
|
25
|
+
try {
|
|
26
|
+
urlObject = new URL(url.trim());
|
|
27
|
+
} catch {
|
|
28
|
+
throw new Error('Enter a valid FASTA URL.');
|
|
29
|
+
}
|
|
30
|
+
if (urlObject.protocol !== 'http:' && urlObject.protocol !== 'https:') {
|
|
31
|
+
throw new Error('FASTA URLs must use HTTP or HTTPS.');
|
|
32
|
+
}
|
|
33
|
+
return urlObject.href;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './UrlUtil';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/tsconfig",
|
|
3
|
+
"extends": "./tsconfig.json",
|
|
4
|
+
"include": [
|
|
5
|
+
"src/**/*"
|
|
6
|
+
],
|
|
7
|
+
"exclude": [
|
|
8
|
+
"src/**/*.test.*",
|
|
9
|
+
"src/**/*.spec.*",
|
|
10
|
+
"src/**/__tests__/**",
|
|
11
|
+
"src/**/tests/**",
|
|
12
|
+
"src/test/setup/**"
|
|
13
|
+
]
|
|
14
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/tsconfig",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"target": "esnext",
|
|
5
|
+
"useDefineForClassFields": true,
|
|
6
|
+
"lib": [
|
|
7
|
+
"ESNext",
|
|
8
|
+
"DOM",
|
|
9
|
+
"DOM.Iterable",
|
|
10
|
+
"WebWorker"
|
|
11
|
+
],
|
|
12
|
+
"module": "ESNext",
|
|
13
|
+
"skipLibCheck": true,
|
|
14
|
+
"moduleResolution": "bundler",
|
|
15
|
+
"allowImportingTsExtensions": true,
|
|
16
|
+
"isolatedModules": true,
|
|
17
|
+
"moduleDetection": "force",
|
|
18
|
+
"noEmit": true,
|
|
19
|
+
"jsx": "react-jsx",
|
|
20
|
+
"strict": true,
|
|
21
|
+
"noUnusedLocals": true,
|
|
22
|
+
"noUnusedParameters": true,
|
|
23
|
+
"noFallthroughCasesInSwitch": true,
|
|
24
|
+
"types": [
|
|
25
|
+
"vite/client"
|
|
26
|
+
]
|
|
27
|
+
},
|
|
28
|
+
"include": [
|
|
29
|
+
"src",
|
|
30
|
+
"vite.config.ts"
|
|
31
|
+
]
|
|
32
|
+
}
|
package/vite.config.ts
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import {
|
|
2
|
+
globSync,
|
|
3
|
+
readFileSync,
|
|
4
|
+
} from 'fs';
|
|
5
|
+
import {
|
|
6
|
+
dirname,
|
|
7
|
+
join,
|
|
8
|
+
} from 'path';
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
defineConfig,
|
|
12
|
+
esmExternalRequirePlugin,
|
|
13
|
+
} from 'vite';
|
|
14
|
+
import dts from 'vite-plugin-dts';
|
|
15
|
+
import { viteStaticCopy } from 'vite-plugin-static-copy';
|
|
16
|
+
import type { Target } from 'vite-plugin-static-copy';
|
|
17
|
+
|
|
18
|
+
type PackageManifest = {
|
|
19
|
+
peerDependencies?: Record<string, string>;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const createPeerDependencyExternalPatterns = () => {
|
|
23
|
+
const packageJson = JSON.parse(
|
|
24
|
+
readFileSync(join(import.meta.dirname, 'package.json'), 'utf8'),
|
|
25
|
+
) as PackageManifest;
|
|
26
|
+
|
|
27
|
+
return Object.keys(packageJson.peerDependencies ?? {}).map((dependency) => {
|
|
28
|
+
const escapedDependency = dependency.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
29
|
+
return new RegExp(`^${escapedDependency}(?:/.+)?$`);
|
|
30
|
+
});
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const peerDependencyExternalPatterns = createPeerDependencyExternalPatterns();
|
|
34
|
+
|
|
35
|
+
const discoverEntries = (): { entries: Record<string, string>; flatKeys: Set<string> } => {
|
|
36
|
+
const entries: Record<string, string> = {};
|
|
37
|
+
const flatKeys = new Set<string>();
|
|
38
|
+
|
|
39
|
+
const srcDir = join(import.meta.dirname, 'src');
|
|
40
|
+
|
|
41
|
+
// index.ts-based entries (classes, utils): key = component directory
|
|
42
|
+
globSync([
|
|
43
|
+
join(srcDir, 'classes', '**', 'index.ts'),
|
|
44
|
+
join(srcDir, 'components', '**', 'index.ts'),
|
|
45
|
+
join(srcDir, 'constants', '**', 'index.ts'),
|
|
46
|
+
join(srcDir, 'context', '**', 'index.ts'),
|
|
47
|
+
join(srcDir, 'dataHooks', '**', 'index.ts'),
|
|
48
|
+
join(srcDir, 'hoc', '**', 'index.ts'),
|
|
49
|
+
join(srcDir, 'hooks', '**', 'index.ts'),
|
|
50
|
+
join(srcDir, 'locale', '**', 'index.ts'),
|
|
51
|
+
join(srcDir, 'pages', '**', 'index.ts'),
|
|
52
|
+
join(srcDir, 'routes', '**', 'index.ts'),
|
|
53
|
+
join(srcDir, 'stores', '**', 'index.ts'),
|
|
54
|
+
join(srcDir, 'theme', '**', 'index.ts'),
|
|
55
|
+
join(srcDir, 'utils', '**', 'index.ts'),
|
|
56
|
+
]).forEach((file) => {
|
|
57
|
+
const relKey = dirname(file).replace(`${srcDir}/`, '');
|
|
58
|
+
entries[relKey] = `./${file.replace(`${import.meta.dirname}/`, '')}`;
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// flat .ts entries (models): key = file path without extension
|
|
62
|
+
globSync([join(srcDir, 'models', '*.ts'), join(srcDir, 'constants', '*.ts'), join(srcDir, 'setup', '*.ts')])
|
|
63
|
+
.filter((file) => !file.endsWith('/index.ts'))
|
|
64
|
+
.forEach((file) => {
|
|
65
|
+
const relKey = file.replace(`${srcDir}/`, '').replace(/\.ts$/, '');
|
|
66
|
+
entries[relKey] = `./${file.replace(`${import.meta.dirname}/`, '')}`;
|
|
67
|
+
flatKeys.add(relKey);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
return { entries, flatKeys };
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const { entries: libEntries, flatKeys } = discoverEntries();
|
|
74
|
+
|
|
75
|
+
// https://vitejs.dev/config/
|
|
76
|
+
// eslint-disable-next-line import-x/no-default-export
|
|
77
|
+
export default defineConfig({
|
|
78
|
+
build: {
|
|
79
|
+
copyPublicDir: false,
|
|
80
|
+
lib: {
|
|
81
|
+
entry: libEntries,
|
|
82
|
+
fileName: (_, entryAlias) => {
|
|
83
|
+
if (entryAlias === 'index') {
|
|
84
|
+
return 'index.js';
|
|
85
|
+
}
|
|
86
|
+
// flat .ts entries output alongside their .d.ts; index.ts entries output in a subdirectory
|
|
87
|
+
return flatKeys.has(entryAlias) ? `${entryAlias}.js` : `${entryAlias}/index.js`;
|
|
88
|
+
},
|
|
89
|
+
formats: ['es'],
|
|
90
|
+
},
|
|
91
|
+
rolldownOptions: {
|
|
92
|
+
output: {
|
|
93
|
+
chunkFileNames: '_chunks/[name].js',
|
|
94
|
+
},
|
|
95
|
+
plugins: [
|
|
96
|
+
esmExternalRequirePlugin({
|
|
97
|
+
external: peerDependencyExternalPatterns,
|
|
98
|
+
}),
|
|
99
|
+
],
|
|
100
|
+
treeshake: true,
|
|
101
|
+
},
|
|
102
|
+
sourcemap: true,
|
|
103
|
+
},
|
|
104
|
+
plugins: [
|
|
105
|
+
dts({
|
|
106
|
+
entryRoot: './src',
|
|
107
|
+
insertTypesEntry: false,
|
|
108
|
+
tsconfigPath: './tsconfig.build.json',
|
|
109
|
+
}),
|
|
110
|
+
viteStaticCopy({
|
|
111
|
+
targets: ([
|
|
112
|
+
{
|
|
113
|
+
dest: '@types',
|
|
114
|
+
rename: { stripBase: true },
|
|
115
|
+
src: [
|
|
116
|
+
'./src/@types/**/*.d.ts',
|
|
117
|
+
],
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
dest: './locale',
|
|
121
|
+
rename: { stripBase: true },
|
|
122
|
+
src: [
|
|
123
|
+
'./src/locale',
|
|
124
|
+
],
|
|
125
|
+
},
|
|
126
|
+
] satisfies Target[]).filter((target) => {
|
|
127
|
+
const sources = Array.isArray(target.src) ? target.src : [target.src];
|
|
128
|
+
return sources.some((pattern) => globSync(pattern).length > 0);
|
|
129
|
+
}),
|
|
130
|
+
}),
|
|
131
|
+
],
|
|
132
|
+
server: {
|
|
133
|
+
host: false,
|
|
134
|
+
open: true,
|
|
135
|
+
port: 5173,
|
|
136
|
+
},
|
|
137
|
+
});
|
package/README.md
DELETED