@fgv/ts-res-browser 1.0.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.
Files changed (95) hide show
  1. package/.rush/temp/5fc90bc7c2ccf812114ea099111568e5429be2a2.tar.log +15 -0
  2. package/.rush/temp/chunked-rush-logs/ts-res-browser.build.chunks.jsonl +35 -0
  3. package/.rush/temp/operation/build/all.log +35 -0
  4. package/.rush/temp/operation/build/log-chunks.jsonl +35 -0
  5. package/.rush/temp/operation/build/state.json +3 -0
  6. package/.rush/temp/shrinkwrap-deps.json +1217 -0
  7. package/README.md +95 -0
  8. package/TS_RES_BROWSER_PLAN.md +689 -0
  9. package/babel.config.js +17 -0
  10. package/config/jest.config.json +13 -0
  11. package/config/rig.json +16 -0
  12. package/dist/bundle.js +2 -0
  13. package/dist/bundle.js.LICENSE.txt +62 -0
  14. package/dist/index.html +1 -0
  15. package/package.json +78 -0
  16. package/postcss.config.js +3 -0
  17. package/public/index.html +15 -0
  18. package/rush-logs/ts-res-browser.build.cache.log +3 -0
  19. package/rush-logs/ts-res-browser.build.log +35 -0
  20. package/src/App.tsx +153 -0
  21. package/src/cli.ts +44 -0
  22. package/src/components/common/FileImporter.tsx +138 -0
  23. package/src/components/common/NavigationWarningModal.tsx +62 -0
  24. package/src/components/layout/AppLayout.tsx +46 -0
  25. package/src/components/layout/Header.tsx +22 -0
  26. package/src/components/layout/MessagesWindow.tsx +108 -0
  27. package/src/components/layout/Sidebar.tsx +92 -0
  28. package/src/components/tools/CompiledBrowser.tsx +949 -0
  29. package/src/components/tools/ConfigurationTool.tsx +1402 -0
  30. package/src/components/tools/FilterTool.tsx +1027 -0
  31. package/src/components/tools/ImportTool.tsx +246 -0
  32. package/src/components/tools/ResolutionViewer.tsx +1672 -0
  33. package/src/components/tools/SourceBrowser.tsx +496 -0
  34. package/src/hooks/useAppState.ts +107 -0
  35. package/src/hooks/useFileImport.ts +86 -0
  36. package/src/hooks/useNavigationWarning.ts +67 -0
  37. package/src/hooks/useResourceManager.ts +244 -0
  38. package/src/index.css +38 -0
  39. package/src/main.tsx +16 -0
  40. package/src/types/app.ts +62 -0
  41. package/src/types/fileSystemAccess.d.ts +94 -0
  42. package/src/utils/browserFileTreeAccessors.ts +245 -0
  43. package/src/utils/fileImport.ts +334 -0
  44. package/src/utils/filterResources.ts +677 -0
  45. package/src/utils/tsResIntegration.ts +467 -0
  46. package/tailwind.config.js +21 -0
  47. package/test-data/README.md +184 -0
  48. package/test-data/config-variations/README.md +64 -0
  49. package/test-data/config-variations/composition-terminology.json +112 -0
  50. package/test-data/config-variations/enterprise-complex.json +129 -0
  51. package/test-data/config-variations/gaming-app.json +87 -0
  52. package/test-data/config-variations/high-priority-language.json +65 -0
  53. package/test-data/config-variations/minimal-basic.json +37 -0
  54. package/test-data/config-variations/qualifier-default-values.json +71 -0
  55. package/test-data/config-variations/territory-first.json +68 -0
  56. package/test-data/config-variations/territory-hierarchy-test.json +31 -0
  57. package/test-data/config-variations/territory-hierarchy-unconstrained.json +32 -0
  58. package/test-data/default-matching-test/greeting.res.json +83 -0
  59. package/test-data/default-matching-test/only-defaults.res.json +48 -0
  60. package/test-data/sample-project/package.json +27 -0
  61. package/test-data/sample-project/src/resources/app-config.json +27 -0
  62. package/test-data/sample-project/src/resources/app.json +177 -0
  63. package/test-data/sample-project/src/resources/config/app.env=development.json +21 -0
  64. package/test-data/sample-project/src/resources/config/app.json +21 -0
  65. package/test-data/sample-project/src/resources/config/app.platform=mobile.json +23 -0
  66. package/test-data/sample-project/src/resources/feature-flags.json +23 -0
  67. package/test-data/sample-project/src/resources/home=CA/common.json +16 -0
  68. package/test-data/sample-project/src/resources/images/density=hdpi.json +29 -0
  69. package/test-data/sample-project/src/resources/images/home.json +29 -0
  70. package/test-data/sample-project/src/resources/images/logout.json +14 -0
  71. package/test-data/sample-project/src/resources/images/settings.json +14 -0
  72. package/test-data/sample-project/src/resources/images/ui-icons.json +29 -0
  73. package/test-data/sample-project/src/resources/images/user.density=hdpi.json +14 -0
  74. package/test-data/sample-project/src/resources/images/user.json +14 -0
  75. package/test-data/sample-project/src/resources/lang=en-US/common.json +15 -0
  76. package/test-data/sample-project/src/resources/language=en-CA/ui-terms.json +19 -0
  77. package/test-data/sample-project/src/resources/language=en-GB/ui-terms.json +30 -0
  78. package/test-data/sample-project/src/resources/language=fr-CA/ui-terms.json +25 -0
  79. package/test-data/sample-project/src/resources/language=nl-BE/ui-terms.json +17 -0
  80. package/test-data/sample-project/src/resources/region=europe/feature-flags.json +17 -0
  81. package/test-data/sample-project/src/resources/region=north-america/feature-flags.json +18 -0
  82. package/test-data/sample-project/src/resources/strings/dashboard.home=CA,language=fr.json +20 -0
  83. package/test-data/sample-project/src/resources/strings/dashboard.json +18 -0
  84. package/test-data/sample-project/src/resources/strings/dashboard.role=admin.json +23 -0
  85. package/test-data/sample-project/src/resources/strings/dashboard.role=user.json +21 -0
  86. package/test-data/sample-project/src/resources/strings/language=en-US.json +16 -0
  87. package/test-data/sample-project/src/resources/strings.json +78 -0
  88. package/test-data/sample-project/src/resources/territory=BE/app-config.json +19 -0
  89. package/test-data/sample-project/src/resources/territory=CA/app-config.json +21 -0
  90. package/test-data/sample-project/src/resources/territory=GB/app-config.json +25 -0
  91. package/test-data/sample-project/src/resources/territory=NL/app-config.json +25 -0
  92. package/test-data/sample-project/src/resources/ui-terms.json +96 -0
  93. package/test-data/sample-project/src/resources-config.json +111 -0
  94. package/tsconfig.json +27 -0
  95. package/webpack.config.js +54 -0
@@ -0,0 +1,467 @@
1
+ import { Result, succeed, fail, FileTree } from '@fgv/ts-utils';
2
+ import {
3
+ QualifierTypes,
4
+ Qualifiers,
5
+ ResourceTypes,
6
+ Resources,
7
+ Import,
8
+ Runtime,
9
+ ResourceJson,
10
+ Config
11
+ } from '@fgv/ts-res';
12
+ import { ImportedDirectory, ImportedFile } from './fileImport';
13
+ import { BrowserFileTreeAccessors } from './browserFileTreeAccessors';
14
+
15
+ /**
16
+ * Configuration for setting up ts-res system
17
+ */
18
+ export interface TsResConfig {
19
+ qualifierDecls?: Qualifiers.IQualifierDecl[];
20
+ additionalQualifierTypes?: QualifierTypes.QualifierType[];
21
+ additionalResourceTypes?: ResourceTypes.ResourceType[];
22
+ }
23
+
24
+ /**
25
+ * Complete ts-res system setup
26
+ */
27
+ export interface TsResSystem {
28
+ qualifierTypes: QualifierTypes.ReadOnlyQualifierTypeCollector;
29
+ qualifiers: Qualifiers.IReadOnlyQualifierCollector;
30
+ resourceTypes: ResourceTypes.ReadOnlyResourceTypeCollector;
31
+ resourceManager: Resources.ResourceManagerBuilder;
32
+ importManager: Import.ImportManager;
33
+ contextQualifierProvider: Runtime.ValidatingSimpleContextQualifierProvider;
34
+ }
35
+
36
+ /**
37
+ * Result of processing resources
38
+ */
39
+ export interface ProcessedResources {
40
+ system: TsResSystem;
41
+ compiledCollection: ResourceJson.Compiled.ICompiledResourceCollection;
42
+ resolver: Runtime.ResourceResolver;
43
+ resourceCount: number;
44
+ summary: {
45
+ totalResources: number;
46
+ resourceIds: string[];
47
+ errorCount: number;
48
+ warnings: string[];
49
+ };
50
+ }
51
+
52
+ /**
53
+ * Get the default system configuration from ts-res library
54
+ */
55
+ export function getDefaultSystemConfiguration(): Config.Model.ISystemConfiguration {
56
+ const defaultResult = Config.getPredefinedDeclaration('default');
57
+ if (defaultResult.isSuccess()) {
58
+ return defaultResult.value;
59
+ }
60
+
61
+ // Fallback configuration if predefined default is not available
62
+ return {
63
+ name: 'Browser Default Configuration',
64
+ description: 'Fallback default configuration for ts-res browser tool',
65
+ qualifierTypes: [
66
+ {
67
+ name: 'language',
68
+ systemType: 'language',
69
+ configuration: {
70
+ allowContextList: true
71
+ }
72
+ },
73
+ {
74
+ name: 'territory',
75
+ systemType: 'territory',
76
+ configuration: {
77
+ allowContextList: false
78
+ }
79
+ }
80
+ ],
81
+ qualifiers: [
82
+ {
83
+ name: 'currentTerritory',
84
+ token: 'geo',
85
+ typeName: 'territory',
86
+ defaultPriority: 850
87
+ },
88
+ {
89
+ name: 'language',
90
+ token: 'lang',
91
+ typeName: 'language',
92
+ defaultPriority: 800
93
+ }
94
+ ],
95
+ resourceTypes: [
96
+ {
97
+ name: 'json',
98
+ typeName: 'json'
99
+ }
100
+ ]
101
+ };
102
+ }
103
+
104
+ /**
105
+ * Default system configuration for ts-res browser tool
106
+ * Now uses the predefined default from ts-res library
107
+ */
108
+ export const DEFAULT_SYSTEM_CONFIGURATION: Config.Model.ISystemConfiguration =
109
+ getDefaultSystemConfiguration();
110
+
111
+ /**
112
+ * Get all available predefined configuration options
113
+ */
114
+ export function getAvailablePredefinedConfigurations(): {
115
+ name: Config.PredefinedSystemConfiguration;
116
+ label: string;
117
+ description: string;
118
+ }[] {
119
+ return [
120
+ {
121
+ name: 'default',
122
+ label: 'Default',
123
+ description: 'Default system configuration'
124
+ },
125
+ {
126
+ name: 'language-priority',
127
+ label: 'Language Priority',
128
+ description: 'Language priority system configuration'
129
+ },
130
+ {
131
+ name: 'territory-priority',
132
+ label: 'Territory Priority',
133
+ description: 'Territory priority system configuration'
134
+ },
135
+ {
136
+ name: 'extended-example',
137
+ label: 'Extended Example',
138
+ description: 'Extended example configuration with market, role, environment, and currency qualifiers'
139
+ }
140
+ ];
141
+ }
142
+
143
+ /**
144
+ * Get a predefined configuration by name
145
+ */
146
+ export function getPredefinedConfiguration(
147
+ name: Config.PredefinedSystemConfiguration
148
+ ): Result<Config.Model.ISystemConfiguration> {
149
+ return Config.getPredefinedDeclaration(name);
150
+ }
151
+
152
+ /**
153
+ * Legacy default qualifier declarations for backward compatibility
154
+ */
155
+ export const DEFAULT_QUALIFIER_DECLARATIONS: Qualifiers.IQualifierDecl[] = [
156
+ {
157
+ name: 'homeTerritory',
158
+ typeName: 'territory',
159
+ defaultPriority: 800,
160
+ token: 'home'
161
+ },
162
+ {
163
+ name: 'currentTerritory',
164
+ typeName: 'territory',
165
+ defaultPriority: 700
166
+ },
167
+ {
168
+ name: 'language',
169
+ typeName: 'language',
170
+ defaultPriority: 600,
171
+ token: 'lang'
172
+ },
173
+ {
174
+ name: 'role',
175
+ typeName: 'literal',
176
+ defaultPriority: 550
177
+ },
178
+ {
179
+ name: 'env',
180
+ typeName: 'literal',
181
+ defaultPriority: 500
182
+ },
183
+ {
184
+ name: 'platform',
185
+ typeName: 'literal',
186
+ defaultPriority: 450
187
+ },
188
+ {
189
+ name: 'density',
190
+ typeName: 'literal',
191
+ defaultPriority: 400
192
+ }
193
+ ];
194
+
195
+ /**
196
+ * Creates a complete ts-res system setup using SystemConfiguration
197
+ */
198
+ export function createTsResSystemFromConfig(
199
+ systemConfig?: Config.Model.ISystemConfiguration
200
+ ): Result<TsResSystem> {
201
+ const configToUse = systemConfig ?? DEFAULT_SYSTEM_CONFIGURATION;
202
+
203
+ return Config.SystemConfiguration.create(configToUse).onSuccess((systemConfiguration) => {
204
+ try {
205
+ // Set up resource manager
206
+ const resourceManager = Resources.ResourceManagerBuilder.create({
207
+ qualifiers: systemConfiguration.qualifiers,
208
+ resourceTypes: systemConfiguration.resourceTypes
209
+ }).orThrow();
210
+
211
+ // Set up import manager
212
+ const importManager = Import.ImportManager.create({
213
+ resources: resourceManager
214
+ }).orThrow();
215
+
216
+ // Set up context qualifier provider
217
+ const contextQualifierProvider = Runtime.ValidatingSimpleContextQualifierProvider.create({
218
+ qualifiers: systemConfiguration.qualifiers
219
+ }).orThrow();
220
+
221
+ return succeed({
222
+ qualifierTypes: systemConfiguration.qualifierTypes,
223
+ qualifiers: systemConfiguration.qualifiers,
224
+ resourceTypes: systemConfiguration.resourceTypes,
225
+ resourceManager,
226
+ importManager,
227
+ contextQualifierProvider
228
+ });
229
+ } catch (error) {
230
+ return fail(
231
+ `Failed to create ts-res system: ${error instanceof Error ? error.message : String(error)}`
232
+ );
233
+ }
234
+ });
235
+ }
236
+
237
+ /**
238
+ * Legacy function - creates a complete ts-res system setup using manual construction
239
+ * @deprecated Use createTsResSystemFromConfig instead
240
+ */
241
+ export function createTsResSystem(config: TsResConfig = {}): Result<TsResSystem> {
242
+ try {
243
+ // Set up qualifier types
244
+ const defaultQualifierTypes = [
245
+ QualifierTypes.LanguageQualifierType.create().orThrow(),
246
+ QualifierTypes.TerritoryQualifierType.create().orThrow(),
247
+ QualifierTypes.LiteralQualifierType.create().orThrow()
248
+ ];
249
+
250
+ const qualifierTypes = QualifierTypes.QualifierTypeCollector.create({
251
+ qualifierTypes: [...defaultQualifierTypes, ...(config.additionalQualifierTypes ?? [])]
252
+ }).orThrow();
253
+
254
+ // Set up qualifiers
255
+ const qualifierDecls = config.qualifierDecls ?? DEFAULT_QUALIFIER_DECLARATIONS;
256
+ const qualifiers = Qualifiers.QualifierCollector.create({
257
+ qualifierTypes,
258
+ qualifiers: qualifierDecls
259
+ }).orThrow();
260
+
261
+ // Set up resource types
262
+ const defaultResourceTypes = [ResourceTypes.JsonResourceType.create().orThrow()];
263
+
264
+ const resourceTypes = ResourceTypes.ResourceTypeCollector.create({
265
+ resourceTypes: [...defaultResourceTypes, ...(config.additionalResourceTypes ?? [])]
266
+ }).orThrow();
267
+
268
+ // Set up resource manager
269
+ const resourceManager = Resources.ResourceManagerBuilder.create({
270
+ qualifiers,
271
+ resourceTypes
272
+ }).orThrow();
273
+
274
+ // Set up import manager
275
+ const importManager = Import.ImportManager.create({
276
+ resources: resourceManager
277
+ }).orThrow();
278
+
279
+ // Set up context qualifier provider
280
+ const contextQualifierProvider = Runtime.ValidatingSimpleContextQualifierProvider.create({
281
+ qualifiers
282
+ }).orThrow();
283
+
284
+ return succeed({
285
+ qualifierTypes,
286
+ qualifiers,
287
+ resourceTypes,
288
+ resourceManager,
289
+ importManager,
290
+ contextQualifierProvider
291
+ });
292
+ } catch (error) {
293
+ return fail(`Failed to create ts-res system: ${error instanceof Error ? error.message : String(error)}`);
294
+ }
295
+ }
296
+
297
+ /**
298
+ * Processes imported files through ts-res ImportManager
299
+ */
300
+ export function processImportedFiles(
301
+ files: ImportedFile[],
302
+ systemConfigOrSystem?: Config.Model.ISystemConfiguration | TsResSystem
303
+ ): Result<ProcessedResources> {
304
+ if (files.length === 0) {
305
+ return fail('No files provided for processing');
306
+ }
307
+
308
+ // Determine if we have a system or a config
309
+ const systemResult =
310
+ systemConfigOrSystem &&
311
+ 'qualifierTypes' in systemConfigOrSystem &&
312
+ 'resourceManager' in systemConfigOrSystem
313
+ ? succeed(systemConfigOrSystem as TsResSystem)
314
+ : createTsResSystemFromConfig(systemConfigOrSystem as Config.Model.ISystemConfiguration);
315
+
316
+ return systemResult.onSuccess((tsResSystem) => {
317
+ // Convert ImportedFile[] to IInMemoryFile[] format
318
+ const inMemoryFiles = files.map((file) => ({
319
+ path: file.path,
320
+ contents: file.content
321
+ }));
322
+
323
+ return FileTree.inMemory(inMemoryFiles)
324
+ .onSuccess((fileTree) => {
325
+ return Import.ImportManager.create({
326
+ fileTree,
327
+ resources: tsResSystem.resourceManager
328
+ });
329
+ })
330
+ .onSuccess<ProcessedResources>((importManager) => {
331
+ // Import each file using its filesystem path
332
+ for (const file of files) {
333
+ const importResult = importManager.importFromFileSystem(file.path);
334
+ if (importResult.isFailure()) {
335
+ return fail(`Failed to import file ${file.path}: ${importResult.message}`);
336
+ }
337
+ }
338
+ // Update the system with the new ImportManager
339
+ const updatedSystem = {
340
+ ...tsResSystem,
341
+ importManager
342
+ };
343
+ return finalizeProcessing(updatedSystem);
344
+ })
345
+ .withErrorFormat((message) => `processImportedFiles failed: ${message}`);
346
+ });
347
+ }
348
+
349
+ export function processImportedDirectory(
350
+ directory: ImportedDirectory,
351
+ systemConfigOrSystem?: Config.Model.ISystemConfiguration | TsResSystem
352
+ ): Result<ProcessedResources> {
353
+ // Determine if we have a system or a config
354
+ const systemResult =
355
+ systemConfigOrSystem &&
356
+ 'qualifierTypes' in systemConfigOrSystem &&
357
+ 'resourceManager' in systemConfigOrSystem
358
+ ? succeed(systemConfigOrSystem as TsResSystem)
359
+ : createTsResSystemFromConfig(systemConfigOrSystem as Config.Model.ISystemConfiguration);
360
+
361
+ return systemResult.onSuccess((tsResSystem) => {
362
+ // Create custom FileTree that preserves directory structure
363
+ return BrowserFileTreeAccessors.create(directory)
364
+ .onSuccess((accessors) => {
365
+ return FileTree.FileTree.create(accessors);
366
+ })
367
+ .onSuccess((fileTree) => {
368
+ return Import.ImportManager.create({
369
+ fileTree,
370
+ resources: tsResSystem.resourceManager
371
+ });
372
+ })
373
+ .onSuccess<ProcessedResources>((importManager) => {
374
+ // Import from root path since we set the imported directory as the FileTree root
375
+ // The importer will traverse the tree structure and extract qualifiers from directory names
376
+ const importResult = importManager.importFromFileSystem('/');
377
+ if (importResult.isFailure()) {
378
+ return fail(`Failed to import directory ${directory.name}: ${importResult.message}`);
379
+ }
380
+
381
+ // Update the system with the new ImportManager
382
+ const updatedSystem = {
383
+ ...tsResSystem,
384
+ importManager
385
+ };
386
+ return finalizeProcessing(updatedSystem);
387
+ })
388
+ .withErrorFormat((message) => `processImportedDirectory failed: ${message}`);
389
+ });
390
+ }
391
+
392
+ /**
393
+ * Finalizes processing and creates compiled resources
394
+ */
395
+ function finalizeProcessing(system: TsResSystem): Result<ProcessedResources> {
396
+ console.log('=== FINALIZING PROCESSING ===');
397
+ console.log('Resource manager resources:', system.resourceManager.resources.size);
398
+ console.log('Resource manager resource keys:', Array.from(system.resourceManager.resources.keys()));
399
+
400
+ return system.resourceManager
401
+ .getCompiledResourceCollection({ includeMetadata: true })
402
+ .onSuccess((compiledCollection: ResourceJson.Compiled.ICompiledResourceCollection) => {
403
+ console.log('=== COMPILED COLLECTION CREATED ===');
404
+ console.log('Compiled collection data:', compiledCollection);
405
+ console.log('Decisions length:', compiledCollection.decisions?.length);
406
+ console.log('ConditionSets length:', compiledCollection.conditionSets?.length);
407
+ console.log('Conditions length:', compiledCollection.conditions?.length);
408
+ console.log('Resources length:', compiledCollection.resources?.length);
409
+
410
+ return Runtime.ResourceResolver.create({
411
+ resourceManager: system.resourceManager,
412
+ qualifierTypes: system.qualifierTypes,
413
+ contextQualifierProvider: system.contextQualifierProvider
414
+ }).onSuccess((resolver) => {
415
+ // Create summary
416
+ const resourceIds = Array.from(system.resourceManager.resources.keys());
417
+ const summary = {
418
+ totalResources: resourceIds.length,
419
+ resourceIds,
420
+ errorCount: 0, // TODO: Track errors during processing
421
+ warnings: [] // TODO: Collect warnings during processing
422
+ };
423
+
424
+ console.log('=== FINALIZATION COMPLETE ===');
425
+ console.log('Resource count:', resourceIds.length);
426
+ console.log('Resource IDs:', resourceIds);
427
+
428
+ return succeed({
429
+ system,
430
+ compiledCollection,
431
+ resolver,
432
+ resourceCount: resourceIds.length,
433
+ summary
434
+ });
435
+ });
436
+ })
437
+ .withErrorFormat((message: string) => `Failed to finalize processing: ${message}`);
438
+ }
439
+
440
+ /**
441
+ * Creates a simple context for resource resolution
442
+ */
443
+ export function createSimpleContext(
444
+ contextValues: Record<string, string>,
445
+ system: TsResSystem
446
+ ): Result<Runtime.ValidatingSimpleContextQualifierProvider> {
447
+ return Runtime.ValidatingSimpleContextQualifierProvider.create({
448
+ qualifiers: system.qualifiers
449
+ })
450
+ .onSuccess((provider): Result<Runtime.ValidatingSimpleContextQualifierProvider> => {
451
+ // Set context values
452
+ for (const [qualifierName, value] of Object.entries(contextValues)) {
453
+ try {
454
+ provider.set(qualifierName as any, value as any);
455
+ } catch (error) {
456
+ return fail(
457
+ `Failed to set context value ${qualifierName}=${value}: ${
458
+ error instanceof Error ? error.message : String(error)
459
+ }`
460
+ );
461
+ }
462
+ }
463
+
464
+ return succeed(provider);
465
+ })
466
+ .withErrorFormat((message) => `Failed to create context: ${message}`);
467
+ }
@@ -0,0 +1,21 @@
1
+ /** @type {import('tailwindcss').Config} */
2
+ module.exports = {
3
+ content: [
4
+ './src/**/*.{js,jsx,ts,tsx}',
5
+ './public/index.html',
6
+ './src/components/**/*.{js,jsx,ts,tsx}',
7
+ './src/hooks/**/*.{js,jsx,ts,tsx}',
8
+ './src/utils/**/*.{js,jsx,ts,tsx}'
9
+ ],
10
+ theme: {
11
+ extend: {
12
+ colors: {
13
+ // Custom colors for the ts-res browser
14
+ 'res-primary': '#2563eb',
15
+ 'res-secondary': '#1e40af',
16
+ 'res-accent': '#3b82f6'
17
+ }
18
+ }
19
+ },
20
+ plugins: []
21
+ };
@@ -0,0 +1,184 @@
1
+ # TS-RES Test Data
2
+
3
+ This directory contains test data for the ts-res-browser tool that demonstrates all ts-res file formats and naming conventions.
4
+
5
+ ## Structure
6
+
7
+ The test data demonstrates three different JSON file formats supported by ts-res:
8
+
9
+ ### 1. Simple JSON Files (Auto-converted to Resource Candidates)
10
+ Individual JSON files with plain resource values that the ImportManager automatically converts to resource candidates.
11
+
12
+ ### 2. Resource Collection Format (`IResourceCollectionDecl`)
13
+ JSON files that explicitly define multiple resources with their candidates and conditions.
14
+
15
+ ### 3. Resource Tree Format (`IResourceTreeRootDecl`)
16
+ JSON files that define hierarchical resource structures with nested organization.
17
+
18
+ ## Directory Structure
19
+
20
+ ```
21
+ src/resources/
22
+ ├── strings.json # Resource Collection (common, errors resources)
23
+ ├── app.json # Resource Tree (app.config, app.ui.dashboard, app.ui.icons.* resources)
24
+ ├── strings/
25
+ │ ├── common.json # Simple JSON → "common" resource
26
+ │ ├── common.language=es.json # Simple JSON → "common" resource (Spanish)
27
+ │ ├── common.language=fr.json # Simple JSON → "common" resource (French)
28
+ │ ├── language=en-US.json # Simple JSON → "language" resource
29
+ │ ├── dashboard.json # Simple JSON → "dashboard" resource
30
+ │ ├── dashboard.role=user.json # Simple JSON → "dashboard" resource (user)
31
+ │ ├── dashboard.role=admin.json # Simple JSON → "dashboard" resource (admin)
32
+ │ ├── dashboard.home=CA,language=fr.json # Simple JSON → "dashboard" resource (CA+FR)
33
+ │ └── errors.json # Simple JSON → "errors" resource
34
+ ├── config/
35
+ │ ├── app.json # Simple JSON → "app" resource
36
+ │ ├── app.env=development.json # Simple JSON → "app" resource (dev)
37
+ │ └── app.platform=mobile.json # Simple JSON → "app" resource (mobile)
38
+ ├── images/
39
+ │ ├── home.json # Simple JSON → "home" resource
40
+ │ ├── home.density=hdpi.json # Simple JSON → "home" resource (hdpi)
41
+ │ ├── user.json # Simple JSON → "user" resource
42
+ │ ├── user.density=hdpi.json # Simple JSON → "user" resource (hdpi)
43
+ │ ├── settings.json # Simple JSON → "settings" resource
44
+ │ ├── settings.density=hdpi.json # Simple JSON → "settings" resource (hdpi)
45
+ │ ├── logout.json # Simple JSON → "logout" resource
46
+ │ └── logout.density=hdpi.json # Simple JSON → "logout" resource (hdpi)
47
+ ├── en-US/
48
+ │ └── common.json # Simple JSON → "common" resource (en-US context)
49
+ ├── home=CA/
50
+ │ └── common.json # Simple JSON → "common" resource (CA context)
51
+ └── import-config.json # TS-RES configuration
52
+ ```
53
+
54
+ ## Format Examples
55
+
56
+ ### Simple JSON Format
57
+ ```json
58
+ {
59
+ "welcome": "Welcome to the application",
60
+ "hello": "Hello",
61
+ "goodbye": "Goodbye"
62
+ }
63
+ ```
64
+ *Auto-converted to resource candidate by ImportManager*
65
+
66
+ ### Resource Collection Format
67
+ ```json
68
+ {
69
+ "resources": [
70
+ {
71
+ "id": "common",
72
+ "resourceTypeName": "json",
73
+ "candidates": [
74
+ {
75
+ "json": {
76
+ "welcome": "Welcome to the application",
77
+ "hello": "Hello"
78
+ }
79
+ },
80
+ {
81
+ "json": {
82
+ "welcome": "Bienvenido a la aplicación",
83
+ "hello": "Hola"
84
+ },
85
+ "conditions": {
86
+ "language": "es"
87
+ }
88
+ }
89
+ ]
90
+ }
91
+ ]
92
+ }
93
+ ```
94
+
95
+ ### Resource Tree Format
96
+ ```json
97
+ {
98
+ "context": {
99
+ "baseId": "app"
100
+ },
101
+ "resources": {
102
+ "config": {
103
+ "resourceTypeName": "json",
104
+ "candidates": [
105
+ {
106
+ "json": {
107
+ "app_name": "Sample Application",
108
+ "version": "1.0.0"
109
+ }
110
+ }
111
+ ]
112
+ }
113
+ },
114
+ "children": {
115
+ "ui": {
116
+ "resources": {
117
+ "dashboard": {
118
+ "resourceTypeName": "json",
119
+ "candidates": [
120
+ {
121
+ "json": {
122
+ "title": "Dashboard",
123
+ "overview": "Overview"
124
+ }
125
+ }
126
+ ]
127
+ }
128
+ }
129
+ }
130
+ }
131
+ }
132
+ ```
133
+
134
+ ## Resources Generated
135
+
136
+ The test data creates these resources:
137
+
138
+ ### From Simple JSON Files
139
+ - `common` (4 candidates: base, Spanish, French, directory-based)
140
+ - `dashboard` (4 candidates: base, user, admin, CA+French)
141
+ - `errors` (1 candidate: base)
142
+ - `language` (1 candidate: en-US)
143
+ - `app` (3 candidates: base, dev, mobile)
144
+ - `home`, `user`, `settings`, `logout` (2 candidates each: standard, hdpi)
145
+
146
+ ### From Resource Collection (strings.json)
147
+ - `common` (3 candidates: base, Spanish, French)
148
+ - `errors` (1 candidate: base)
149
+
150
+ ### From Resource Tree (app.json)
151
+ - `app.config` (3 candidates: base, dev, mobile)
152
+ - `app.ui.dashboard` (3 candidates: base, user, admin)
153
+ - `app.ui.icons.home` (2 candidates: standard, hdpi)
154
+ - `app.ui.icons.user` (2 candidates: standard, hdpi)
155
+
156
+ ## Qualifiers Demonstrated
157
+
158
+ - **language**: `es`, `fr`, `en-US` (language codes)
159
+ - **homeTerritory**: `CA` (with `home=` token, optional)
160
+ - **role**: `user`, `admin` (literal values)
161
+ - **env**: `development` (literal values)
162
+ - **platform**: `mobile` (literal values)
163
+ - **density**: `hdpi` (literal values)
164
+
165
+ ## Import Configuration
166
+
167
+ The `import-config.json` file defines:
168
+ - **Qualifier Types**: language, territory, literal
169
+ - **Qualifiers**: language, homeTerritory, currentTerritory, role, env, platform, density
170
+ - **Resource Types**: json
171
+
172
+ ## Usage
173
+
174
+ This test data demonstrates:
175
+
176
+ 1. **Simple JSON Import**: Basic file-to-resource conversion
177
+ 2. **Resource Collections**: Multiple resources with candidates in one file
178
+ 3. **Resource Trees**: Hierarchical organization with nested resources
179
+ 4. **Qualifier Parsing**: File-based and explicit condition formats
180
+ 5. **Multi-condition Resources**: Complex qualifier combinations
181
+ 6. **Directory-based Organization**: Conditional directories
182
+ 7. **Mixed Formats**: Using different formats together
183
+
184
+ The data follows ts-res patterns and provides comprehensive examples for testing all browser tool features.