Skip to content

Commit 42c744a

Browse files
authored
Convert generated config objects to TS (#959)
* Move config.js to cached assets * Typed config imports * Fix lint error * Fix .gitignore for html books TODO: move as much as possible from static into gen-assets * Resolve CR feedback and lint/build errors * Fix missing config during initial conversion run * Convert firebase config to TS * Convert contents config to TS
1 parent 4d81b7d commit 42c744a

79 files changed

Lines changed: 456 additions & 417 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,8 @@ static/illustrations
1717
static/icons
1818
static/manifest*.json
1919
static/assets
20+
static/collections
2021
static/contents
2122
src/lib/data/catalog.js
22-
src/lib/data/firebase-config.js
23-
src/lib/data/config.js
24-
src/lib/data/contents.js
2523
src/gen-assets
2624
vite.config.js.timestamp*

.prettierignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ node_modules
77
.env.*
88
!.env.example
99
/data
10-
src/lib/data/config.js
10+
src/gen-assets
1111
src/lib/data/catalog.js
1212
src/lib/data/firebase-config.js
1313
static

config/index.d.ts

Lines changed: 56 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,8 @@ export type BookConfig = {
6262
file: string;
6363
hashedFileName?: string; // currently just for HTML books
6464
audio: BookCollectionAudioConfig[];
65-
features: any;
66-
quizFeatures?: Record<string, string | boolean | number>;
65+
features: FeatureConfig;
66+
quizFeatures?: FeatureConfig;
6767
footer?: HTML;
6868
style?: StyleConfig;
6969
styles?: {
@@ -157,13 +157,17 @@ export type MenuItemConfig = {
157157
file: string;
158158
}[];
159159
};
160+
161+
export type FeatureValue = string | boolean | number;
162+
export type FeatureConfig = Record<string, FeatureValue>;
163+
160164
export type AppConfig = {
161165
name?: string;
162166
package?: string;
163167
version?: string;
164-
programVersion?: string;
165-
programType?: string;
166-
mainFeatures?: any;
168+
programVersion: string;
169+
programType: string;
170+
mainFeatures: FeatureConfig;
167171
audio?: AudioConfig;
168172
fonts?: {
169173
name?: string;
@@ -379,3 +383,50 @@ export type Quiz = {
379383
}[];
380384
passScore?: number; //\pm
381385
};
386+
387+
export type LangContainer = { [lang: string]: string };
388+
389+
export type LinkMeta = {
390+
// intended to pass between functions so that there is one object passed
391+
linkType?: string;
392+
linkTarget?: string;
393+
linkLocation?: string;
394+
};
395+
396+
export type ContentItem = {
397+
id: number;
398+
heading?: boolean;
399+
features?: any;
400+
title: LangContainer;
401+
subtitle?: LangContainer;
402+
audioFilename?: LangContainer;
403+
imageFilename?: string;
404+
itemType?: string;
405+
contentItemContainer: boolean;
406+
linkType?: string;
407+
linkTarget?: string;
408+
linkLocation?: string;
409+
layoutMode?: string;
410+
layoutCollection?: string[];
411+
children?: ContentItem[];
412+
};
413+
414+
export type ContentScreen = {
415+
id: number;
416+
title?: {
417+
[lang: string]: string;
418+
};
419+
items?: {
420+
id: number;
421+
}[];
422+
};
423+
424+
export type ContentsData = {
425+
title?: {
426+
[lang: string]: string;
427+
};
428+
features?: any;
429+
items?: ContentItem[];
430+
nestedItems?: boolean;
431+
screens?: ContentScreen[];
432+
};

convert/convertConfig.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { existsSync, readdirSync, readFileSync, type PathLike } from 'fs';
1+
import { existsSync, mkdirSync, readdirSync, readFileSync, type PathLike } from 'fs';
22
import path, { basename, extname, join } from 'path';
33
import type {
44
AppConfig,
@@ -219,6 +219,10 @@ function isDictionaryConfig(data: ScriptureConfig | DictionaryConfig): data is D
219219
}
220220

221221
function convertConfig(dataDir: string, verbose: number) {
222+
const genAssets = path.join('src/gen-assets');
223+
if (!existsSync(genAssets)) {
224+
mkdirSync(genAssets, { recursive: true });
225+
}
222226
const dom = new jsdom.JSDOM(readFileSync(path.join(dataDir, 'appdef.xml')).toString(), {
223227
contentType: 'text/xml'
224228
});
@@ -1613,7 +1617,7 @@ export interface ConfigTaskOutput extends TaskOutput {
16131617

16141618
/**
16151619
* Converts appdef.xml into a config object which is passed to other conversion functions
1616-
* and is also written to src/config.js.
1620+
* and is also written to src/gen-assets/config.ts.
16171621
*/
16181622
export class ConvertConfig extends Task {
16191623
public triggerFiles: string[] = ['appdef.xml'];
@@ -1624,8 +1628,14 @@ export class ConvertConfig extends Task {
16241628
data,
16251629
files: [
16261630
{
1627-
path: 'src/lib/data/config.js',
1628-
content: `export default ${JSON.stringify(data, null, 2)};`
1631+
path: 'src/gen-assets/config.ts',
1632+
content: [
1633+
`import type { AppConfig, DictionaryConfig, ScriptureConfig } from '$config';`,
1634+
`export const config = ${JSON.stringify(data, null, 2)} as Readonly<AppConfig>;`,
1635+
`export const dictionaryConfig = config as Readonly<DictionaryConfig>;`,
1636+
`export const scriptureConfig = config as Readonly<ScriptureConfig>;`,
1637+
`export default config;\n`
1638+
].join('\n')
16291639
}
16301640
]
16311641
};

convert/convertContents.ts

Lines changed: 9 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,11 @@
11
import { existsSync, readFileSync } from 'fs';
22
import path from 'path';
3-
import { ScriptureConfig } from '$config';
3+
import { ContentItem, ContentsData, LangContainer, LinkMeta, ScriptureConfig } from '$config';
44
import jsdom from 'jsdom';
55
import { ConfigTaskOutput, parseLangAttribute } from './convertConfig';
66
import { createHashedFile, createOutputDir, deleteOutputDir, joinUrlPath } from './fileUtils';
77
import { Task, TaskOutput } from './Task';
88

9-
export type LangContainer = { [lang: string]: string };
10-
11-
export type LinkMeta = {
12-
// intended to pass between functions so that there is one object passed
13-
linkType?: string;
14-
linkTarget?: string;
15-
linkLocation?: string;
16-
};
17-
18-
type ContentItem = {
19-
id: number;
20-
heading?: boolean;
21-
features?: any;
22-
title: LangContainer;
23-
subtitle?: LangContainer;
24-
audioFilename?: LangContainer;
25-
imageFilename?: string;
26-
itemType?: string;
27-
contentItemContainer: boolean;
28-
linkType?: string;
29-
linkTarget?: string;
30-
linkLocation?: string;
31-
layoutMode?: string;
32-
layoutCollection?: string[];
33-
children?: ContentItem[];
34-
};
35-
36-
type ContentScreen = {
37-
id: number;
38-
title?: {
39-
[lang: string]: string;
40-
};
41-
items?: {
42-
id: number;
43-
}[];
44-
};
45-
46-
export type ContentsData = {
47-
title?: {
48-
[lang: string]: string;
49-
};
50-
features?: any;
51-
items?: ContentItem[];
52-
nestedItems?: boolean;
53-
screens?: ContentScreen[];
54-
};
55-
56-
const data: ContentsData = {};
57-
589
export interface ContentsTaskOutput extends TaskOutput {
5910
taskName: 'ConvertContents';
6011
}
@@ -367,6 +318,8 @@ export function convertContents(
367318
deleteOutputDir(destDir);
368319
}
369320

321+
const data: ContentsData = {};
322+
370323
const contentsFile = path.join(dataDir, 'contents.xml');
371324
if (!existsSync(contentsFile)) {
372325
return data;
@@ -602,8 +555,12 @@ export class ConvertContents extends Task {
602555
data,
603556
files: [
604557
{
605-
path: 'src/lib/data/contents.js',
606-
content: `export default ${JSON.stringify(data, null, 2)}`
558+
path: 'src/gen-assets/contents.ts',
559+
content: [
560+
`import type { ContentsData } from '$config';`,
561+
`export const contents = ${JSON.stringify(data, null, 2)} as Readonly<ContentsData>;`,
562+
`export default contents;\n`
563+
].join('\n')
607564
}
608565
]
609566
};

convert/convertFirebase.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,17 @@ export interface FirebaseTaskOutput extends TaskOutput {
1111
export function convertFirebase(dataDir: string, verbose: number) {
1212
const srcFile = path.join(dataDir, 'firebase-config.js');
1313
const srcExists = existsSync(srcFile);
14-
const dstFile = path.join('src', 'lib', 'data', 'firebase-config.js');
14+
const dstFile = path.join('src', 'gen-assets', 'firebase-config.ts');
1515
if (verbose) {
1616
console.log(`FirebaseConfig: path=${srcFile}, exists=${srcExists}`);
1717
}
18+
const prefix = `import type { FirebaseOptions } from 'firebase/app';\nexport const firebaseConfig: Readonly<FirebaseOptions> | null`;
1819
if (srcExists) {
1920
let content = readFileSync(srcFile, 'utf-8');
20-
content = content.replace('const firebaseConfig', 'export const firebaseConfig');
21+
content = content.replace('const firebaseConfig', prefix);
2122
writeFileSync(dstFile, content, 'utf-8');
2223
} else {
23-
const firebaseConfig = 'export const firebaseConfig = null;';
24+
const firebaseConfig = `${prefix} = null`;
2425
writeFileSync(dstFile, firebaseConfig);
2526
}
2627
}

convert/index.ts

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { writeFile } from 'fs';
1+
import { writeFile } from 'fs/promises';
22
import path from 'path';
33
import { watch } from 'chokidar';
44
import { ConvertAbout } from './convertAbout';
@@ -87,14 +87,7 @@ async function fullConvert(printDetails: boolean): Promise<boolean> {
8787
// step may be async, in which case it should be awaited
8888
const out = await step.run(verbose, outputs, step.triggerFiles);
8989
outputs.set(step.constructor.name, out);
90-
await Promise.all(
91-
out.files.map(
92-
(f) =>
93-
new Promise((r) => {
94-
writeFile(f.path, f.content, r);
95-
})
96-
)
97-
);
90+
await Promise.all(out.files.map((f) => writeFile(f.path, f.content)));
9891
} catch (e) {
9992
oldConsoleLog(lastStepOutput);
10093
oldConsoleLog(e);
@@ -157,11 +150,7 @@ if (process.argv.includes('--watch')) {
157150
outputs.set(step.constructor.name, out);
158151
// Write all files to disk
159152
/*await*/ // We don't need to await the file writes; next steps can continue running while writes occur
160-
Promise.all(
161-
out.files.map((f) => {
162-
new Promise((r) => writeFile(f.path, f.content, r));
163-
})
164-
);
153+
Promise.all(out.files.map((f) => writeFile(f.path, f.content)));
165154
} catch (e) {
166155
oldConsoleLog(lastStepOutput);
167156
oldConsoleLog(e);

convert/tests/dab/convertConfigDAB.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { readFileSync } from 'fs';
22
import path from 'path';
3-
import type { DictionaryConfig, DictionaryWritingSystemConfig } from '$config';
3+
import type { DictionaryWritingSystemConfig } from '$config';
44
import jsdom from 'jsdom';
55
import { expect, test } from 'vitest';
66
import { parseDictionaryWritingSystem, parseFeatures } from '../../convertConfig';

eslint.config.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,6 @@ export default ts.config(
7373
'.env.*',
7474
'!env.example',
7575
'data',
76-
'src/config.js',
7776
'static',
7877
'example_data',
7978
'test_data',

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
"convert": "ts-node convert/index.ts",
1818
"convert:watch": "ts-node convert/index.ts --watch",
1919
"sandbox": "ts-node data-sandbox/index.ts",
20-
"clean": "rimraf .svelte-kit build src/gen-assets src/lib/data/config.js src/lib/data/catalog.js src/lib/data/firebase-config.js src/lib/data/contents.js static/illustrations static/icons static/contents static/manifest*.json && echo 🔔 Reminder: The project cannot be built until the conversion scripts are run again.",
20+
"clean": "rimraf .svelte-kit build src/gen-assets src/lib/data/catalog.js static/illustrations static/icons static/collections static/contents static/manifest*.json && echo 🔔 Reminder: The project cannot be built until the conversion scripts are run again.",
2121
"clean:data": "rimraf --glob data/*",
2222
"clean:all": "npm run clean && npm run clean:data",
2323
"test": "vitest",

0 commit comments

Comments
 (0)