Skip to content

Commit 3f0f586

Browse files
authored
Merge pull request #118 from bluecadet/feat/strict-typing
2 parents 3a98a7e + 1a4c1ea commit 3f0f586

Some content is hidden

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

55 files changed

+1020
-377
lines changed

.changeset/soft-books-listen.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
"@bluecadet/launchpad-dashboard": patch
3+
"@bluecadet/launchpad": patch
4+
"@bluecadet/launchpad-scaffold": patch
5+
"@bluecadet/launchpad-content": patch
6+
"@bluecadet/launchpad-monitor": patch
7+
"@bluecadet/launchpad-utils": patch
8+
---
9+
10+
Generate d.ts declaration files for intellisense, and fix all type errors.

.github/workflows/main.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,7 @@ jobs:
2323
run: npm install
2424

2525
- name: Run eslint
26-
run: npm run lint
26+
run: npm run lint
27+
28+
- name: Validate types
29+
run: npm run build

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ jobs:
2727
id: changesets
2828
uses: changesets/action@v1
2929
with:
30-
publish: npx changeset publish
30+
publish: npm run publish
3131
env:
3232
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
3333
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,3 +146,6 @@ dist
146146

147147
# changesets
148148
!.changeset/config.json
149+
150+
# generated .d.ts files
151+
types

package.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616
"watch-docs": "nodemon",
1717
"lint": "npx eslint ./packages/**/*.js",
1818
"lint:fix": "npx eslint ./packages/**/*.js --fix",
19-
"lint:fix-dry": "npx eslint ./packages/**/*.js --fix-dry-run"
19+
"lint:fix-dry": "npx eslint ./packages/**/*.js --fix-dry-run",
20+
"build": "npm run build -w @bluecadet/launchpad",
21+
"release": "npm run build && changeset publish"
2022
},
2123
"repository": {
2224
"type": "git",
@@ -34,9 +36,11 @@
3436
"devDependencies": {
3537
"@changesets/changelog-github": "^0.4.6",
3638
"@changesets/cli": "^2.23.0",
39+
"@types/node": "^20.3.1",
3740
"eslint": "^8.32.0",
3841
"eslint-config-standard": "^17.0.0",
3942
"jsdoc-to-markdown": "^7.1.1",
40-
"nodemon": "^2.0.20"
43+
"nodemon": "^2.0.20",
44+
"typescript": "^5.1.3"
4145
}
4246
}

packages/content/index.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,11 @@ export * from './lib/launchpad-content.js';
88
export * from './lib/utils/file-utils.js';
99
export default LaunchpadContent;
1010

11+
/**
12+
* @param {import('./lib/content-options.js').ContentOptions | {content: import('./lib/content-options.js').ContentOptions}} config
13+
*/
1114
export const launch = async (config) => {
12-
const content = new LaunchpadContent(config.content || config);
15+
const content = new LaunchpadContent('content' in config ? config.content : config);
1316
await content.start();
1417
};
1518

packages/content/lib/content-options.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ export class ContentOptions {
1616
return '%TIMESTAMP%';
1717
}
1818

19+
/**
20+
* @param {any} options
21+
*/
1922
constructor({
2023
sources = [],
2124
imageTransforms = [],
@@ -77,21 +80,21 @@ export class ContentOptions {
7780

7881
/**
7982
* Temp file directory path.
80-
* @type {boolean}
83+
* @type {string}
8184
* @default '%DOWNLOAD_PATH%/.tmp/'
8285
*/
8386
this.tempPath = tempPath;
8487

8588
/**
8689
* Temp directory path where all downloaded content will be backed up before removal.
87-
* @type {boolean}
90+
* @type {string}
8891
* @default '%DOWNLOAD_PATH%/.backups/'
8992
*/
9093
this.backupPath = backupPath;
9194

9295
/**
9396
* Which files to keep in `dest` if `clearOldFilesOnSuccess` or `clearOldFilesOnStart` are `true`. E.g. `'*.json|*.csv|*.xml|*.git*'`
94-
* @type {boolean}
97+
* @type {string}
9598
* @default ''
9699
*/
97100
this.keep = keep;

packages/content/lib/content-sources/airtable-source.js

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ import Credentials from '../credentials.js';
1313
* Options for AirtableSource
1414
*/
1515
export class AirtableOptions extends SourceOptions {
16+
/**
17+
* @param {any} options
18+
*/
1619
constructor({
1720
baseId = undefined,
1821
tables = [],
@@ -31,6 +34,7 @@ export class AirtableOptions extends SourceOptions {
3134
this.baseId = baseId;
3235

3336
/**
37+
* The table view which to select for syncing by default
3438
* @type {string}
3539
* @default 'Grid view'
3640
*/
@@ -58,12 +62,6 @@ export class AirtableOptions extends SourceOptions {
5862
*/
5963
this.endpointUrl = endpointUrl;
6064

61-
/**
62-
* The table view which to select for syncing by default
63-
* @type {string}
64-
*/
65-
this.defaultView = defaultView;
66-
6765
/**
6866
* Appends the local path of attachments to the saved JSON
6967
* @type {boolean}
@@ -73,12 +71,23 @@ export class AirtableOptions extends SourceOptions {
7371
}
7472
}
7573

74+
/**
75+
* @extends {ContentSource<AirtableOptions>}
76+
*/
7677
export class AirtableSource extends ContentSource {
77-
/** @type {Airtable.Base} */
78-
_base = null;
78+
/**
79+
* @type {Airtable.Base}
80+
*/
81+
_base;
7982

83+
/**
84+
* @type {Record<string, Airtable.Record<Airtable.FieldSet>[]>}
85+
*/
8086
_rawAirtableData = {};
8187

88+
/**
89+
* @type {Record<string, unknown[]>}
90+
*/
8291
_simplifiedData = {};
8392

8493
/**
@@ -135,14 +144,17 @@ export class AirtableSource extends ContentSource {
135144
* @param {string} tableId
136145
* @param {boolean} isKeyValueTable
137146
* @param {ContentResult} result
138-
* @returns {ContentResult}
147+
* @returns {Promise<ContentResult>}
139148
*/
140149
async _processTable(tableId, isKeyValueTable = false, result = new ContentResult()) {
141150
// Write raw Data file.
142151

143152
const rawDataPath = `${tableId}.raw.json`;
144153
result.addDataFile(rawDataPath, this._rawAirtableData[tableId]);
145154

155+
/**
156+
* @type {any}
157+
*/
146158
const simpData = isKeyValueTable ? {} : [];
147159
this._simplifiedData[tableId] = [];
148160

@@ -198,15 +210,27 @@ export class AirtableSource extends ContentSource {
198210
return result;
199211
}
200212

213+
/**
214+
* @param {string} str
215+
* @returns {boolean}
216+
*/
201217
_isBoolStr(str) {
202218
return str === 'true' || str === 'false';
203219
}
204220

221+
/**
222+
* @param {string} str
223+
* @returns {boolean}
224+
*/
205225
_isNumericStr(str) {
206-
return !isNaN(str);
226+
return !isNaN(Number(str));
207227
}
208228

209229
// Get Data.
230+
/**
231+
* @param {string} table
232+
* @param {boolean} force
233+
*/
210234
async _getData(table, force = false) {
211235
// If force, clear the data.
212236
if (force) {
@@ -224,9 +248,16 @@ export class AirtableSource extends ContentSource {
224248
});
225249
}
226250

227-
// Fetch from Airtable.
251+
/**
252+
* Fetch from Airtable.
253+
* @param {string} table table name
254+
* @returns {Promise<Airtable.Record<Airtable.FieldSet>[]>}
255+
*/
228256
async _fetchData(table) {
229257
return new Promise((resolve, reject) => {
258+
/**
259+
* @type {Airtable.Record<Airtable.FieldSet>[]}
260+
*/
230261
const rows = [];
231262

232263
this._base(table)

packages/content/lib/content-sources/content-result.js

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export class DataFile {
1313
/**
1414
*
1515
* @param {string} localPath
16-
* @param {string|JSON|Object|Array} content
16+
* @param {string|JSON|object|Array<any>} content
1717
*/
1818
constructor(localPath, content) {
1919
this.localPath = localPath;
@@ -35,11 +35,16 @@ export class DataFile {
3535
}
3636
}
3737
export class MediaDownload {
38+
/**
39+
* @param {object} options
40+
* @param {string} options.url
41+
* @param {string} [options.localPath]
42+
*/
3843
constructor({
3944
url,
4045
localPath = undefined,
4146
...rest
42-
} = {}) {
47+
}) {
4348
/**
4449
* The url to download
4550
* @type {string}
@@ -68,7 +73,7 @@ export class MediaDownload {
6873
export class ContentResult {
6974
/**
7075
* List of data files to save
71-
* @type {Array<ContentResult>}
76+
* @param {Array<ContentResult>} results
7277
*/
7378
static combine(results) {
7479
const finalResult = results.reduce((previousValue, currentValue) => {
@@ -120,10 +125,10 @@ export class ContentResult {
120125

121126
/**
122127
*
123-
* @param {MediaDownload} urlOrDownload
128+
* @param {MediaDownload | string} urlOrDownload
124129
*/
125130
addMediaDownload(urlOrDownload) {
126-
if (typeof urlOrDownload === 'string' || urlOrDownload instanceof String) {
131+
if (typeof urlOrDownload === 'string') {
127132
urlOrDownload = new MediaDownload({
128133
url: urlOrDownload
129134
});
@@ -133,7 +138,7 @@ export class ContentResult {
133138

134139
/**
135140
*
136-
* @param {Iterable} files
141+
* @param {Iterable<MediaDownload>} files
137142
*/
138143
addMediaDownloads(files) {
139144
this.mediaDownloads.push(...files);
@@ -144,10 +149,15 @@ export class ContentResult {
144149
* @param {string} id
145150
*/
146151
collate(id) {
152+
/**
153+
* @type {Array<DataFile>}
154+
*/
155+
const initial = [];
156+
147157
// Collect all data into 1 object.
148158
const collatedData = this.dataFiles.reduce((previousValue, currentValue) => {
149159
return [...previousValue, ...currentValue.content];
150-
}, []);
160+
}, initial);
151161

152162
// Remove old datafiles.
153163
this.dataFiles = [];

packages/content/lib/content-sources/content-source.js

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -38,19 +38,22 @@ export class SourceOptions {
3838
}
3939
}
4040

41+
/**
42+
* @template {SourceOptions} [C=SourceOptions]
43+
*/
4144
export class ContentSource {
42-
/** @type {SourceOptions} */
43-
config = null;
44-
/** @type {Logger} */
45-
logger = null;
45+
/** @type {C} */
46+
config;
47+
/** @type {Logger | Console} */
48+
logger;
4649

4750
/**
48-
* @param {SourceOptions} config Content source options. `id` is a required field.
49-
* @param {Logger} logger The logger to use for all output. Defaults to console.
51+
* @param {C} config Content source options. `id` is a required field.
52+
* @param {Logger} [logger] The logger to use for all output. Defaults to console.
5053
* @throws {Error} Throws an error if no `config` or `config.id` is defined.
5154
*/
52-
constructor(config, logger = console) {
53-
this.logger = logger;
55+
constructor(config, logger) {
56+
this.logger = logger ?? console;
5457
this.config = config;
5558

5659
if (!this.config || !this.config.id) {
@@ -64,17 +67,16 @@ export class ContentSource {
6467
* @returns {Promise<ContentResult>} that resolves only when all content has been downloaded successfully
6568
*/
6669
async fetchContent() {
67-
this.logger.info(chalk.green(`Downloading functionality not implemented for '${chalk.yellow(this.config.id)}'`));
68-
return Promise.resolve();
70+
throw new Error(`Downloading functionality not implemented for '${chalk.yellow(this.config.id)}'`);
6971
}
7072

7173
/**
7274
* Removes all content and media files in the temp and dest directories (temp first, then dest).
7375
*
74-
* @returns {Promise}
76+
* @returns {Promise<void>}
7577
*/
7678
async clearContent() {
77-
await fs.remove(this.config);
79+
throw new Error('clearContent not implemented');
7880
}
7981

8082
toString() {

0 commit comments

Comments
 (0)