From a0960933594f322e43f9e25fb6132c93d7d3173a Mon Sep 17 00:00:00 2001 From: Cosmo Myzrail Gorynych Date: Thu, 18 Apr 2024 18:39:46 +1200 Subject: [PATCH] :zap: Stricter type checks for ct.IDE's node_requires --- src/js/utils/codeEditorHelpers.js | 6 +- src/node_requires/catnip/blockUtils.ts | 25 ++++--- .../catnip/declarationExtractor.ts | 26 +++++-- src/node_requires/catnip/index.ts | 42 ++++++----- src/node_requires/events/coreEventsActions.ts | 4 +- src/node_requires/events/index.ts | 12 +-- src/node_requires/exporter/ExporterError.ts | 6 +- .../exporter/_exporterContracts.ts | 2 +- src/node_requires/exporter/fonts.ts | 2 +- src/node_requires/exporter/icons.ts | 2 +- src/node_requires/exporter/rooms.ts | 3 + .../exporter/scriptableProcessor.ts | 9 ++- src/node_requires/exporter/sounds.ts | 17 +++-- src/node_requires/exporter/templates.ts | 10 +-- src/node_requires/exporter/textures.ts | 37 +++++---- src/node_requires/exporter/utils.ts | 2 +- src/node_requires/generators/gridTexture.ts | 2 +- src/node_requires/platformUtils.ts | 2 +- .../resources/behaviors/index.ts | 7 +- .../resources/emitterTandems/index.ts | 8 +- src/node_requires/resources/index.ts | 22 +++--- src/node_requires/resources/modules/index.ts | 3 + src/node_requires/resources/preview/font.ts | 2 +- .../resources/projects/defaultProject.ts | 2 +- src/node_requires/resources/projects/index.ts | 25 ++++--- .../resources/projects/scripts.ts | 2 +- src/node_requires/resources/rooms/index.ts | 8 +- src/node_requires/resources/styles/index.ts | 10 ++- .../resources/templates/index.ts | 10 ++- src/node_requires/resources/textures/index.ts | 24 ++++-- src/node_requires/riotMixins/discardio.ts | 12 +-- src/node_requires/roomEditor/common.ts | 2 +- .../roomEditor/entityClasses/Background.ts | 4 +- .../roomEditor/entityClasses/Copy.ts | 75 ++++++++++--------- .../roomEditor/entityClasses/SnapTarget.ts | 4 +- .../roomEditor/entityClasses/Tile.ts | 1 - .../roomEditor/entityClasses/TileLayer.ts | 3 +- .../roomEditor/entityClasses/Transformer.ts | 12 +-- .../entityClasses/ViewportRestriction.ts | 16 ++-- src/node_requires/roomEditor/history.ts | 52 ++++++++----- src/node_requires/roomEditor/index.ts | 28 ++++--- .../roomEditor/interactions/selectUi.ts | 4 +- .../roomEditor/interactions/tab.ts | 10 ++- .../interactions/tiles/deleteTiles.ts | 4 +- .../interactions/tiles/placeTile.ts | 6 +- .../interactions/transformer/move.ts | 8 +- .../interactions/transformer/nudge.ts | 16 +++- .../interactions/transformer/rotate.ts | 8 +- .../interactions/transformer/scale.ts | 10 ++- .../interactions/transformer/select.ts | 16 ++-- src/node_requires/utils/imageUtils.ts | 12 +-- tsconfig.json | 1 + 52 files changed, 380 insertions(+), 256 deletions(-) diff --git a/src/js/utils/codeEditorHelpers.js b/src/js/utils/codeEditorHelpers.js index 58bf34c7d..4db595a1c 100644 --- a/src/js/utils/codeEditorHelpers.js +++ b/src/js/utils/codeEditorHelpers.js @@ -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 */ diff --git a/src/node_requires/catnip/blockUtils.ts b/src/node_requires/catnip/blockUtils.ts index 21b913ee9..ccb8acdc0 100644 --- a/src/node_requires/catnip/blockUtils.ts +++ b/src/node_requires/catnip/blockUtils.ts @@ -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 = {}; const piecesDefaults: Record = {}; const extraNames: Record = {}; @@ -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(); @@ -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; @@ -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'; } } @@ -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, diff --git a/src/node_requires/catnip/declarationExtractor.ts b/src/node_requires/catnip/declarationExtractor.ts index 2dd8ea89b..9160fdf0a 100644 --- a/src/node_requires/catnip/declarationExtractor.ts +++ b/src/node_requires/catnip/declarationExtractor.ts @@ -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, @@ -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 @@ -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'; @@ -114,20 +122,24 @@ const visit = ( }; }); } - const useful: usableDeclaration = { + const useful: Partial = { // 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); diff --git a/src/node_requires/catnip/index.ts b/src/node_requires/catnip/index.ts index e8fbdf7e6..f6ae668bd 100644 --- a/src/node_requires/catnip/index.ts +++ b/src/node_requires/catnip/index.ts @@ -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); } @@ -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); @@ -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 => { @@ -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(); }; @@ -306,7 +310,7 @@ export const loadAllBlocks = async (project: IProject) => { let transmittedBlocks: IBlock[] = []; let transmissionSource: (IBlock | 'MARKER')[] | Record = []; -let transmissionSourceKey: string; +let transmissionSourceKey: string | undefined; let cloningMode = false; let transmissionType: blockDeclaration['type']; export const getTransmissionType = () => transmissionType; @@ -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); @@ -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 { @@ -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; diff --git a/src/node_requires/events/coreEventsActions.ts b/src/node_requires/events/coreEventsActions.ts index c1185e810..61294224b 100644 --- a/src/node_requires/events/coreEventsActions.ts +++ b/src/node_requires/events/coreEventsActions.ts @@ -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 diff --git a/src/node_requires/events/index.ts b/src/node_requires/events/index.ts index 480b45a46..33715f5d0 100644 --- a/src/node_requires/events/index.ts +++ b/src/node_requires/events/index.ts @@ -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]]; }; @@ -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]; } @@ -142,7 +142,7 @@ 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); @@ -150,7 +150,7 @@ const localizeLocalVarDesc = (eventFullCode: string, local: string): string => { 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]; @@ -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; } @@ -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 diff --git a/src/node_requires/exporter/ExporterError.ts b/src/node_requires/exporter/ExporterError.ts index 4ed760a92..f0e24baa1 100644 --- a/src/node_requires/exporter/ExporterError.ts +++ b/src/node_requires/exporter/ExporterError.ts @@ -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)); diff --git a/src/node_requires/exporter/_exporterContracts.ts b/src/node_requires/exporter/_exporterContracts.ts index 866f92a52..3fec1352d 100644 --- a/src/node_requires/exporter/_exporterContracts.ts +++ b/src/node_requires/exporter/_exporterContracts.ts @@ -222,7 +222,7 @@ export type ExportedStyle = { dropShadowDistance?: number; } -export type ExportedSound = Omit & +export type ExportedSound = Omit & Partial>; export type ExportedBehaviorDynamic = { diff --git a/src/node_requires/exporter/fonts.ts b/src/node_requires/exporter/fonts.ts index 2ac37e1d5..8105bf872 100644 --- a/src/node_requires/exporter/fonts.ts +++ b/src/node_requires/exporter/fonts.ts @@ -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', diff --git a/src/node_requires/exporter/icons.ts b/src/node_requires/exporter/icons.ts index a8f2d6088..3be76d174 100644 --- a/src/node_requires/exporter/icons.ts +++ b/src/node_requires/exporter/icons.ts @@ -11,7 +11,7 @@ export const resizeIcon = async function ( soft: boolean ): Promise { 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 diff --git a/src/node_requires/exporter/rooms.ts b/src/node_requires/exporter/rooms.ts index 6b94f06a1..be13ffbf1 100644 --- a/src/node_requires/exporter/rooms.ts +++ b/src/node_requires/exporter/rooms.ts @@ -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; diff --git a/src/node_requires/exporter/scriptableProcessor.ts b/src/node_requires/exporter/scriptableProcessor.ts index 363bc8f75..0cdf6c1cc 100644 --- a/src/node_requires/exporter/scriptableProcessor.ts +++ b/src/node_requires/exporter/scriptableProcessor.ts @@ -43,7 +43,7 @@ const populateEventCache = async (project: IProject): Promise { return eventsCache[cacheName]; }; -// eslint-disable-next-line max-lines-per-function +// eslint-disable-next-line max-lines-per-function, complexity const getBaseScripts = function (entity: IScriptable, project: IProject): ScriptableCode { const domains = { thisOnStep: '', @@ -135,6 +135,9 @@ const getBaseScripts = function (entity: IScriptable, project: IProject): Script // Add a preamble to each event for easier debugging by users resultingCode = `/* ${entity.type} ${entity.name} — ${event.lib}_${event.eventKey} (${eventSpec.name} event) */\n`; if (lib === 'core') { + if (!eventSpec.inlineCodeTemplates) { + throw new Error(`Found a misconfuguration in event ${event.lib}_${event.eventKey} (no inlineCodeTemplate for ${target}). This is a ct.js bug.`); + } resultingCode += eventSpec.inlineCodeTemplates[target]; } else { resultingCode += getFromCache(event, target); @@ -151,7 +154,7 @@ const getBaseScripts = function (entity: IScriptable, project: IProject): Script throw exporterError; } const exp = new RegExp(`/\\*%%${argCode}%%\\*/`, 'g'); - const argType = eventSpec.arguments[argCode].type; + const argType = eventSpec.arguments![argCode].type; if (['template', 'room', 'sound', 'tandem', 'font', 'style', 'texture'].indexOf(argType) !== -1) { const value = getName(getById(argType, String(eventArgs[argCode]))); resultingCode = resultingCode.replace(exp, `'${value.replace(/'/g, '\\\'')}'`); diff --git a/src/node_requires/exporter/sounds.ts b/src/node_requires/exporter/sounds.ts index 6bc47b59e..b01c8766d 100644 --- a/src/node_requires/exporter/sounds.ts +++ b/src/node_requires/exporter/sounds.ts @@ -14,21 +14,22 @@ export const getSounds = (input: ISound[]): ExportedSound[] => { }); throw exporterError; } - - sounds.push({ + const out: ExportedSound = { name: s.name, variants: s.variants.map((v) => ({ uid: v.uid, source: `./snd/${v.uid}.${v.source.slice(-3)}` })), preload: s.preload, - volume: (s.volume.enabled && s.volume) || void 0, - pitch: (s.pitch.enabled && s.pitch) || void 0, - distortion: (s.distortion.enabled && s.distortion) || void 0, - reverb: (s.reverb.enabled && s.reverb) || void 0, - eq: (s.eq.enabled && s.eq) || void 0, panning: s.panning - }); + }; + const keys = ['volume', 'pitch', 'eq', 'distortion', 'reverb'] as const; + for (const k of keys) { + if (s[k].enabled) { + (out as any)[k] = s[k]; + } + } + sounds.push(out); } return sounds; }; diff --git a/src/node_requires/exporter/templates.ts b/src/node_requires/exporter/templates.ts index ac1b09d46..125aa5ad9 100644 --- a/src/node_requires/exporter/templates.ts +++ b/src/node_requires/exporter/templates.ts @@ -41,7 +41,7 @@ const getBaseClassInfo = (blankTextures: IBlankTexture[], template: ITemplate) = anchorY: ${blankTexture.anchorY}, height: ${blankTexture.height}, width: ${blankTexture.width},`; - } else if (template.texture !== -1) { + } else if (template.texture && template.texture !== -1) { classInfo += ` texture: "${getById('texture', template.texture).name}",`; } else { @@ -86,13 +86,13 @@ const getBaseClassInfo = (blankTextures: IBlankTexture[], template: ITemplate) = } if (hasCapability(bc, 'scroller')) { classInfo += ` - scrollX: ${template.tilingSettings.scrollSpeedX}, - scrollY: ${template.tilingSettings.scrollSpeedY}, - isUi: ${template.tilingSettings.isUi},`; + scrollX: ${template.tilingSettings!.scrollSpeedX}, + scrollY: ${template.tilingSettings!.scrollSpeedY}, + isUi: ${template.tilingSettings!.isUi},`; } if (hasCapability(bc, 'repeater')) { classInfo += ` - spriteCount: ${template.repeaterSettings.defaultCount},`; + spriteCount: ${template.repeaterSettings!.defaultCount},`; } return classInfo; }; diff --git a/src/node_requires/exporter/textures.ts b/src/node_requires/exporter/textures.ts index 48fb48fb5..a5f71b601 100644 --- a/src/node_requires/exporter/textures.ts +++ b/src/node_requires/exporter/textures.ts @@ -36,23 +36,23 @@ export const getTextureShape = (texture: ITexture): TextureShape => { if (texture.shape === 'rect') { return { type: 'rect', - top: texture.top, - bottom: texture.bottom, - left: texture.left, - right: texture.right + top: texture.top || 0, + bottom: texture.bottom || 0, + left: texture.left || 0, + right: texture.right || 0 }; } if (texture.shape === 'circle') { return { type: 'circle', - r: texture.r + r: texture.r || 0 }; } if (texture.shape === 'strip') { return { type: 'strip', - points: texture.stripPoints, - closedStrip: texture.closedStrip + points: texture.stripPoints!, + closedStrip: texture.closedStrip || false }; } return { @@ -145,9 +145,8 @@ const drawAtlasFromBin = (bin: packerBin, binInd: number) => { const atlas = document.createElement('canvas'); atlas.width = bin.width; atlas.height = bin.height; - const cx = atlas.getContext('2d'); + const cx = atlas.getContext('2d')!; cx.imageSmoothingQuality = 'low'; - // eslint-disable-next-line id-length cx.imageSmoothingEnabled = false; const atlasJSON = { @@ -253,7 +252,7 @@ const getPackerFor = (textures: ITexture[], spritedTextures: ITexture[]) => { const packer = new Packer(...packerSettings); const animationsByTextures = spritedTextures .map(getTextureFrameCrops); - const animations = [].concat(...animationsByTextures); + const animations = ([] as typeof animationsByTextures[0]).concat(...animationsByTextures); const getFailedPacks = () => { const failedPacks: string[] = []; const allTags: Record = {}; @@ -369,7 +368,7 @@ export const packImages = async ( // Output all the atlases into JSON and WebP files const atlases: string[] = []; packer.bins.map(drawAtlasFromBin).forEach((atlas: exportedTextureAtlas, ind: number) => { - let jsonHash: string; + let jsonHash: string | undefined; for (const format of formats) { const atlasBase64 = atlas.canvas.toDataURL(`image/${format}`, 1).replace(/^data:image\/\w+;base64,/, ''); const buf = Buffer.from(atlasBase64, 'base64'); @@ -391,7 +390,7 @@ export const packImages = async ( } } if (production) { - atlases.push(`./img/a${ind}${pixiMask}.${jsonHash}.json`); + atlases.push(`./img/a${ind}${pixiMask}.${jsonHash!}.json`); } else { atlases.push(`./img/a${ind}${pixiMask}.json`); } @@ -411,7 +410,7 @@ export const packImages = async ( } const ind = packer.bins.length + btInd; const atlas = drawAtlasFromBin(bigPacker.bins[0], ind); - let jsonHash: string; + let jsonHash: string | undefined; for (const format of formats) { const atlasBase64 = atlas.canvas.toDataURL(`image/${format}`, 1).replace(/^data:image\/\w+;base64,/, ''); const buf = Buffer.from(atlasBase64, 'base64'); @@ -422,7 +421,7 @@ export const packImages = async ( if (!jsonHash) { jsonHash = revHash(json); } - writePromises.push(fs.outputJSON(`${writeDir}/img/a${ind}.${format}.${jsonHash}.json`, atlas.json)); + writePromises.push(fs.outputJSON(`${writeDir}/img/a${ind}.${format}.${jsonHash!}.json`, atlas.json)); writePromises.push(fs.writeFile(`${writeDir}/img/a${ind}.${imageHash}.${format}`, buf)); } else { writePromises.push(fs.outputJSON(`${writeDir}/img/a${ind}.${format}.json`, atlas.json)); @@ -430,7 +429,7 @@ export const packImages = async ( } } if (production) { - atlases.push(`./img/a${ind}${pixiMask}.${jsonHash}.json`); + atlases.push(`./img/a${ind}${pixiMask}.${jsonHash!}.json`); } else { atlases.push(`./img/a${ind}${pixiMask}.json`); } @@ -442,7 +441,7 @@ export const packImages = async ( for (const tex of tiledTextures) { const atlas = document.createElement('canvas'), img = getDOMTexture(tex); - const cx = atlas.getContext('2d'); + const cx = atlas.getContext('2d')!; atlas.width = tex.width; atlas.height = tex.height; cx.drawImage(img, 0, 0); @@ -455,15 +454,15 @@ export const packImages = async ( } }; // Use one hash for both formats to simplify loading on Pixi.js side. - let imageHash: string; + let imageHash: string | undefined; for (const format of formats) { const buf = Buffer.from(atlas.toDataURL(`image/${format}`, 1).replace(/^data:image\/\w+;base64,/, ''), 'base64'); if (production) { if (!imageHash) { imageHash = revHash(buf); - tiledImages[tex.name].source = `./img/t${tiledCounter}.${imageHash}${pixiMask}`; + tiledImages[tex.name].source = `./img/t${tiledCounter}.${imageHash!}${pixiMask}`; } - writePromises.push(fs.writeFile(`${writeDir}/img/t${tiledCounter}.${imageHash}.${format}`, buf)); + writePromises.push(fs.writeFile(`${writeDir}/img/t${tiledCounter}.${imageHash!}.${format}`, buf)); } else { writePromises.push(fs.writeFile(`${writeDir}/img/t${tiledCounter}.${format}`, buf)); } diff --git a/src/node_requires/exporter/utils.ts b/src/node_requires/exporter/utils.ts index 9e47c4859..919604fa6 100644 --- a/src/node_requires/exporter/utils.ts +++ b/src/node_requires/exporter/utils.ts @@ -36,7 +36,7 @@ export const getUnwrappedExtends = (exts: Record): Record e.lib !== 'core' && e.lib) + const missingCatmods = source.events!.filter(e => e.lib !== 'core' && e.lib) .map(e => e.lib); if (missingCatmods.length) { const message = getByPath('createAsset.behaviorMissingCatmods') @@ -43,7 +43,7 @@ export const createAsset = async (opts: { alertify.warn(message); throw new Error(message); } - const behavior = getDefaultBehavior(source.behaviorType); + const behavior = getDefaultBehavior(source.behaviorType!); Object.assign(behavior, source); return behavior; } @@ -103,6 +103,9 @@ export const assetContextMenuItems: IAssetContextItem[] = [{ } const copy = { ...asset + } as Omit & { + uid?: string; + lastmod?: number; }; delete copy.uid; delete copy.lastmod; diff --git a/src/node_requires/resources/emitterTandems/index.ts b/src/node_requires/resources/emitterTandems/index.ts index 5b7d69dda..019af0d10 100644 --- a/src/node_requires/resources/emitterTandems/index.ts +++ b/src/node_requires/resources/emitterTandems/index.ts @@ -34,7 +34,7 @@ const createNewTandem = async (opts: {src?: string}): Promise => { return tandem; } // Importing from file - const source = YAML.load(await readFile(opts.src)) as Partial; + const source = YAML.load(await readFile(opts.src!)) as Partial; const keys: (keyof ITandem)[] = [ 'name', 'type', @@ -88,6 +88,12 @@ export const assetContextMenuItems: IAssetContextItem[] = [{ } const copy = { ...asset + } as { + type: 'tandem'; + name: string; + emitters: Partial[]; + uid?: string; + lastmod?: number; }; delete copy.uid; delete copy.lastmod; diff --git a/src/node_requires/resources/index.ts b/src/node_requires/resources/index.ts index 9beea0637..787024a1d 100644 --- a/src/node_requires/resources/index.ts +++ b/src/node_requires/resources/index.ts @@ -207,11 +207,11 @@ export const isNameOccupied = (type: resourceType, name: string): boolean => { return false; }; -export const getFolderById = (uid: string | null): IAssetFolder => { +export const getFolderById = (uid: string): IAssetFolder | null => { const recursiveFolderWalker = ( uid: string, collection: folderEntries - ): IAssetFolder => { + ): IAssetFolder | null => { for (const entry of collection) { if (entry.type === 'folder') { if (entry.uid === uid) { @@ -250,7 +250,11 @@ export const getParentFolder = (object: IAsset | IAssetFolder): IAssetFolder | n }; return recursiveFolderWalker(object, window.currentProject.assets, null); } - return folderMap.get(object); + const out = folderMap.get(object); + if (!out) { + throw new Error(`Cannot get parent folder for ${object.type} with ID ${object.uid}`); + } + return out; }; /** @@ -325,7 +329,7 @@ export const createFolder = (parentFolder: IAssetFolder | null): IAssetFolder => * If set to `null`, the asset is moved to the project's root. */ export const moveAsset = (asset: IAsset, newFolder: IAssetFolder | null): void => { - const oldCollection = collectionMap.get(asset); + const oldCollection = collectionMap.get(asset)!; const newCollection = newFolder === null ? window.currentProject.assets : newFolder.entries; oldCollection.splice(oldCollection.indexOf(asset), 1); collectionMap.delete(asset); @@ -380,7 +384,7 @@ export const moveFolder = ( export const deleteAsset = async (asset: IAsset): Promise => { // Execute additional cleanup steps for this asset type, if applicable if ('removeAsset' in typeToApiMap[asset.type]) { - await typeToApiMap[asset.type].removeAsset(asset); + await typeToApiMap[asset.type].removeAsset!(asset); } // Clear asset references from content types' entries for (const contentType of window.currentProject.contentTypes) { @@ -405,7 +409,7 @@ export const deleteAsset = async (asset: IAsset): Promise => { } } // Remove from the parent folder - const collection = collectionMap.get(asset); + const collection = collectionMap.get(asset)!; collection.splice(collection.indexOf(asset), 1); // Clear references from converting maps and uidMap.delete(asset.uid); @@ -492,7 +496,7 @@ export const getName = (asset: IAsset | IAssetFolder): string => { return asset.name; } return typeToApiMap[asset.type].getName ? - typeToApiMap[asset.type].getName(asset) : + typeToApiMap[asset.type].getName!(asset) : (asset as IAsset & {name: string}).name; }; export const getContextActions = ( @@ -509,12 +513,12 @@ export const getContextActions = ( label: getByPath(item.vocPath) as string, icon: item.icon, click: async () => { - await item.action(asset, collectionMap.get(asset), folderMap.get(asset)); + await item.action(asset, collectionMap.get(asset)!, folderMap.get(asset) || null); if (callback) { callback(asset); } }, - checked: item.checked && (() => item.checked(asset)) + checked: item.checked && (() => item.checked!(asset)) })); return actions; }; diff --git a/src/node_requires/resources/modules/index.ts b/src/node_requires/resources/modules/index.ts index 96fec1c0f..87c5a3aee 100644 --- a/src/node_requires/resources/modules/index.ts +++ b/src/node_requires/resources/modules/index.ts @@ -127,6 +127,9 @@ const addDefaults = async (moduleName: string, moduleData?: ICatmodManifest) => return; } for (const field of moduleData.fields) { + if (!field.key) { + continue; + } if (!global.currentProject.libs[moduleName][field.key]) { if (field.default) { global.currentProject.libs[moduleName][field.key] = field.default; diff --git a/src/node_requires/resources/preview/font.ts b/src/node_requires/resources/preview/font.ts index 51ddd0faf..2a55fbbfe 100644 --- a/src/node_requires/resources/preview/font.ts +++ b/src/node_requires/resources/preview/font.ts @@ -43,7 +43,7 @@ export class FontPreviewer { document.fonts.add(loaded); const canvas = document.createElement('canvas'); - const cx = canvas.getContext('2d'); + const cx = canvas.getContext('2d')!; canvas.width = canvas.height = 128; cx.clearRect(0, 0, 128, 128); cx.font = `${font.italic ? 'italic ' : ''}${font.weight} ${Math.floor(96 * 0.75)}px "${loaded.family}"`; diff --git a/src/node_requires/resources/projects/defaultProject.ts b/src/node_requires/resources/projects/defaultProject.ts index fc148e7d2..63857abe9 100644 --- a/src/node_requires/resources/projects/defaultProject.ts +++ b/src/node_requires/resources/projects/defaultProject.ts @@ -1,5 +1,5 @@ const defaultProjectTemplate: IProject = { - ctjsVersion: process.versions.ctjs, + ctjsVersion: process.versions.ctjs as string, backups: 3, language: 'typescript', notes: '/* empty */', diff --git a/src/node_requires/resources/projects/index.ts b/src/node_requires/resources/projects/index.ts index ff4bb3ee2..eb4224bce 100644 --- a/src/node_requires/resources/projects/index.ts +++ b/src/node_requires/resources/projects/index.ts @@ -15,15 +15,18 @@ import fs from 'fs-extra'; // @see https://semver.org/ const semverRegex = /(\d+)\.(\d+)\.(\d+)(-[A-Za-z.-]*(\d+)?[A-Za-z.-]*)?/; -const semverToArray = (string: string) => { +const semverToArray = (string: string): [number, number, number, number | null] => { const raw = semverRegex.exec(string); + if (!raw) { + throw new Error(`Invalid semver string: ${string}`); + } return [ - raw[1], - raw[2], - raw[3], + Number(raw[1]), + Number(raw[2]), + Number(raw[3]), // -next- versions and other postfixes will count as a fourth component. // They all will apply before regular versions - raw[4] ? raw[5] || 1 : null + raw[4] ? Number(raw[5]) || 1 : null ]; }; @@ -45,9 +48,13 @@ const adapter = async (project: Partial) => { const m2Version = semverToArray(m2.version); for (let i = 0; i < 4; i++) { - if (m1Version[i] < m2Version[i] || m1Version[i] === null) { + if (m1Version[i] === null) { + return -1; + } else if (m2Version[i] === null) { + return 1; + } else if (m1Version[i]! < m2Version[i]!) { return -1; - } else if (m1Version[i] > m2Version[i]) { + } else if (m1Version[i]! > m2Version[i]!) { return 1; } } @@ -61,10 +68,10 @@ const adapter = async (project: Partial) => { for (let i = 0; i < 3; i++) { // if any of the first three version numbers is lower than project's, // skip the patch - if (migrationVersion[i] < version[i]) { + if ((migrationVersion[i] || 0) < (version[i] || 0)) { return false; } - if (migrationVersion[i] > version[i]) { + if ((migrationVersion[i] || 0) > (version[i] || 0)) { return true; } } diff --git a/src/node_requires/resources/projects/scripts.ts b/src/node_requires/resources/projects/scripts.ts index 70c7b7e3d..6329ee4c0 100644 --- a/src/node_requires/resources/projects/scripts.ts +++ b/src/node_requires/resources/projects/scripts.ts @@ -3,7 +3,7 @@ type Script = IProject['scripts'][0]; export const scriptModels = new Map(); export const dropScriptModel = (script: Script) => { - scriptModels.get(script).dispose(); + scriptModels.get(script)?.dispose(); scriptModels.delete(script); }; /** This method is to be used when loading a project or creating a new script */ diff --git a/src/node_requires/resources/rooms/index.ts b/src/node_requires/resources/rooms/index.ts index 0c76468fc..50cf13002 100644 --- a/src/node_requires/resources/rooms/index.ts +++ b/src/node_requires/resources/rooms/index.ts @@ -6,7 +6,7 @@ import generateGUID from './../../generateGUID'; const getDefaultRoom = require('./defaultRoom').get; const fs = require('fs-extra'); -const createNewRoom = async (name?: string): Promise => { +const createNewRoom = async (name?: string): Promise => { const room = getDefaultRoom(); if (name) { room.name = String(name); @@ -29,7 +29,11 @@ const createNewRoom = async (name?: string): Promise => { export const getStartingRoom = (): IRoom => { const rooms = getOfType('room'); if (global.currentProject.startroom && global.currentProject.startroom !== -1) { - return rooms.find(room => room.uid === global.currentProject.startroom); + const room = rooms.find(room => room.uid === global.currentProject.startroom); + if (room) { + return room; + } + global.currentProject.startroom = -1; } return rooms[0]; }; diff --git a/src/node_requires/resources/styles/index.ts b/src/node_requires/resources/styles/index.ts index 4da7ede47..ca5b08b75 100644 --- a/src/node_requires/resources/styles/index.ts +++ b/src/node_requires/resources/styles/index.ts @@ -61,11 +61,15 @@ export const assetContextMenuItems: IAssetContextItem[] = [{ ): Promise => { let template: ITemplate; if (getOfType('template').some(t => t.name === asset.name)) { - template = await createProjAsset('template', folder); + const answer = await createProjAsset('template', folder); + if (!answer) { + return; + } + template = answer; } else { - template = await createProjAsset('template', folder, { + template = (await createProjAsset('template', folder, { name: asset.name - }); + }))!; } template.baseClass = 'Text'; Object.assign(template, getBaseClassFields('Text')); diff --git a/src/node_requires/resources/templates/index.ts b/src/node_requires/resources/templates/index.ts index 8a2881f27..a8d3c8198 100644 --- a/src/node_requires/resources/templates/index.ts +++ b/src/node_requires/resources/templates/index.ts @@ -186,7 +186,9 @@ export const getTemplatePreview = function getTemplatePreview( if (template.baseClass === 'Container') { return TexturePreviewer.get(-1, fs); } - return TexturePreviewer.get(template.texture === -1 ? -1 : getById('texture', template.texture), fs); + return TexturePreviewer.get((!template.texture || template.texture === -1) ? + -1 : + getById('texture', template.texture), fs); }; export const getThumbnail = getTemplatePreview; export const areThumbnailsIcons = false; @@ -201,7 +203,7 @@ export const getTemplateTextureOrig = (template: ITemplate | assetRef, fs: boole if (template === -1) { throw new Error('Cannot work with -1 assetRefs'); } - return getTextureOrig(template.texture, fs); + return getTextureOrig(template.texture || -1, fs); }; export const getPixiTexture = (template: ITemplate | assetRef): @@ -212,7 +214,7 @@ PIXI.Texture[] => { if (template === -1) { throw new Error('Cannot work with -1 assetRefs'); } - return getTexturePixiTexture(template.texture, void 0, true); + return getTexturePixiTexture(template.texture || -1, void 0, true); }; export const getDOMTexture = (template: ITemplate | assetRef): HTMLImageElement => { @@ -222,7 +224,7 @@ export const getDOMTexture = (template: ITemplate | assetRef): HTMLImageElement if (typeof template === 'string') { template = getById('template', template); } - return getTextureDOMImage(template.texture); + return getTextureDOMImage(template.texture || -1); }; export const assetContextMenuItems: IAssetContextItem[] = [{ diff --git a/src/node_requires/resources/textures/index.ts b/src/node_requires/resources/textures/index.ts index 6bfcc8c45..fb239c701 100644 --- a/src/node_requires/resources/textures/index.ts +++ b/src/node_requires/resources/textures/index.ts @@ -319,13 +319,17 @@ const importImageToTexture = async (opts: { const exec = texturePostfixParser.exec(obj.name); if (exec) { obj.name = obj.name.replace(texturePostfixParser, ''); - obj.grid = [Number(exec.groups.cols) || 1, Number(exec.groups.rows) || 1]; + obj.grid = [Number(exec.groups!.cols) || 1, Number(exec.groups!.rows) || 1]; obj.width /= obj.grid[0]; obj.height /= obj.grid[1]; - obj.right /= obj.grid[0]; - obj.bottom /= obj.grid[1]; - if (exec.groups.until) { - obj.untill = Number(exec.groups.until); + if (obj.right) { + obj.right /= obj.grid[0]; + } + if (obj.bottom) { + obj.bottom /= obj.grid[1]; + } + if (exec.groups!.until) { + obj.untill = Number(exec.groups!.until); } } else if (isBgPostfixTester.test(obj.name)) { // Test whether it has a @bg postfix @@ -443,7 +447,7 @@ const removeTexture = (tex: string | ITexture): void => { } } if (global.currentProject.settings.branding.icon === uid) { - delete global.currentProject.settings.branding.icon; + global.currentProject.settings.branding.icon = -1; } }; @@ -485,12 +489,16 @@ export const assetContextMenuItems: IAssetContextItem[] = [{ ): Promise => { if (getOfType('template').some(t => t.name === asset.name)) { const template = await createAsset('template', folder); - template.texture = asset.uid; + if (template) { + template.texture = asset.uid; + } } else { const template = await createAsset('template', folder, { name: asset.name }); - template.texture = asset.uid; + if (template) { + template.texture = asset.uid; + } } } }]; diff --git a/src/node_requires/riotMixins/discardio.ts b/src/node_requires/riotMixins/discardio.ts index e5495677b..c5d376dff 100644 --- a/src/node_requires/riotMixins/discardio.ts +++ b/src/node_requires/riotMixins/discardio.ts @@ -25,16 +25,16 @@ const discardio = (riotTag: IRiotTag) => { discardioSources.set(riotTag, riotTag.opts.asset); riotTag.asset = structuredClone(riotTag.opts.asset); riotTag.writeChanges = (): void => { - riotTag.asset.lastmod = Number(new Date()); - const sourceObject = discardioSources.get(riotTag); - const changedObject = riotTag.asset; + riotTag.asset!.lastmod = Number(new Date()); + const sourceObject = discardioSources.get(riotTag)!; + const changedObject = riotTag.asset!; // update the innards of the object without creating a new one for (const key of Object.keys(sourceObject)) { delete sourceObject[key as keyof typeof sourceObject]; } Object.assign(sourceObject, changedObject); - window.signals.trigger('assetChanged', riotTag.asset.uid); - window.signals.trigger(`${riotTag.asset.type}Changed`, riotTag.asset.uid); + window.signals.trigger('assetChanged', riotTag.asset!.uid); + window.signals.trigger(`${riotTag.asset!.type}Changed`, riotTag.asset!.uid); }; riotTag.discardChanges = (): void => { riotTag.asset = structuredClone(discardioSources.get(riotTag)); @@ -45,7 +45,7 @@ const discardio = (riotTag: IRiotTag) => { ); const renamer = (payload: [string, string]) => { const [uid, name] = payload; - if (riotTag.asset.uid === uid) { + if (riotTag.asset!.uid === uid) { (riotTag.asset as IAsset & {name: string}).name = name; } }; diff --git a/src/node_requires/roomEditor/common.ts b/src/node_requires/roomEditor/common.ts index fbcd7dae6..fb09dbdcf 100644 --- a/src/node_requires/roomEditor/common.ts +++ b/src/node_requires/roomEditor/common.ts @@ -144,7 +144,7 @@ export const getBindingsForBaseClass = (baseClass: TemplateBaseClass): CopyBindi if (!(capability in bindingsMap)) { continue; } - for (const binding of bindingsMap[capability]) { + for (const binding of bindingsMap[capability]!) { if (!bindings.includes(binding)) { bindings.push(binding); } diff --git a/src/node_requires/roomEditor/entityClasses/Background.ts b/src/node_requires/roomEditor/entityClasses/Background.ts index cd29df912..225c09dba 100644 --- a/src/node_requires/roomEditor/entityClasses/Background.ts +++ b/src/node_requires/roomEditor/entityClasses/Background.ts @@ -40,13 +40,13 @@ class Background extends PIXI.TilingSprite { } this.editor.backgrounds.splice(ind, 1); this.parent.removeChild(this); - this.editor.riotEditor?.refs.backgroundsEditor.update(); + this.editor.riotEditor?.refs.backgroundsEditor?.update(); return this; } restore(): this { this.editor.backgrounds.push(this); (this.editor.room.addChild as any)(this); - this.editor.riotEditor?.refs.backgroundsEditor.update(); + this.editor.riotEditor?.refs.backgroundsEditor?.update(); return this; } diff --git a/src/node_requires/roomEditor/entityClasses/Copy.ts b/src/node_requires/roomEditor/entityClasses/Copy.ts index d4917141d..471f71095 100644 --- a/src/node_requires/roomEditor/entityClasses/Copy.ts +++ b/src/node_requires/roomEditor/entityClasses/Copy.ts @@ -125,7 +125,7 @@ class Copy extends PIXI.Container { } } updateTilingSprite(): void { - const tiling = this.tilingSprite, + const tiling = this.tilingSprite!, template = this.cachedTemplate; if (hasCapability(template.baseClass, 'tilingSprite') && template.baseClass !== 'SpritedCounter') { tiling.scale.set( @@ -135,7 +135,7 @@ class Copy extends PIXI.Container { tiling.width = tiling.initialWidth * this.scale.x; tiling.height = tiling.initialHeight * this.scale.y; } else if (template.baseClass === 'SpritedCounter') { - tiling.width = template.repeaterSettings.defaultCount * tiling.initialWidth; + tiling.width = template.repeaterSettings!.defaultCount * tiling.initialWidth; tiling.height = tiling.initialHeight; } } @@ -185,17 +185,19 @@ class Copy extends PIXI.Container { copy.align = this.align; } if (this.text) { - if (this.customTextSettings.anchor) { - copy.customAnchor = this.customTextSettings.anchor; - } - if (this.customTextSettings.wordWrapWidth) { - copy.customWordWrap = this.customTextSettings.wordWrapWidth; - } - if (this.customTextSettings.fontSize) { - copy.customSize = this.customTextSettings.fontSize; - } - if (this.customTextSettings.customText) { - copy.customText = this.customTextSettings.customText; + if (this.customTextSettings) { + if (this.customTextSettings.anchor) { + copy.customAnchor = this.customTextSettings.anchor; + } + if (this.customTextSettings.wordWrapWidth) { + copy.customWordWrap = this.customTextSettings.wordWrapWidth; + } + if (this.customTextSettings.fontSize) { + copy.customSize = this.customTextSettings.fontSize; + } + if (this.customTextSettings.customText) { + copy.customText = this.customTextSettings.customText; + } } } return copy; @@ -233,7 +235,7 @@ class Copy extends PIXI.Container { if (t.playAnimationOnStart) { this.sprite.play(); } - if (t.texture !== -1) { + if (t.texture && t.texture !== -1) { [this.sprite.anchor.x, this.sprite.anchor.y] = getTexturePivot(t.texture); } else { this.sprite.anchor.x = this.sprite.anchor.y = 0.5; @@ -260,19 +262,19 @@ class Copy extends PIXI.Container { if (copy.customText) { this.customTextSettings.customText = copy.customText; } - const style: Partial | false = t.textStyle && (t.textStyle !== -1) && + const style: Partial | false = (t.textStyle && (t.textStyle !== -1) && (Object.assign( {}, styleToTextStyle(getById('style', t.textStyle)), blends - ) as unknown as Partial); + ) as unknown as Partial)) || false; // ts is drunk let text = copy.customText || this.cachedTemplate.defaultText || getByPath('roomView.emptyTextFiller') as string; if (this.cachedTemplate.fieldType === 'password') { text = '•'.repeat(text.length); } - this.text = new PIXI.Text(text, style); + this.text = new PIXI.Text(text, style || {}); this.addChild(this.text); if (copy.customAnchor) { this.customTextSettings.anchor = { @@ -282,20 +284,20 @@ class Copy extends PIXI.Container { } } if (hasCapability(t.baseClass, 'ninePatch')) { - this.nineSlicePlane = - new PIXI.NineSlicePlane(getPixiTexture(copy.uid)[0]) as Copy['nineSlicePlane']; - this.nineSlicePlane.initialWidth = this.nineSlicePlane.width; - this.nineSlicePlane.initialHeight = this.nineSlicePlane.height; - this.nineSlicePlane.topHeight = t.nineSliceSettings.top; - this.nineSlicePlane.bottomHeight = t.nineSliceSettings.bottom; - this.nineSlicePlane.leftWidth = t.nineSliceSettings.left; - this.nineSlicePlane.rightWidth = t.nineSliceSettings.right; - this.addChildAt(this.nineSlicePlane, 0); + const nsp = new PIXI.NineSlicePlane(getPixiTexture(copy.uid)[0]) as Exclude; + this.nineSlicePlane = nsp; + nsp.initialWidth = nsp.width; + nsp.initialHeight = nsp.height; + nsp.topHeight = t.nineSliceSettings!.top; + nsp.bottomHeight = t.nineSliceSettings!.bottom; + nsp.leftWidth = t.nineSliceSettings!.left; + nsp.rightWidth = t.nineSliceSettings!.right; + this.addChildAt(nsp, 0); this.updateNinePatch(); if (this.text) { this.text.anchor.set(0.5); - this.text.x = this.nineSlicePlane.initialWidth / 2; - this.text.y = this.nineSlicePlane.initialHeight / 2; + this.text.x = nsp.initialWidth / 2; + this.text.y = nsp.initialHeight / 2; } } if (hasCapability(t.baseClass, 'tilingSprite')) { @@ -304,13 +306,13 @@ class Copy extends PIXI.Container { tex, tex.width, tex.height - ) as Copy['tilingSprite']; + ) as Exclude; this.tilingSprite.initialWidth = this.tilingSprite.width; this.tilingSprite.initialHeight = this.tilingSprite.height; this.tilingSprite.anchor.set(0); if (hasCapability(t.baseClass, 'scroller')) { - this.tilingSprite.scrollSpeedX = t.tilingSettings.scrollSpeedX; - this.tilingSprite.scrollSpeedY = t.tilingSettings.scrollSpeedY; + this.tilingSprite.scrollSpeedX = t.tilingSettings!.scrollSpeedX; + this.tilingSprite.scrollSpeedY = t.tilingSettings!.scrollSpeedY; } else { this.tilingSprite.scrollSpeedX = 0; this.tilingSprite.scrollSpeedY = 0; @@ -332,7 +334,7 @@ class Copy extends PIXI.Container { const t = this.cachedTemplate; if (this.sprite) { this.sprite.textures = getPixiTexture(t); - if (t.texture !== -1) { + if (t.texture && t.texture !== -1) { [this.sprite.anchor.x, this.sprite.anchor.y] = getTexturePivot(t.texture); } else { this.sprite.anchor.x = this.sprite.anchor.y = 0.5; @@ -344,7 +346,10 @@ class Copy extends PIXI.Container { } } updateText(): void { - const cts = this.customTextSettings; + if (!this.text) { + return; + } + const cts = this.customTextSettings || {}; this.text.text = cts.customText || this.cachedTemplate.defaultText || getByPath('roomView.emptyTextFiller') as string; @@ -355,9 +360,9 @@ class Copy extends PIXI.Container { this.text.style.wordWrapWidth = Number(cts.wordWrapWidth); this.text.style.wordWrap = true; } else { - this.text.style.wordWrap = this.cachedTemplate.textStyle && + this.text.style.wordWrap = (this.cachedTemplate.textStyle && this.cachedTemplate.textStyle !== -1 && - getById('style', this.cachedTemplate.textStyle).font.wrap; + getById('style', this.cachedTemplate.textStyle).font.wrap) || false; } if (cts.fontSize) { this.text.style.fontSize = Number(cts.fontSize); diff --git a/src/node_requires/roomEditor/entityClasses/SnapTarget.ts b/src/node_requires/roomEditor/entityClasses/SnapTarget.ts index 41895e9b2..85ee3efe8 100644 --- a/src/node_requires/roomEditor/entityClasses/SnapTarget.ts +++ b/src/node_requires/roomEditor/entityClasses/SnapTarget.ts @@ -53,13 +53,13 @@ export class SnapTarget extends PIXI.Container { this.ghost.visible = spritelike; this.ghostText.visible = textlike; if (spritelike) { - if (currentTemplate.texture === -1 && + if ((!currentTemplate.texture || currentTemplate.texture === -1) && this.ghost.textures !== unknownTextures ) { this.updateGhost(-1); this.ghost.textures = unknownTextures; } - if (currentTemplate.texture !== -1 && + if (currentTemplate.texture && currentTemplate.texture !== -1 && this.prevGhostTex !== getById('texture', currentTemplate.texture) ) { this.updateGhost(currentTemplate.texture); diff --git a/src/node_requires/roomEditor/entityClasses/Tile.ts b/src/node_requires/roomEditor/entityClasses/Tile.ts index fb8927504..1d863ba73 100644 --- a/src/node_requires/roomEditor/entityClasses/Tile.ts +++ b/src/node_requires/roomEditor/entityClasses/Tile.ts @@ -12,7 +12,6 @@ import {RoomEditorPreview} from '../previewer'; class Tile extends PIXI.Sprite { tileTexture: assetRef; tileFrame: number; - parent: TileLayer | null; editor: RoomEditor | RoomEditorPreview; isGhost: boolean; diff --git a/src/node_requires/roomEditor/entityClasses/TileLayer.ts b/src/node_requires/roomEditor/entityClasses/TileLayer.ts index 04972115d..7beb33dc3 100644 --- a/src/node_requires/roomEditor/entityClasses/TileLayer.ts +++ b/src/node_requires/roomEditor/entityClasses/TileLayer.ts @@ -10,9 +10,8 @@ export const resetCounter = (): void => { idCounter = 0; }; -export class TileLayer extends PIXI.Container { +export class TileLayer extends PIXI.Container { extends: Record; - children: Tile[]; editor: RoomEditor | RoomEditorPreview; id: number; shouldCache: boolean; diff --git a/src/node_requires/roomEditor/entityClasses/Transformer.ts b/src/node_requires/roomEditor/entityClasses/Transformer.ts index 74438e2f5..09f14d079 100644 --- a/src/node_requires/roomEditor/entityClasses/Transformer.ts +++ b/src/node_requires/roomEditor/entityClasses/Transformer.ts @@ -118,7 +118,7 @@ export class Transformer extends PIXI.Container { } this.visible = true; - let rect; + let rect: PIXI.Rectangle | undefined; for (const elt of this.editor.currentSelection) { const w = elt.width, h = elt.height, @@ -154,10 +154,10 @@ export class Transformer extends PIXI.Container { rotation: elt.rotation }); } - this.frameWidth = rect.width; - this.frameHeight = rect.height; - this.transformPivotX = rect.x + rect.width / 2; - this.transformPivotY = rect.y + rect.height / 2; + this.frameWidth = rect!.width; + this.frameHeight = rect!.height; + this.transformPivotX = rect!.x + rect!.width / 2; + this.transformPivotY = rect!.y + rect!.height / 2; if (!skipHistoryUpdate) { this.editor.history.initiateTransformChange(); } @@ -170,7 +170,7 @@ export class Transformer extends PIXI.Container { applyTransforms(): void { for (const elt of this.editor.currentSelection) { - const initial = this.initialTransforms.get(elt); + const initial = this.initialTransforms.get(elt)!; const delta = { x: ((initial.x + this.applyTranslateX) - this.transformPivotX) * this.applyScaleX, y: ((initial.y + this.applyTranslateY) - this.transformPivotY) * this.applyScaleY diff --git a/src/node_requires/roomEditor/entityClasses/ViewportRestriction.ts b/src/node_requires/roomEditor/entityClasses/ViewportRestriction.ts index 724d34782..dd79c78bd 100644 --- a/src/node_requires/roomEditor/entityClasses/ViewportRestriction.ts +++ b/src/node_requires/roomEditor/entityClasses/ViewportRestriction.ts @@ -6,8 +6,8 @@ export class ViewportRestriction extends ViewportFrame { editor: RoomEditor; constructor(editor: RoomEditor) { super(editor); - this.x = this.editor.ctRoom.restrictMinX; - this.y = this.editor.ctRoom.restrictMinY; + this.x = this.editor.ctRoom.restrictMinX || 0; + this.y = this.editor.ctRoom.restrictMinY || 0; this.icon .lineStyle(2, getPixiSwatch('orange'), 1, 0.5); this.icon.drawRect(0, 10, 20, 14); @@ -28,17 +28,17 @@ export class ViewportRestriction extends ViewportFrame { return; } this.visible = true; - this.x = this.editor.ctRoom.restrictMinX; - this.y = this.editor.ctRoom.restrictMinY; - let width = this.editor.ctRoom.restrictMaxX - this.editor.ctRoom.restrictMinX, - height = this.editor.ctRoom.restrictMaxY - this.editor.ctRoom.restrictMinY; + this.x = this.editor.ctRoom.restrictMinX || 0; + this.y = this.editor.ctRoom.restrictMinY || 0; + let width = (this.editor.ctRoom.restrictMaxX || 0) - this.x, + height = (this.editor.ctRoom.restrictMaxY || 0) - this.y; if (width < 0) { width = Math.abs(width); - this.x = this.editor.ctRoom.restrictMaxX; + this.x = this.editor.ctRoom.restrictMaxX || 0; } if (height < 0) { height = Math.abs(height); - this.y = this.editor.ctRoom.restrictMaxY; + this.y = this.editor.ctRoom.restrictMaxY || 0; } this.icon.visible = (height / this.editor.camera.scale.x > 48); super.redrawFrame(width, height); diff --git a/src/node_requires/roomEditor/history.ts b/src/node_requires/roomEditor/history.ts index aa9d87a72..42d789618 100644 --- a/src/node_requires/roomEditor/history.ts +++ b/src/node_requires/roomEditor/history.ts @@ -33,11 +33,11 @@ type transformation = { type deletion = { type: 'deletion', - deleted: Set<[Copy | Tile, TileLayer?]> + deleted: Set<[Copy] | [Tile, TileLayer]> }; type creation = { type: 'creation', - created: Set<[Copy | Tile, TileLayer?]> + created: Set<[Copy] | [Tile, TileLayer]> }; type tileLayerCreation = { @@ -151,7 +151,11 @@ export class History { case 'deletion': for (const deletion of change.deleted) { const [entity, parent] = deletion; - entity.restore(parent); + if (entity instanceof Tile) { + entity.restore(parent as TileLayer); + } else { + entity.restore(); + } } break; case 'creation': @@ -214,7 +218,7 @@ export class History { if (this.currentChange === this.stack[this.stack.length - 1]) { return false; } - const newChange = this.stack[this.stack.indexOf(this.currentChange) + 1]; + const newChange = this.stack[this.stack.indexOf(this.currentChange!) + 1]; // eslint-disable-next-line default-case switch (newChange.type) { case 'transformation': @@ -246,7 +250,11 @@ export class History { case 'creation': for (const creation of newChange.created) { const [entity, parent] = creation; - entity.restore(parent); + if (entity instanceof Tile) { + entity.restore(parent as TileLayer); + } else { + entity.restore(); + } } break; case 'tileLayerCreation': @@ -292,9 +300,13 @@ export class History { return true; } pushChange(change: change): void { - const id = this.stack.indexOf(this.currentChange); - this.stack = this.stack.slice(0, id + 1); - this.stack.push(change); + if (this.currentChange) { + const id = this.stack.indexOf(this.currentChange); + this.stack = this.stack.slice(0, id + 1); + this.stack.push(change); + } else { + this.stack.push(change); + } this.currentChange = change; if (this.stack.length > 30) { this.stack.shift(); @@ -318,7 +330,7 @@ export class History { this.pushChange(transform); } snapshotTransforms(): void { - if (this.currentChange.type !== 'transformation') { + if (!this.currentChange || this.currentChange.type !== 'transformation') { throw new Error('Cannot snapshot transforms as the current change\'s type is not "transformation"'); } for (const [entity, value] of this.currentChange.transformations) { @@ -331,11 +343,13 @@ export class History { const change = {} as ui['before']; if (target && target.text) { change.customTextSettings = { - ...target.customTextSettings, - anchor: { - ...target.customTextSettings.anchor - } + ...target.customTextSettings }; + if (change.customTextSettings.anchor) { + change.customTextSettings.anchor = { + ...change.customTextSettings.anchor + }; + } } return change; } @@ -355,17 +369,19 @@ export class History { if (!this.editor.currentUiSelection) { throw new Error('Cannot snapshot a ui change as the current selection is not set'); } - if (this.currentChange.type !== 'ui') { + if (!this.currentChange || this.currentChange.type !== 'ui') { throw new Error('Cannot snapshot transforms as the current change\'s type is not "ui"'); } this.currentChange.after = { customTextSettings: { - ...this.editor.currentUiSelection.customTextSettings, - anchor: { - ...this.editor.currentUiSelection.customTextSettings.anchor - } + ...this.editor.currentUiSelection.customTextSettings } }; + if (this.currentChange.after.customTextSettings?.anchor) { + this.currentChange.after.customTextSettings.anchor = { + ...this.currentChange.after.customTextSettings.anchor + }; + } } updateUiFor(change: propChange): void { const {target, key} = change, diff --git a/src/node_requires/roomEditor/index.ts b/src/node_requires/roomEditor/index.ts index 12c352df9..7723683e9 100644 --- a/src/node_requires/roomEditor/index.ts +++ b/src/node_requires/roomEditor/index.ts @@ -120,7 +120,7 @@ class RoomEditor extends PIXI.Application { */ interacting = false; interactions: IRoomEditorInteraction[]; - currentInteraction: IRoomEditorInteraction; + currentInteraction: IRoomEditorInteraction | undefined; affixedInteractionData: unknown; copies = new Set(); @@ -206,12 +206,12 @@ class RoomEditor extends PIXI.Application { this.drawSelection([this.currentUiSelection]); } if (['addCopies', 'addTiles'].includes(this.riotEditor.currentTool)) { - if (this.riotEditor.controlMode && ['default', 'inherit'].includes(this.view.style.cursor)) { - this.view.style.cursor = eraseCursor; + if (this.riotEditor.controlMode && ['default', 'inherit'].includes(this.view.style!.cursor as string)) { + this.view.style!.cursor = eraseCursor; this.snapTarget.visible = false; } - if (!this.riotEditor.controlMode && this.view.style.cursor.includes('Erase')) { - this.view.style.cursor = 'default'; + if (!this.riotEditor.controlMode && (this.view.style!.cursor as string).includes('Erase')) { + this.view.style!.cursor = 'default'; this.snapTarget.visible = true; } } @@ -273,7 +273,7 @@ class RoomEditor extends PIXI.Application { for (const interaction of this.interactions) { if (this.interacting && this.currentInteraction === interaction) { if (listener in this.currentInteraction.listeners) { - this.currentInteraction.listeners[listener].apply( + this.currentInteraction.listeners[listener]!.apply( this, [event, this.riotEditor, this.affixedInteractionData, callback] ); @@ -474,11 +474,11 @@ class RoomEditor extends PIXI.Application { if (this.riotEditor.currentTool !== 'select') { return; } - const changes = new Set<[Copy | Tile, TileLayer?]>(); + const changes = new Set<[Copy] | [Tile, TileLayer]>(); for (const stuff of this.currentSelection) { if (stuff instanceof Tile) { const {parent} = stuff; - changes.add([stuff.detach(), parent]); + changes.add([stuff.detach(), parent as TileLayer]); } else if (stuff instanceof Copy) { changes.add([stuff.detach()]); } @@ -488,7 +488,9 @@ class RoomEditor extends PIXI.Application { deleted: changes }); this.transformer.clear(); - this.riotEditor.refs.propertiesPanel.updatePropList(); + if (this.riotEditor.refs.propertiesPanel) { + this.riotEditor.refs.propertiesPanel.updatePropList(); + } } copySelection(): void { if (this.riotEditor.currentTool !== 'select' || !this.currentSelection.size) { @@ -505,14 +507,14 @@ class RoomEditor extends PIXI.Application { this.clipboard.add([ 'tile', stuff.serialize(), - stuff.parent + stuff.parent as TileLayer ]); } } this.transformer.blink(); } pasteSelection(): void { - const createdSet = new Set<[Copy | Tile, TileLayer?]>(); + const createdSet = new Set<[Copy] | [Tile, TileLayer]>(); if (this.riotEditor.currentTool === 'select' && this.currentSelection.size && this.history.currentChange?.type === 'transformation' @@ -588,7 +590,9 @@ class RoomEditor extends PIXI.Application { this.transformer.applyTranslateY += dy; this.transformer.applyTransforms(); this.transformer.setup(); - this.riotEditor.refs.propertiesPanel.updatePropList(); + if (this.riotEditor.refs.propertiesPanel) { + this.riotEditor.refs.propertiesPanel.updatePropList(); + } this.transformer.blink(); } sort(method: 'x' | 'y' | 'toFront' | 'toBack'): void { diff --git a/src/node_requires/roomEditor/interactions/selectUi.ts b/src/node_requires/roomEditor/interactions/selectUi.ts index 2841b237c..b3b333bf7 100644 --- a/src/node_requires/roomEditor/interactions/selectUi.ts +++ b/src/node_requires/roomEditor/interactions/selectUi.ts @@ -21,7 +21,9 @@ const selectUi: IRoomEditorInteraction = { this.clearSelectionOverlay(); this.currentUiSelection = void 0; } - this.riotEditor.refs.uiTools.update(); + if (this.riotEditor.refs.uiTools) { + this.riotEditor.refs.uiTools.update(); + } callback(); } } diff --git a/src/node_requires/roomEditor/interactions/tab.ts b/src/node_requires/roomEditor/interactions/tab.ts index be6a1b515..211f676b0 100644 --- a/src/node_requires/roomEditor/interactions/tab.ts +++ b/src/node_requires/roomEditor/interactions/tab.ts @@ -8,8 +8,10 @@ export const tab: IRoomEditorInteraction = { listeners: { tab(e: KeyboardEvent, roomTag, affixedData, callback) { if (this.copiesVisible) { - // Apply any possible property changes to the previous selectio set - this.riotEditor.refs.propertiesPanel.applyChanges(); + // Apply any possible property changes to the previous selectio set + if (this.riotEditor.refs.propertiesPanel) { + this.riotEditor.refs.propertiesPanel.applyChanges(); + } const copies = Array.from(this.copies.values()) .sort((a, b) => (a.y - b.y) || (a.x - b.x)); @@ -27,7 +29,9 @@ export const tab: IRoomEditorInteraction = { this.currentSelection.add(copies[0]); } this.transformer.setup(); - this.riotEditor.refs.propertiesPanel.updatePropList(); + if (this.riotEditor.refs.propertiesPanel) { + this.riotEditor.refs.propertiesPanel.updatePropList(); + } } callback(); } diff --git a/src/node_requires/roomEditor/interactions/tiles/deleteTiles.ts b/src/node_requires/roomEditor/interactions/tiles/deleteTiles.ts index 5659d91e5..d5f2fc4ea 100644 --- a/src/node_requires/roomEditor/interactions/tiles/deleteTiles.ts +++ b/src/node_requires/roomEditor/interactions/tiles/deleteTiles.ts @@ -21,14 +21,14 @@ export const deleteTiles: IRoomEditorInteraction = { affixedData.deleted = new Set(); if (e.target instanceof Tile && e.target.parent === riotTag.currentTileLayer) { const {parent} = e.target; - affixedData.deleted.add([e.target.detach(), parent]); + affixedData.deleted.add([e.target.detach(), parent as TileLayer]); } }, pointermove(e: PIXI.FederatedPointerEvent, riotTag, affixedData) { this.cursor.update(e); if (e.target instanceof Tile && e.target.parent === riotTag.currentTileLayer) { const {parent} = e.target; - affixedData.deleted.add([e.target.detach(), parent]); + affixedData.deleted.add([e.target.detach(), parent as TileLayer]); } }, pointerup(e: PIXI.FederatedPointerEvent, roomTag, affixedData, callback) { diff --git a/src/node_requires/roomEditor/interactions/tiles/placeTile.ts b/src/node_requires/roomEditor/interactions/tiles/placeTile.ts index 24bbb940c..8d9cb7de6 100644 --- a/src/node_requires/roomEditor/interactions/tiles/placeTile.ts +++ b/src/node_requires/roomEditor/interactions/tiles/placeTile.ts @@ -119,7 +119,7 @@ export const placeTile: IRoomEditorInteraction = { ); riotTag.currentTileLayer.addChild(...newTiles); for (const tile of newTiles) { - affixedData.created.add([tile, tile.parent]); + affixedData.created.add([tile, tile.parent as TileLayer]); } soundbox.play('Wood_Start'); }, @@ -139,7 +139,7 @@ export const placeTile: IRoomEditorInteraction = { ); riotTag.currentTileLayer.addChild(...newTiles); for (const tile of newTiles) { - affixedData.created.add([tile, tile.parent]); + affixedData.created.add([tile, tile.parent as TileLayer]); } }) ); @@ -180,7 +180,7 @@ export const placeTile: IRoomEditorInteraction = { ); riotTag.currentTileLayer.addChild(...newTiles); for (const tile of newTiles) { - affixedData.created.add([tile, tile.parent]); + affixedData.created.add([tile, tile.parent as TileLayer]); } } } diff --git a/src/node_requires/roomEditor/interactions/transformer/move.ts b/src/node_requires/roomEditor/interactions/transformer/move.ts index 14e5d9089..3c90f9597 100644 --- a/src/node_requires/roomEditor/interactions/transformer/move.ts +++ b/src/node_requires/roomEditor/interactions/transformer/move.ts @@ -50,11 +50,15 @@ export const moveSelection: IRoomEditorInteraction = { this.transformer.transformPivotX = affixed.startingPivotX + delta.x; this.transformer.transformPivotY = affixed.startingPivotY + delta.y; this.transformer.applyTransforms(); - this.riotEditor.refs.propertiesPanel.updatePropList(); + if (this.riotEditor.refs.propertiesPanel) { + this.riotEditor.refs.propertiesPanel.updatePropList(); + } }, pointerup(e: PIXI.FederatedPointerEvent, roomTag, affixedData, callback) { this.dropPrecision(); - this.riotEditor.refs.propertiesPanel.updatePropList(); + if (this.riotEditor.refs.propertiesPanel) { + this.riotEditor.refs.propertiesPanel.updatePropList(); + } this.history.snapshotTransforms(); callback(); } diff --git a/src/node_requires/roomEditor/interactions/transformer/nudge.ts b/src/node_requires/roomEditor/interactions/transformer/nudge.ts index d01fa181f..9da4bdf40 100644 --- a/src/node_requires/roomEditor/interactions/transformer/nudge.ts +++ b/src/node_requires/roomEditor/interactions/transformer/nudge.ts @@ -12,7 +12,9 @@ export const nudgeDown: IRoomEditorInteraction = { this.transformer.transformPivotY += delta; this.transformer.applyTransforms(); this.dropPrecision(); - this.riotEditor.refs.propertiesPanel.updatePropList(); + if (this.riotEditor.refs.propertiesPanel) { + this.riotEditor.refs.propertiesPanel.updatePropList(); + } this.history.snapshotTransforms(); callback(); } @@ -31,7 +33,9 @@ export const nudgeUp: IRoomEditorInteraction = { this.transformer.transformPivotY -= delta; this.transformer.applyTransforms(); this.dropPrecision(); - this.riotEditor.refs.propertiesPanel.updatePropList(); + if (this.riotEditor.refs.propertiesPanel) { + this.riotEditor.refs.propertiesPanel.updatePropList(); + } this.history.snapshotTransforms(); callback(); } @@ -50,7 +54,9 @@ export const nudgeLeft: IRoomEditorInteraction = { this.transformer.transformPivotX -= delta; this.transformer.applyTransforms(); this.dropPrecision(); - this.riotEditor.refs.propertiesPanel.updatePropList(); + if (this.riotEditor.refs.propertiesPanel) { + this.riotEditor.refs.propertiesPanel.updatePropList(); + } this.history.snapshotTransforms(); callback(); } @@ -69,7 +75,9 @@ export const nudgeRight: IRoomEditorInteraction = { this.transformer.transformPivotX += delta; this.transformer.applyTransforms(); this.dropPrecision(); - this.riotEditor.refs.propertiesPanel.updatePropList(); + if (this.riotEditor.refs.propertiesPanel) { + this.riotEditor.refs.propertiesPanel.updatePropList(); + } this.history.snapshotTransforms(); callback(); } diff --git a/src/node_requires/roomEditor/interactions/transformer/rotate.ts b/src/node_requires/roomEditor/interactions/transformer/rotate.ts index 1cf01b770..33b170b98 100644 --- a/src/node_requires/roomEditor/interactions/transformer/rotate.ts +++ b/src/node_requires/roomEditor/interactions/transformer/rotate.ts @@ -39,11 +39,15 @@ export const rotateSelection: IRoomEditorInteraction = { } this.transformer.applyRotation = rad; this.transformer.applyTransforms(); - this.riotEditor.refs.propertiesPanel.updatePropList(); + if (this.riotEditor.refs.propertiesPanel) { + this.riotEditor.refs.propertiesPanel.updatePropList(); + } }, pointerup(e: PIXI.FederatedPointerEvent, roomTag, affixedData, callback) { this.dropPrecision(); - this.riotEditor.refs.propertiesPanel.updatePropList(); + if (this.riotEditor.refs.propertiesPanel) { + this.riotEditor.refs.propertiesPanel.updatePropList(); + } this.history.snapshotTransforms(); callback(); } diff --git a/src/node_requires/roomEditor/interactions/transformer/scale.ts b/src/node_requires/roomEditor/interactions/transformer/scale.ts index d03aed7f0..498266bcd 100644 --- a/src/node_requires/roomEditor/interactions/transformer/scale.ts +++ b/src/node_requires/roomEditor/interactions/transformer/scale.ts @@ -48,7 +48,7 @@ export const scaleSelection: IRoomEditorInteraction = { affixedData.axes = 'y'; } }, - // eslint-disable-next-line max-lines-per-function + // eslint-disable-next-line max-lines-per-function, complexity globalpointermove(e: PIXI.FederatedPointerEvent, riotTag, affixedData) { this.cursor.update(e); const {transformer} = this; @@ -148,11 +148,15 @@ export const scaleSelection: IRoomEditorInteraction = { } transformer.applyTransforms(); - this.riotEditor.refs.propertiesPanel.updatePropList(); + if (this.riotEditor.refs.propertiesPanel) { + this.riotEditor.refs.propertiesPanel.updatePropList(); + } }, pointerup(e, roomTag, affixedData, callback) { this.dropPrecision(); - this.riotEditor.refs.propertiesPanel.updatePropList(); + if (this.riotEditor.refs.propertiesPanel) { + this.riotEditor.refs.propertiesPanel.updatePropList(); + } this.history.snapshotTransforms(); callback(); } diff --git a/src/node_requires/roomEditor/interactions/transformer/select.ts b/src/node_requires/roomEditor/interactions/transformer/select.ts index 901e1e5f5..dd0eb453c 100644 --- a/src/node_requires/roomEditor/interactions/transformer/select.ts +++ b/src/node_requires/roomEditor/interactions/transformer/select.ts @@ -3,6 +3,7 @@ import * as PIXI from 'node_modules/pixi.js'; import {IRoomEditorInteraction} from '../..'; import {Copy} from '../../entityClasses/Copy'; import {Tile} from '../../entityClasses/Tile'; +import {TileLayer} from '../../entityClasses/TileLayer'; interface IAffixedData { startRoomPos: PIXI.IPoint; @@ -94,9 +95,12 @@ const select: IRoomEditorInteraction = { roomPos.y - affixedData.startRoomPos.y ); }, + // eslint-disable-next-line complexity pointerup(e: PIXI.FederatedPointerEvent, riotTag, affixedData, callback) { // Apply any possible property changes to the previous selectio set - this.riotEditor.refs.propertiesPanel.applyChanges(); + if (this.riotEditor.refs.propertiesPanel) { + this.riotEditor.refs.propertiesPanel.applyChanges(); + } const selectMap: [boolean, Iterable][] = [ [this.selectCopies, this.copies], @@ -112,13 +116,13 @@ const select: IRoomEditorInteraction = { currentSelection; // Pick a suitable entity under the cursor (try both from pointerdown and pointerup) if ((s instanceof Copy && this.selectCopies) || - (s instanceof Tile && this.selectTiles && !s.parent.isHidden) + (s instanceof Tile && this.selectTiles && !(s.parent as TileLayer).isHidden) ) { currentSelection = s; } else { s = e.target as PIXI.DisplayObject; if ((s instanceof Copy && this.selectCopies) || - (s instanceof Tile && this.selectTiles && !s.parent.isHidden) + (s instanceof Tile && this.selectTiles && !(s.parent as TileLayer).isHidden) ) { currentSelection = s; } @@ -143,7 +147,7 @@ const select: IRoomEditorInteraction = { for (const selectType of selectMap) { if (selectType[0]) { for (const object of selectType[1]) { - if (object instanceof Tile && object.parent.isHidden) { + if (object instanceof Tile && (object.parent as TileLayer).isHidden) { continue; } const {x, y} = getCenter(object, this.room); @@ -157,7 +161,9 @@ const select: IRoomEditorInteraction = { } this.transformer.setup(); this.marqueeBox.visible = false; - this.riotEditor.refs.propertiesPanel.updatePropList(); + if (this.riotEditor.refs.propertiesPanel) { + this.riotEditor.refs.propertiesPanel.updatePropList(); + } callback(); } } diff --git a/src/node_requires/utils/imageUtils.ts b/src/node_requires/utils/imageUtils.ts index 5d594d31f..6d49767b5 100644 --- a/src/node_requires/utils/imageUtils.ts +++ b/src/node_requires/utils/imageUtils.ts @@ -12,7 +12,7 @@ const imageCover = function ( const canvas = document.createElement('canvas'); canvas.width = w; canvas.height = h; - const cx = canvas.getContext('2d'); + const cx = canvas.getContext('2d') as CanvasRenderingContext2D; cx.clearRect(0, 0, w, h); const k = Math.max(w / image.width, h / image.height); if (!forceSmooth && window.currentProject.settings.rendering.pixelatedrender) { @@ -41,7 +41,7 @@ const imageContain = function ( const canvas = document.createElement('canvas'); canvas.width = w; canvas.height = h; - const cx = canvas.getContext('2d'); + const cx = canvas.getContext('2d') as CanvasRenderingContext2D; cx.clearRect(0, 0, w, h); let k; if (w / image.width < h / image.height) { @@ -78,7 +78,7 @@ const imagePlaceInRect = function ( const canvas = document.createElement('canvas'); canvas.width = wb; canvas.height = hb; - const cx = canvas.getContext('2d'); + const cx = canvas.getContext('2d') as CanvasRenderingContext2D; cx.clearRect(0, 0, wb, hb); let k; if (wi / image.width < hi / image.height) { @@ -104,7 +104,7 @@ const imageRound = function (image: GenericImage): HTMLCanvasElement { const canvas = document.createElement('canvas'); const w = canvas.width = image.width; const h = canvas.height = image.height; - const cx = canvas.getContext('2d'); + const cx = canvas.getContext('2d') as CanvasRenderingContext2D; cx.clearRect(0, 0, w, h); cx.ellipse(w / 2, h / 2, w / 2, h / 2, 0, 0, Math.PI * 2); cx.fill(); @@ -119,7 +119,7 @@ const toCanvas = function (image: GenericImage): HTMLCanvasElement { const canvas = document.createElement('canvas'); canvas.width = image.width; canvas.height = image.height; - const cx = canvas.getContext('2d'); + const cx = canvas.getContext('2d') as CanvasRenderingContext2D; cx.clearRect(0, 0, canvas.width, canvas.height); cx.drawImage(image, 0, 0); return canvas; @@ -137,7 +137,7 @@ const crop = function ( const canvas = document.createElement('canvas'); canvas.width = w; canvas.height = h; - const cx = canvas.getContext('2d'); + const cx = canvas.getContext('2d') as CanvasRenderingContext2D; cx.clearRect(0, 0, w, h); cx.drawImage( image, diff --git a/tsconfig.json b/tsconfig.json index 0ed1da945..c4e81251e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,6 +7,7 @@ "esModuleInterop": true, "skipLibCheck": true, "alwaysStrict": true, + "strictNullChecks": true, "baseUrl": "./", "typeRoots": [ "./node_modules/@types/",