Skip to content

Commit

Permalink
⚡ Stricter type checks for ct.IDE's node_requires
Browse files Browse the repository at this point in the history
  • Loading branch information
CosmoMyzrailGorynych committed Apr 18, 2024
1 parent d2128c8 commit a096093
Show file tree
Hide file tree
Showing 52 changed files with 380 additions and 256 deletions.
6 changes: 4 additions & 2 deletions src/js/utils/codeEditorHelpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,16 @@
noLib: true,
allowNonTsExtensions: true,
target: 'ES2020',
downlevelIteration: true
downlevelIteration: true,
alwaysStrict: true
});
monaco.languages.typescript.typescriptDefaults.setEagerModelSync(true);
monaco.languages.typescript.typescriptDefaults.setCompilerOptions({
noLib: true,
allowNonTsExtensions: true,
target: 'ES2020',
downlevelIteration: true
downlevelIteration: true,
alwaysStrict: true
});

/** Custom hover provider that removes @catnip tags from documentation */
Expand Down
25 changes: 14 additions & 11 deletions src/node_requires/catnip/blockUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export const convertFromDtsToBlocks = (usefuls: usableDeclaration[], lib: 'core'
}
let documentation = useful.description,
name = niceBlockName(useful.name),
displayName: string;
displayName: string | undefined;
const piecesAssets: Record<string, resourceType | 'action'> = {};
const piecesDefaults: Record<string, string | number | boolean> = {};
const extraNames: Record<string, string> = {};
Expand All @@ -82,24 +82,24 @@ export const convertFromDtsToBlocks = (usefuls: usableDeclaration[], lib: 'core'
} else if (node.tagName.escapedText === 'catnipIgnore') {
// eslint-disable-next-line no-labels
continue onusefulLoop;
} else if (node.tagName.escapedText === 'catnipLabel') {
displayName = node.comment.toString();
} else if (node.tagName.escapedText === 'catnipName') {
} else if (node.tagName.escapedText === 'catnipLabel' && node.comment) {
displayName = node.comment?.toString();
} else if (node.tagName.escapedText === 'catnipName' && node.comment) {
name = node.comment.toString();
} else if (String(node.tagName.escapedText).startsWith('catnipName_')) {
} else if (String(node.tagName.escapedText).startsWith('catnipName_') && node.comment) {
const key = String(node.tagName.escapedText).replace('catnipName_', 'name_');
extraNames[key] = node.comment.toString().trim();
} else if (String(node.tagName.escapedText).startsWith('catnipLabel_')) {
} else if (String(node.tagName.escapedText).startsWith('catnipLabel_') && node.comment) {
const key = String(node.tagName.escapedText).replace('catnipLabel_', 'displayName_');
extraNames[key] = node.comment.toString().trim();
} else if (node.tagName.escapedText === 'catnipIcon') {
} else if (node.tagName.escapedText === 'catnipIcon' && node.comment) {
icon = node.comment.toString();
} else if (node.tagName.escapedText === 'catnipAsset') {
} else if (node.tagName.escapedText === 'catnipAsset' && node.comment) {
let [key, assetType] = node.comment.toString().split(':');
key = key.trim();
assetType = assetType.trim();
piecesAssets[key] = assetType as resourceType | 'action';
} else if (node.tagName.escapedText === 'catnipDefault') {
} else if (node.tagName.escapedText === 'catnipDefault' && node.comment) {
let [key, value] = node.comment.toString().split(':');
key = key.trim();
value = value.trim();
Expand All @@ -112,7 +112,7 @@ export const convertFromDtsToBlocks = (usefuls: usableDeclaration[], lib: 'core'
} else {
piecesDefaults[key] = value;
}
} else if (node.tagName.escapedText === 'catnipList') {
} else if (node.tagName.escapedText === 'catnipList' && node.comment) {
listType = node.comment.toString().trim() as resourceType;
} else if (node.tagName.escapedText === 'catnipSaveReturn') {
returnSave = true;
Expand All @@ -125,7 +125,7 @@ export const convertFromDtsToBlocks = (usefuls: usableDeclaration[], lib: 'core'
} else {
promise = 'both';
}
} else if (node.tagName.escapedText === 'catnipPromiseSave') {
} else if (node.tagName.escapedText === 'catnipPromiseSave' && node.comment) {
promiseSave = node.comment.toString().trim() as blockArgumentType || 'wildcard';
}
}
Expand All @@ -135,6 +135,9 @@ export const convertFromDtsToBlocks = (usefuls: usableDeclaration[], lib: 'core'
if (returnSave || promise) {
isCommand = true;
}
if (!displayName) {
displayName = name;
}
const draft = {
code: useful.name,
lib,
Expand Down
26 changes: 19 additions & 7 deletions src/node_requires/catnip/declarationExtractor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ const simplifyJsDoc = (jsDoc: ts.JSDoc[] | void): string | void => {
.join('\n\n');
};

// eslint-disable-next-line max-lines-per-function
const visit = (
node: ts.Node,
topLevelPath: string,
Expand All @@ -72,7 +73,9 @@ const visit = (
// Remove the `root.` ⤵️
name: `${topLevelPath.slice(5)}.${(name as any).escapedText}`,
kind: 'prop',
returnType: (paramConstTypeMap[declaration?.type?.kind] as blockArgumentType) ?? 'wildcard',
returnType: declaration?.type?.kind ?
((paramConstTypeMap[declaration?.type?.kind] as blockArgumentType) ?? 'wildcard') :
'wildcard',
description: (first as any).jsDoc?.[0].comment,
jsDoc: (first as any).jsDoc,
node
Expand All @@ -94,13 +97,18 @@ const visit = (
ts.PropertySignature |
ts.FunctionDeclaration
) & {jsDoc: ts.JSDoc[]});
let args;
let args: {
type: blockArgumentType | 'BLOCKS';
name: string;
}[] = [];
if (node.kind === ts.SyntaxKind.FunctionDeclaration ||
node.kind === ts.SyntaxKind.MethodSignature
) {
args = (node as ts.FunctionDeclaration | ts.MethodSignature).parameters.map(param => {
let tsType = (param.type as any)?.typeName?.escapedText ?? 'any';
if (ts.isToken(param.type) || param?.type?.kind === ts.SyntaxKind.FunctionType) {
if (param.type &&
(ts.isToken(param.type) ||
param?.type?.kind === ts.SyntaxKind.FunctionType)) {
tsType = paramConstTypeMap[
param.type.kind as keyof typeof paramConstTypeMap
] ?? 'any';
Expand All @@ -114,20 +122,24 @@ const visit = (
};
});
}
const useful: usableDeclaration = {
const useful: Partial<usableDeclaration> = {
// Remove the `root.` ⤵️
name: `${topLevelPath.slice(5)}.${(name as any).escapedText}`,
kind: usefulMap[node.kind],
args,
returnType: (paramConstTypeMap[type.kind] as blockArgumentType) ?? 'wildcard',
jsDoc,
node
};
if (useful.kind === 'function') {
useful.args = args;
useful.returnType = type ?
((paramConstTypeMap[type.kind] as blockArgumentType) ?? 'wildcard') :
'wildcard';
}
const description = simplifyJsDoc(jsDoc);
if (description) {
useful.description = description;
}
onUseful(useful);
onUseful(useful as usableDeclaration);
} break;
default:
console.debug('skipping', node.kind, ts.SyntaxKind[node.kind], node);
Expand Down
42 changes: 23 additions & 19 deletions src/node_requires/catnip/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,12 +100,15 @@ loadBlocks().then(menus => {
builtinBlockLibrary.splice(4, 0, ...menus);
});

/** A utility function for dumping all the i18n keys of built-in blocks */
export const getCtjsI18nKeys = (): void => {
const nameKeys: string[] = [],
displayNameKeys: string[] = [];
for (const menu of ctjsApiMenus) {
for (const item of menu.items) {
nameKeys.push(item.i18nKey);
if (item.i18nKey) {
nameKeys.push(item.i18nKey);
}
if (item.displayI18nKey) {
displayNameKeys.push(item.displayI18nKey);
}
Expand Down Expand Up @@ -144,7 +147,7 @@ const recreateFuseIndex = () => {
(getByPath('catnip.blockNames.' + block.i18nKey) as string ?? block.name) :
block.name;
});
fuseIndex = Fuse.createIndex(fuseOptions.keys, fuseCollection);
fuseIndex = Fuse.createIndex(fuseOptions.keys!, fuseCollection);
};
export const searchBlocks = (query: string): blockDeclaration[] => {
const fuse = new Fuse(fuseCollection, fuseOptions, fuseIndex);
Expand All @@ -161,7 +164,7 @@ export const getDeclaration = (lib: string, code: string): blockDeclaration => {
if (!blocksRegistry.has(`${lib}@@${code}`)) {
throw new Error(`[catnip] Could not find block declaration for ${lib}@@${code}. Do you have ${lib} catmod enabled?`);
}
return blocksRegistry.get(`${lib}@@${code}`);
return blocksRegistry.get(`${lib}@@${code}`)!;
};

const loadBuiltinBlocks = (): void => {
Expand Down Expand Up @@ -272,19 +275,20 @@ export const loadModdedBlocks = async (modName: string, noIndex?: boolean) => {
};

export const unloadModdedBlocks = (modName: string) => {
if (loadedCategories.has(modName)) {
const cat = loadedCategories.get(modName);
blocksLibrary.splice(blocksLibrary.indexOf(cat), 1);
cat.items.forEach(removeBlockFromRegistry);
cat.items.forEach(block => {
if (block.category) {
const host = blocksLibrary.find(cat => cat.name === block.category);
if (host && host.items.includes(block)) {
host.items.splice(host.items.indexOf(block), 1);
}
}
});
if (!loadedCategories.has(modName)) {
return;
}
const cat = loadedCategories.get(modName)!;
blocksLibrary.splice(blocksLibrary.indexOf(cat), 1);
cat.items.forEach(removeBlockFromRegistry);
cat.items.forEach(block => {
if (block.category) {
const host = blocksLibrary.find(cat => cat.name === block.category);
if (host && host.items.includes(block)) {
host.items.splice(host.items.indexOf(block), 1);
}
}
});
loadedCategories.delete(modName);
recreateFuseIndex();
};
Expand All @@ -306,7 +310,7 @@ export const loadAllBlocks = async (project: IProject) => {

let transmittedBlocks: IBlock[] = [];
let transmissionSource: (IBlock | 'MARKER')[] | Record<string, IBlock> = [];
let transmissionSourceKey: string;
let transmissionSourceKey: string | undefined;
let cloningMode = false;
let transmissionType: blockDeclaration['type'];
export const getTransmissionType = () => transmissionType;
Expand All @@ -318,7 +322,7 @@ export const getTransmissionReturnVal = () => {
return declaration.typeHint;
};
/** A block after which a (+) indicator will be placed */
let suggestedTarget: IBlock;
let suggestedTarget: IBlock | undefined;
export const getSuggestedTarget = () => suggestedTarget;
export const setSuggestedTarget = (target?: IBlock) => (suggestedTarget = target);

Expand Down Expand Up @@ -346,7 +350,7 @@ export const endBlocksTransmit = (
// to maintain positions while reordering blocks inside the same container.
transmissionSource.splice(transmissionSource.indexOf(block), 1, 'MARKER');
}
} else {
} else if (transmissionSourceKey) {
delete transmissionSource[transmissionSourceKey];
}
} else {
Expand Down Expand Up @@ -411,7 +415,7 @@ export const mutate = (
const pos = key as number;
migrateValues(dest[pos], newBlock);
dest.splice(pos, 1, newBlock);
} else if (customOptions) {
} else if (customOptions && dest.customOptions) {
const prevBlock = dest.customOptions[key] as IBlock;
migrateValues(prevBlock, newBlock);
dest.customOptions[key] = newBlock;
Expand Down
4 changes: 2 additions & 2 deletions src/node_requires/events/coreEventsActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,12 @@ coreEvents.core_OnActionPress.locals = {
description: 'Current action\'s value'
}
};
coreEvents.core_OnActionDown.inlineCodeTemplates.thisOnStep = `
coreEvents.core_OnActionDown.inlineCodeTemplates!.thisOnStep = `
if (actions[/*%%action%%*/].down) {
let value = actions[/*%%action%%*/].value;
\n/*%%USER_CODE%%*/\n
}`;
coreEvents.core_OnActionPress.inlineCodeTemplates.thisOnStep = `
coreEvents.core_OnActionPress.inlineCodeTemplates!.thisOnStep = `
if (actions[/*%%action%%*/].pressed) {
let value = actions[/*%%action%%*/].value;
\n/*%%USER_CODE%%*/\n
Expand Down
12 changes: 6 additions & 6 deletions src/node_requires/events/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ const eventNameRegex = /^(\S+?)_(\S+)$/;
* Returns the library and the event code from the full event key
*/
const splitEventName = (name: string): [string, string] => {
const result = eventNameRegex.exec(name);
const result = eventNameRegex.exec(name)!;
return [result[1], result[2]];
};

Expand Down Expand Up @@ -106,7 +106,7 @@ const localizeProp = (eventFullCode: string, prop: string): string => {
const event = events[eventFullCode];
if (lib === 'core') {
if (timerPattern.test(eventCode)) {
return getLanguageJSON().scriptables[propToCoreDictionary[prop]].Timer.replace('$1', timerPattern.exec(eventCode)[1]);
return getLanguageJSON().scriptables[propToCoreDictionary[prop]].Timer.replace('$1', timerPattern.exec(eventCode)![1]);
}
return getLanguageJSON().scriptables[propToCoreDictionary[prop]][eventCode];
}
Expand Down Expand Up @@ -142,15 +142,15 @@ const localizeArgument = (eventFullCode: string, arg: string): string => {
if (lib === 'core') {
return getLanguageJSON().scriptables.coreEventsArguments[arg];
}
return localizeField(event.arguments[arg], 'name');
return localizeField(event.arguments![arg], 'name');
};
const localizeLocalVarDesc = (eventFullCode: string, local: string): string => {
const [lib, eventCode] = splitEventName(eventFullCode);
const event = events[eventFullCode];
if (lib === 'core') {
return getLanguageJSON().scriptables.coreEventsLocals[`${eventCode}_${local}`];
}
return localizeField(event.locals[local], 'description');
return localizeField(event.locals![local], 'description');
};
const tryGetIcon = (eventFullCode: string, scriptedEvent: IScriptableEvent): string | false => {
const event = events[eventFullCode];
Expand All @@ -169,7 +169,7 @@ const tryGetIcon = (eventFullCode: string, scriptedEvent: IScriptableEvent): str
return false;
};

const canUseBaseClass = (event: IEventDeclaration, baseClass?: TemplateBaseClass): boolean => {
const canUseBaseClass = (event: IEventDeclaration, baseClass: TemplateBaseClass): boolean => {
if (!event.baseClasses || event.baseClasses.length === 0) {
return true;
}
Expand Down Expand Up @@ -199,7 +199,7 @@ const bakeCategories = function bakeCategories(
}
});
}
const miscCategory = menu.items.find(s => s.affixedData.core && s.affixedData.key === 'misc');
const miscCategory = menu.items.find(s => s.affixedData.core && s.affixedData.key === 'misc')!;
for (const eventKey in events) {
const event = events[eventKey];
// Filter out events for other entities
Expand Down
6 changes: 3 additions & 3 deletions src/node_requires/exporter/ExporterError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,14 @@ export const highlightProblem = (
const lines = code.split('\n');
const output = [];
let firstColumn, lastColumn, firstLine, lastLine;
if ('first_column' in location) {
if ('first_column' in location!) {
firstColumn = location.first_column;
firstLine = location.first_line;
lastLine = location.last_line;
lastColumn = location.last_column;
} else {
firstColumn = lastColumn = location.column;
firstLine = lastLine = location.line - 1;
firstColumn = lastColumn = location!.column;
firstLine = lastLine = location!.line - 1;
}
// Take a couple of lines before the error
output.push(...lines.slice(Math.max(0, firstLine - 2), firstLine + 1));
Expand Down
2 changes: 1 addition & 1 deletion src/node_requires/exporter/_exporterContracts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ export type ExportedStyle = {
dropShadowDistance?: number;
}

export type ExportedSound = Omit<ISound, 'uid' | 'group' | 'lastmod' | 'type'> &
export type ExportedSound = Omit<ISound, 'uid' | 'group' | 'lastmod' | 'type' | 'distortion' | 'eq' | 'pitch' | 'reverb' | 'volume'> &
Partial<Pick<ISound, 'distortion' | 'eq' | 'pitch' | 'reverb' | 'volume'>>;

export type ExportedBehaviorDynamic = {
Expand Down
2 changes: 1 addition & 1 deletion src/node_requires/exporter/fonts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ export const bakeBitmapFonts = function bakeBitmapFonts(
) => acc + (charSets[charset] || ''), '');
}
if (fCharsets.indexOf('custom') !== -1) {
letterList += font.customCharset;
letterList += font.customCharset!;
}
const settings = {
fill: '#ffffff',
Expand Down
2 changes: 1 addition & 1 deletion src/node_requires/exporter/icons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export const resizeIcon = async function (
soft: boolean
): Promise<void> {
const canvas = document.createElement('canvas'),
ctx = canvas.getContext('2d');
ctx = canvas.getContext('2d')!;
canvas.width = canvas.height = length;
ctx.imageSmoothingQuality = soft ? 'high' : 'low';
// eslint-disable-next-line id-length
Expand Down
3 changes: 3 additions & 0 deletions src/node_requires/exporter/rooms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,9 @@ const stringifyRooms = (
const exportableCopy = {
...copy,
template: getById('template', copy.uid).name
} as ExportedCopy & {
uid?: string;
bindings?: unknown;
};
delete exportableCopy.uid;
delete exportableCopy.bindings;
Expand Down
Loading

0 comments on commit a096093

Please sign in to comment.