@intlayer/chokidar 2.0.0 → 2.0.1

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 (45) hide show
  1. package/dist/cjs/transpiler/declaration_file_to_dictionary/i18n_dictionary/buildI18nDictionary.cjs.map +1 -1
  2. package/dist/cjs/transpiler/declaration_file_to_dictionary/intlayer_dictionary/buildIntlayerDictionary.cjs +3 -3
  3. package/dist/cjs/transpiler/declaration_file_to_dictionary/intlayer_dictionary/buildIntlayerDictionary.cjs.map +1 -1
  4. package/dist/cjs/transpiler/declaration_file_to_dictionary/intlayer_dictionary/extractNestedJSON.cjs.map +1 -1
  5. package/dist/cjs/transpiler/declaration_file_to_dictionary/intlayer_dictionary/extractNestedJSON.d.ts +2 -2
  6. package/dist/cjs/transpiler/declaration_file_to_dictionary/intlayer_dictionary/loadContentDeclaration.cjs.map +1 -1
  7. package/dist/cjs/transpiler/declaration_file_to_dictionary/intlayer_dictionary/loadContentDeclaration.d.ts +2 -2
  8. package/dist/cjs/transpiler/declaration_file_to_dictionary/intlayer_dictionary/processContentDeclaration.cjs +8 -1
  9. package/dist/cjs/transpiler/declaration_file_to_dictionary/intlayer_dictionary/processContentDeclaration.cjs.map +1 -1
  10. package/dist/cjs/transpiler/declaration_file_to_dictionary/intlayer_dictionary/processContentDeclaration.d.ts +3 -3
  11. package/dist/cjs/transpiler/dictionary_to_type/createType.cjs +38 -52
  12. package/dist/cjs/transpiler/dictionary_to_type/createType.cjs.map +1 -1
  13. package/dist/cjs/transpiler/dictionary_to_type/createType.d.ts +3 -3
  14. package/dist/esm/transpiler/declaration_file_to_dictionary/i18n_dictionary/buildI18nDictionary.mjs.map +1 -1
  15. package/dist/esm/transpiler/declaration_file_to_dictionary/intlayer_dictionary/buildIntlayerDictionary.mjs +3 -3
  16. package/dist/esm/transpiler/declaration_file_to_dictionary/intlayer_dictionary/buildIntlayerDictionary.mjs.map +1 -1
  17. package/dist/esm/transpiler/declaration_file_to_dictionary/intlayer_dictionary/extractNestedJSON.d.mts +2 -2
  18. package/dist/esm/transpiler/declaration_file_to_dictionary/intlayer_dictionary/extractNestedJSON.mjs.map +1 -1
  19. package/dist/esm/transpiler/declaration_file_to_dictionary/intlayer_dictionary/loadContentDeclaration.d.mts +2 -2
  20. package/dist/esm/transpiler/declaration_file_to_dictionary/intlayer_dictionary/loadContentDeclaration.mjs.map +1 -1
  21. package/dist/esm/transpiler/declaration_file_to_dictionary/intlayer_dictionary/processContentDeclaration.d.mts +3 -3
  22. package/dist/esm/transpiler/declaration_file_to_dictionary/intlayer_dictionary/processContentDeclaration.mjs +8 -1
  23. package/dist/esm/transpiler/declaration_file_to_dictionary/intlayer_dictionary/processContentDeclaration.mjs.map +1 -1
  24. package/dist/esm/transpiler/dictionary_to_type/createType.d.mts +3 -3
  25. package/dist/esm/transpiler/dictionary_to_type/createType.mjs +38 -52
  26. package/dist/esm/transpiler/dictionary_to_type/createType.mjs.map +1 -1
  27. package/package.json +4 -6
  28. package/src/chokidar/index.ts +0 -1
  29. package/src/chokidar/watcher.ts +0 -75
  30. package/src/transpiler/declaration_file_to_dictionary/i18n_dictionary/buildI18nDictionary.ts +0 -95
  31. package/src/transpiler/declaration_file_to_dictionary/i18n_dictionary/convertContentDeclarationInto18nDictionaries.ts +0 -91
  32. package/src/transpiler/declaration_file_to_dictionary/i18n_dictionary/convertPluralsValues.ts +0 -22
  33. package/src/transpiler/declaration_file_to_dictionary/i18n_dictionary/index.ts +0 -1
  34. package/src/transpiler/declaration_file_to_dictionary/index.ts +0 -21
  35. package/src/transpiler/declaration_file_to_dictionary/intlayer_dictionary/buildIntlayerDictionary.ts +0 -70
  36. package/src/transpiler/declaration_file_to_dictionary/intlayer_dictionary/extractNestedJSON.ts +0 -60
  37. package/src/transpiler/declaration_file_to_dictionary/intlayer_dictionary/index.ts +0 -3
  38. package/src/transpiler/declaration_file_to_dictionary/intlayer_dictionary/loadContentDeclaration.ts +0 -114
  39. package/src/transpiler/declaration_file_to_dictionary/intlayer_dictionary/processContentDeclaration.ts +0 -65
  40. package/src/transpiler/dictionary_to_main/createDictionaryList.ts +0 -65
  41. package/src/transpiler/dictionary_to_main/index.ts +0 -1
  42. package/src/transpiler/dictionary_to_type/createModuleAugmentation.ts +0 -102
  43. package/src/transpiler/dictionary_to_type/createType.ts +0 -162
  44. package/src/transpiler/dictionary_to_type/index.ts +0 -2
  45. package/src/utils.ts +0 -26
@@ -1,114 +0,0 @@
1
- /* eslint-disable @typescript-eslint/no-var-requires */
2
- import { createRequire } from 'module';
3
- import { type Context, runInNewContext } from 'vm';
4
- import type { ContentModule } from '@intlayer/core';
5
- import { type BuildOptions, buildSync, type BuildResult } from 'esbuild';
6
-
7
- const isESModule = typeof import.meta.url === 'string';
8
-
9
- const sandboxContext: Context = {
10
- exports: {
11
- default: {},
12
- },
13
- module: {
14
- exports: {},
15
- },
16
- console,
17
- require: isESModule ? createRequire(import.meta.url) : require,
18
- };
19
-
20
- const transformationOption: BuildOptions = {
21
- loader: {
22
- '.js': 'js',
23
- '.jsx': 'jsx',
24
- '.mjs': 'js',
25
- '.ts': 'ts',
26
- '.tsx': 'tsx',
27
- '.cjs': 'js',
28
- '.json': 'json',
29
- },
30
- format: 'cjs', // Output format as commonjs
31
- target: 'es2017',
32
- packages: 'external',
33
- write: false,
34
- bundle: true,
35
- };
36
-
37
- const filterValidContentDeclaration = (
38
- contentDeclaration: ContentModule
39
- ): ContentModule => {
40
- // @TODO Implement filtering of valid content declaration
41
- return contentDeclaration;
42
- };
43
-
44
- /**
45
- * Load the content declaration from the given path
46
- *
47
- * Accepts JSON, JS, MJS and TS files as configuration
48
- */
49
- export const loadContentDeclaration = (
50
- contentDeclarationFilePath: string
51
- ): ContentModule | undefined => {
52
- let contentDeclaration: ContentModule | undefined = undefined;
53
-
54
- const fileExtension = contentDeclarationFilePath.split('.').pop() ?? '';
55
-
56
- try {
57
- if (fileExtension === 'json') {
58
- // Assume JSON
59
- return require(contentDeclarationFilePath);
60
- }
61
-
62
- // Rest is JS, MJS or TS
63
-
64
- const moduleResult: BuildResult = buildSync({
65
- entryPoints: [contentDeclarationFilePath],
66
-
67
- ...transformationOption,
68
- });
69
-
70
- const moduleResultString = moduleResult.outputFiles?.[0].text;
71
-
72
- if (!moduleResultString) {
73
- console.error('Configuration file could not be loaded.');
74
- return undefined;
75
- }
76
-
77
- runInNewContext(moduleResultString, sandboxContext);
78
-
79
- if (
80
- sandboxContext.exports.default &&
81
- Object.keys(sandboxContext.exports.default).length > 0
82
- ) {
83
- // ES Module
84
- contentDeclaration = sandboxContext.exports.default;
85
- } else if (
86
- sandboxContext.module.exports.defaults &&
87
- Object.keys(sandboxContext.module.exports.defaults).length > 0
88
- ) {
89
- // CommonJS
90
- contentDeclaration = sandboxContext.module.exports.default;
91
- } else if (
92
- sandboxContext.module.exports.default &&
93
- Object.keys(sandboxContext.module.exports.default).length > 0
94
- ) {
95
- // ES Module
96
- contentDeclaration = sandboxContext.module.exports.default;
97
- } else if (
98
- sandboxContext.module.exports &&
99
- Object.keys(sandboxContext.module.exports).length > 0
100
- ) {
101
- // Other
102
- contentDeclaration = sandboxContext.module.exports;
103
- }
104
-
105
- if (typeof contentDeclaration === 'undefined') {
106
- console.error('Configuration file could not be loaded.');
107
- return undefined;
108
- }
109
-
110
- return filterValidContentDeclaration(contentDeclaration);
111
- } catch (error) {
112
- console.error('Error:', error);
113
- }
114
- };
@@ -1,65 +0,0 @@
1
- import { resolve } from 'path';
2
- import type {
3
- Content,
4
- ContentModule,
5
- FlatContent,
6
- FlatContentValue,
7
- } from '@intlayer/core';
8
- import { loadContentDeclaration } from './loadContentDeclaration';
9
-
10
- /**
11
- * Function to replace function and async function fields with their results in the object
12
- */
13
- const processFunctionResults = async (entry: Content): Promise<FlatContent> => {
14
- if (entry && typeof entry === 'object') {
15
- const promises: Promise<void>[] = [];
16
- const result: FlatContent = {};
17
-
18
- for (const key of Object.keys(entry)) {
19
- const field = entry?.[key];
20
-
21
- if (typeof field === 'object') {
22
- result[key] = (await processFunctionResults(
23
- field as Content
24
- )) as FlatContentValue;
25
- } else if (typeof field === 'function') {
26
- // Wait for the function to resolve if it's an async function
27
- const promise = (async () => {
28
- // Execute the function and await the result if it's a Promise
29
- const value = await field();
30
-
31
- result[key] = value as FlatContentValue;
32
- })();
33
- promises.push(promise);
34
- } else {
35
- result[key] = field as FlatContentValue;
36
- }
37
- }
38
-
39
- // Wait for all async operations to complete
40
- await Promise.all(promises);
41
-
42
- return result;
43
- }
44
-
45
- return entry;
46
- };
47
-
48
- /**
49
- * Function to load, process the module and return the Intlayer ContentModule from the module file
50
- */
51
- export const processContentDeclaration = async (file: string) => {
52
- try {
53
- const functionPath = resolve(file);
54
- const entry = loadContentDeclaration(functionPath);
55
-
56
- if (!entry) {
57
- console.error('No entry found in module:', functionPath);
58
- return;
59
- }
60
-
61
- return (await processFunctionResults(entry)) as ContentModule;
62
- } catch (error) {
63
- console.error('Error processing module:', error);
64
- }
65
- };
@@ -1,65 +0,0 @@
1
- import { existsSync, mkdirSync, writeFileSync } from 'fs';
2
- import { basename, extname, relative, resolve } from 'path';
3
- import { getConfiguration } from '@intlayer/config';
4
- import { sync } from 'glob';
5
- import { getFileHash } from '../../utils';
6
-
7
- const { content } = getConfiguration();
8
- const { dictionariesDir, mainDir } = content;
9
-
10
- /**
11
- * This function generates the content of the dictionary list file
12
- */
13
- const generateDictionaryListContent = (
14
- dictionaries: string[],
15
- format: 'cjs' | 'esm' = 'esm'
16
- ): string => {
17
- let content = '';
18
-
19
- const dictionariesRef = dictionaries.map((dictionaryPath) => ({
20
- relativePath: relative(mainDir, dictionaryPath),
21
- id: basename(dictionaryPath, extname(dictionaryPath)), // Get the base name as the dictionary id
22
- hash: `_${getFileHash(dictionaryPath)}`, // Get the hash of the dictionary to avoid conflicts
23
- }));
24
-
25
- // Import all dictionaries
26
- dictionariesRef.forEach((dictionary) => {
27
- if (format === 'esm')
28
- content += `import ${dictionary.hash} from '${dictionary.relativePath}';\n`;
29
- if (format === 'cjs')
30
- content += `const ${dictionary.hash} = require('${dictionary.relativePath}');\n`;
31
- });
32
-
33
- content += '\n';
34
-
35
- // Format Dictionary Map
36
- const formattedDictionaryMap: string = dictionariesRef
37
- .map((dictionary) => `"${dictionary.id}": ${dictionary.hash}`)
38
- .join(',\n');
39
-
40
- if (format === 'esm')
41
- content += `export default {\n${formattedDictionaryMap}\n};\n`;
42
- if (format === 'cjs')
43
- content += `module.exports = {\n${formattedDictionaryMap}\n};\n`;
44
-
45
- return content;
46
- };
47
-
48
- /**
49
- * This function generates a list of dictionaries in the main directory
50
- */
51
- export const createDictionaryList = () => {
52
- // Create main directory if it doesn't exist
53
- if (!existsSync(mainDir)) {
54
- mkdirSync(mainDir, { recursive: true });
55
- }
56
-
57
- const dictionaries: string[] = sync(`${dictionariesDir}/**/*.json`);
58
-
59
- // Create the dictionary list file
60
- const cjsContent = generateDictionaryListContent(dictionaries, 'cjs');
61
- writeFileSync(resolve(mainDir, 'dictionaries.cjs'), cjsContent);
62
-
63
- const esmContent = generateDictionaryListContent(dictionaries, 'esm');
64
- writeFileSync(resolve(mainDir, 'dictionaries.mjs'), esmContent);
65
- };
@@ -1 +0,0 @@
1
- export * from './createDictionaryList';
@@ -1,102 +0,0 @@
1
- import { existsSync, mkdirSync, writeFileSync } from 'fs';
2
- import { basename, join, relative } from 'path';
3
- import { Locales, getConfiguration } from '@intlayer/config';
4
- import { sync } from 'glob';
5
- import { getFileHash, transformToCamelCase } from '../../utils';
6
-
7
- const { content, internationalization } = getConfiguration();
8
- const { typesDir, moduleAugmentationDir } = content;
9
- const { locales, strictMode } = internationalization;
10
-
11
- export const getTypeName = (id: string): string =>
12
- transformToCamelCase(`${id}Content`);
13
-
14
- /**
15
- * This function generates the content of the module augmentation file
16
- */
17
- const generateTypeIndexContent = (typeFiles: string[]): string => {
18
- let content = "/* eslint-disable */\nimport { Locales } from 'intlayer'\n";
19
-
20
- const dictionariesRef = typeFiles.map((dictionaryPath) => ({
21
- relativePath: relative(moduleAugmentationDir, dictionaryPath),
22
- id: basename(dictionaryPath, '.d.ts'), // Get the base name as the dictionary id
23
- hash: `_${getFileHash(dictionaryPath)}`, // Get the hash of the dictionary to avoid conflicts
24
- }));
25
-
26
- // Import all dictionaries
27
- dictionariesRef.forEach((dictionary) => {
28
- const typeName = getTypeName(dictionary.id);
29
- content += `import type { ${typeName} as ${dictionary.hash} } from '${dictionary.relativePath}';\n`;
30
- });
31
-
32
- content += '\n';
33
-
34
- // Format Dictionary Map
35
- const formattedDictionaryMap: string = dictionariesRef
36
- .map((dictionary) => ` "${dictionary.id}": ${dictionary.hash};`)
37
- .join('\n');
38
-
39
- const formatLocales = locales
40
- .map((locale) => {
41
- for (const key in Locales) {
42
- if (Locales[key as keyof typeof Locales] === locale) {
43
- return `Locales.${key}`;
44
- }
45
- }
46
- })
47
- .join(' | ');
48
-
49
- const strictModeRecord =
50
- strictMode === 'strict'
51
- ? `interface IConfigLocales<Content> extends Record<ExtractedLocales, Content> {}`
52
- : strictMode === 'required_only'
53
- ? `interface IConfigLocales<Content> extends Record<ExtractedLocales, Content>, Partial<Record<ExcludedLocales, Content>> {}`
54
- : `interface IConfigLocales<Content> extends Partial<Record<Locales, Content>> {}`;
55
-
56
- /**
57
- * Write the module augmentation to extend the intlayer module with the dictionaries types
58
- * Will suggest the type resulting of the dictionaries
59
- *
60
- * declare module 'intlayer' {
61
- * interface IntlayerDictionaryTypesConnector = {
62
- * dictionaries: {
63
- * id: DictionaryType;
64
- * }
65
- * }
66
- *
67
- * type ConfigLocales = Locales.ENGLISH | Locales.FRENCH | Locales.SPANISH;
68
- * type ExtractedLocales = Extract<Locales, ConfigLocales>;
69
- * type ExcludedLocales = Exclude<Locales, ConfigLocales>;
70
- *
71
- * interface IConfigLocales<Content> extends Record<ExtractedLocales, Content>, Partial<Record<ExcludedLocales, Content>> {}
72
- *
73
- *
74
- * }
75
- * See https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation
76
- */
77
- content += `declare module 'intlayer' {\n`;
78
- content += ` interface IntlayerDictionaryTypesConnector {\n${formattedDictionaryMap}\n }\n\n`;
79
- content += ` type ConfigLocales = ${formatLocales};\n`;
80
- content += ` type ExtractedLocales = Extract<Locales, ConfigLocales>;\n`;
81
- content += ` type ExcludedLocales = Exclude<Locales, ConfigLocales>;\n\n`;
82
- content += ` ${strictModeRecord}\n`;
83
- content += `}`;
84
-
85
- return content;
86
- };
87
-
88
- /**
89
- * This function generates a index file merging all the types
90
- */
91
- export const createModuleAugmentation = () => {
92
- // Create main directory if it doesn't exist
93
- if (!existsSync(moduleAugmentationDir)) {
94
- mkdirSync(moduleAugmentationDir, { recursive: true });
95
- }
96
-
97
- const dictionaries: string[] = sync(`${typesDir}/**/*.d.ts`);
98
- // Create the dictionary list file
99
-
100
- const tsContent = generateTypeIndexContent(dictionaries);
101
- writeFileSync(join(moduleAugmentationDir, 'intlayer.d.ts'), tsContent);
102
- };
@@ -1,162 +0,0 @@
1
- import { existsSync, mkdirSync, writeFileSync } from 'fs';
2
- import { createRequire } from 'module';
3
- import { resolve } from 'path';
4
- import { getConfiguration } from '@intlayer/config';
5
- import {
6
- NodeType,
7
- type Content,
8
- type ContentModule,
9
- type TypedNode,
10
- } from '@intlayer/core';
11
- import { getTypeName } from './createModuleAugmentation';
12
-
13
- const { content, internationalization } = getConfiguration();
14
- const { typesDir } = content;
15
-
16
- const isESModule = typeof import.meta.url === 'string';
17
- const requireFunction = isESModule ? createRequire(import.meta.url) : require;
18
-
19
- /**
20
- *
21
- * This function generates a TypeScript type definition from a JSON object
22
- *
23
- * Example:
24
- *
25
- * const input = {
26
- * id: '1',
27
- * name: 'John Doe',
28
- * address: {
29
- * id: '2',
30
- * street: '123 Main St',
31
- * city: 'Springfield',
32
- * }
33
- * };
34
- *
35
- * const result = generateTypeScriptType(input, 'RootObject');
36
- * console.log(result);
37
- *
38
- * Output:
39
- *
40
- * type RootObject = {
41
- * id: '1',
42
- * name: string,
43
- * address: {
44
- * id: '2',
45
- * street: string,
46
- * city: string,
47
- * },
48
- * };
49
- *
50
- */
51
- export const generateTypeScriptType = (obj: ContentModule): string => {
52
- let typeDefinition = ``;
53
-
54
- const typeName = getTypeName(obj.id);
55
-
56
- typeDefinition += `export type ${typeName} = {\n`;
57
- typeDefinition += generateTypeScriptTypeContent(obj);
58
- typeDefinition += '};\n\n';
59
-
60
- return typeDefinition;
61
- };
62
-
63
- const isReactNode = (node: Record<string, unknown>): boolean =>
64
- typeof node?.key !== 'undefined' && typeof node?.props !== 'undefined';
65
-
66
- // eslint-disable-next-line sonarjs/cognitive-complexity
67
- export const generateTypeScriptTypeContent = (obj: Content): string => {
68
- if (typeof obj !== 'object' || obj === null) {
69
- return `${typeof obj}`;
70
- }
71
-
72
- const isReactNodeValue = isReactNode(obj as Record<string, unknown>);
73
-
74
- if (isReactNodeValue) {
75
- // ReactNode handling
76
- return `JSX.Element`;
77
- }
78
-
79
- let typeDefinition = ``;
80
- for (const [key, value] of Object.entries(obj)) {
81
- const nodeType: NodeType | undefined = (value as TypedNode)?.nodeType;
82
- type ValueKey = keyof typeof value;
83
-
84
- if (
85
- // Check if the value is a typed node
86
- typeof value === 'object' &&
87
- nodeType === NodeType.Translation
88
- ) {
89
- const tsType = generateTypeScriptTypeContent(
90
- value?.[internationalization.defaultLocale as ValueKey]
91
- );
92
- typeDefinition += ` ${key}: ${tsType},\n`;
93
- } else if (
94
- // Check if the value is a typed node
95
- typeof value === 'object' &&
96
- nodeType === NodeType.Enumeration
97
- ) {
98
- const tsType = generateTypeScriptTypeContent(
99
- value?.[internationalization.defaultLocale as ValueKey] as Content
100
- );
101
-
102
- typeDefinition += ` ${key}: (quantity: number) => ${tsType},\n`;
103
- } else if (
104
- // Check if the value is a nested object
105
- typeof value === 'object'
106
- ) {
107
- const isArray = Array.isArray(value);
108
-
109
- if (isArray) {
110
- // Array handling (simplified, assumes non-empty arrays with uniform type)
111
- const arrayType = generateTypeScriptTypeContent(value as Content);
112
-
113
- typeDefinition += ` ${key}: ${arrayType}[],\n`;
114
- } else {
115
- // Nested object, recurse
116
- const nestedType = generateTypeScriptTypeContent(value as Content);
117
-
118
- typeDefinition += ` ${key}: {${nestedType}},\n`;
119
- }
120
- } else if (
121
- // Check if the value is an 'id'
122
- typeof value === 'string' &&
123
- key === 'id'
124
- ) {
125
- // Special handling for 'id' field
126
- const tsType = `"${value}"`;
127
- typeDefinition += ` ${key}: ${tsType},\n`;
128
- } else {
129
- // Primitive type
130
- const tsType = typeof value;
131
- typeDefinition += ` ${key}: ${tsType},\n`;
132
- }
133
- }
134
-
135
- return typeDefinition;
136
- };
137
-
138
- /**
139
- * This function generates a TypeScript type definition from a JSON object
140
- */
141
- export const createTypes = (dictionariesPaths: string[]): string[] => {
142
- const resultTypesPaths: string[] = [];
143
-
144
- // Create type folders if they don't exist
145
- if (!existsSync(typesDir)) {
146
- mkdirSync(typesDir, { recursive: true });
147
- }
148
-
149
- for (const dictionaryPath of dictionariesPaths) {
150
- const contentModule: ContentModule = requireFunction(dictionaryPath);
151
- const dictionaryName: string = contentModule.id;
152
- const typeDefinition: string = generateTypeScriptType(contentModule);
153
-
154
- const outputPath = resolve(typesDir, `${dictionaryName}.d.ts`);
155
-
156
- writeFileSync(outputPath, typeDefinition);
157
-
158
- resultTypesPaths.push(outputPath);
159
- }
160
-
161
- return resultTypesPaths;
162
- };
@@ -1,2 +0,0 @@
1
- export * from './createModuleAugmentation';
2
- export * from './createType';
package/src/utils.ts DELETED
@@ -1,26 +0,0 @@
1
- import crypto from 'crypto-js';
2
-
3
- export const getFileHash = (filePath: string) => {
4
- const hash = crypto.SHA3(filePath);
5
-
6
- return hash
7
- .toString(crypto.enc.Base64)
8
- .replace(/[^A-Z\d]/gi, '')
9
- .substring(0, 20);
10
- };
11
-
12
- export const transformToCamelCase = (string: string): string => {
13
- // Split the string into words using a regex that finds spaces, hyphens, and underscores
14
- const words = string.split(/[\s\-_]+/);
15
-
16
- // Transform each word except the first to have its first letter uppercase
17
- const camelCasedWords = words.map((word, index) => {
18
- if (index === 0) {
19
- return word[0].toUpperCase() + word.slice(1);
20
- }
21
- return word.charAt(0).toUpperCase() + word.slice(1);
22
- });
23
-
24
- // Join the words back together
25
- return camelCasedWords.join('');
26
- };