Skip to content

Commit 1529d5d

Browse files
authored
build: upgrade TypeScript to 7.0 (#56)
* style: auto formatting * build: version bump typescript v7. * build: add `types` to tsconfig to explicitly include type modules. From Node v6, you have to explicitly include type modules, otherwise they will not be added globally to the project like `process` for Node, and module imports will error like `Cannot find name 'node:fs'`. - Added `types` array to tsconfig and included the `node` and `vscode` modules to fix import and usage errors. Ref: http://typescriptlang.org/tsconfig/#types * build: change TypeScript compiler to use `es2025` spec and types. * fix: ts 7.0 type errors. Fixed TS 7.0 errors by ensuring: - Variables don't have `undefined` or `null` values before accessing them and providing fallback values if they are undefined with the null coalescing operator (??) and conditional ternary operator (? .. : .. ). - Values from methods are satisfying their expected return types using the `as` keyword and generic typed method calls. - Object keys aren't accessed if the object itself is `undefined` with the optional chaining operator (?.) and proper undefined type guards and conditionals. - `reconstructRegex` util function infers the correct type from the passed `obj` param. - Error stack logging has a fallback of the error message in case the stack is null/undefined. - `convertMapToReversedObject` util function has properly typed `result` instead of implicit `any` type and changed the reverse object mapping to mutate the existing array instead of rebuilding a new one on every iteration. * fix: ts null error for `packageJsonData`. - Changed type of `packageJsonData` property to allow it to be `null`. - Moved the null check from the `constructor` to the `setExtensionData` method, and use the check to narrow the type and assert that it's not null/falsy, and return early if it is. Using a local variable instead of accessing the property directly ensures TS doesn't keep spitting out null possibility errors. * fix: ts error `outputChannel` property has no initializer in a constructor. - Fixed the TS error "Property 'outputChannel' has no initializer and is not definitely assigned in the constructor." in Logger by adding a `constructor` and initialise the output channel inside it. * remove: the redundant `setupOutputChannel` method in Logger. - Removed the now redundant `setupOutputChannel` Logger method and it's references, this is because the output channel is now setup in the `constructor` so we have no need for this method now. - Removed the redundant `outputChannel` property null check in `showChannel` method since it's never null as it's initialised in `constructor`. * fix: make `edit` param required in `CommandRegistration` interface. - Reverted the `edit` param in the `handleSingleLineBlock` Configuration method back to be required instead of optional because it otherwise insinuates the method can work without the `edit` param which is not true. It must have the param set to work. Also removed the optional chaining operator on the `edit.insert` call. The TS error that the optional operators fixed will return: "Type '(textEditor: TextEditor, edit: TextEditorEdit) => void' is not assignable to type '(textEditor: TextEditor, edit?: TextEditorEdit | undefined) => void'. Types of parameters 'edit' and 'edit' are incompatible. Type 'TextEditorEdit | undefined' is not assignable to type 'TextEditorEdit'. Type 'undefined' is not assignable to type 'TextEditorEdit'." - Fixed the returning TS error above by making the `edit` param required instead of optional in the `handler` function in `CommandRegistration` interface, which the `handleSingleLineBlock` method has to satisfy. * fix: handling of missing error messages in `validateDevEnvVariables`. If an error code was caught but isn't listed in the messages map, then the `errorMsg` in `validateDevEnvVariables` utils function would return something like "ENOTDIR: undefined: ...". - Fixed by adding a fallback to the `UKNOWN` entry when an error code is not mapped.
1 parent cf047c7 commit 1529d5d

8 files changed

Lines changed: 96 additions & 86 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@
137137
"@types/vscode": "^1.130",
138138
"prettier": "^3.9.6",
139139
"prettier-plugin-multiline-arrays": "^4.1.11",
140-
"typescript": "^5.7"
140+
"typescript": "^7.0.0"
141141
},
142142
"dependencies": {
143143
"is-wsl": "^3.1.0",

src/configuration.ts

Lines changed: 31 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ export class Configuration {
9595
// Read the default multi-line config from the JSON file and cache it for later use.
9696
this.defaultMultiLineConfig = utils.readJsonFile(`${configPath}/default-multi-line-config.json`) as vscode.LanguageConfiguration;
9797
// Read the languages to skip from the JSON file and cache it for later use.
98-
this.languagesToSkip = utils.readJsonFile(`${configPath}/skip-languages.jsonc`);
98+
this.languagesToSkip = utils.readJsonFile<JsonObject>(`${configPath}/skip-languages.jsonc`) ?? {};
9999

100100
this.findAllLanguageConfigFilePaths();
101101
this.setLanguageConfigDefinitions();
@@ -255,7 +255,7 @@ export class Configuration {
255255
* ```
256256
*/
257257
public getConfigurationValue<K extends keyof Settings>(key: K): Settings[K] {
258-
return this.getConfiguration().get<Settings[K]>(key);
258+
return this.getConfiguration().get<Settings[K]>(key) as Settings[K];
259259
}
260260

261261
/**
@@ -343,8 +343,8 @@ export class Configuration {
343343
const builtInExtensionsPath = this.extensionData.getExtensionDiscoveryPath("builtInExtensionsPath");
344344

345345
// Read the paths and create arrays of the extensions.
346-
const userExtensions = this.readExtensionsFromDirectory(userExtensionsPath);
347-
const builtInExtensions = this.readExtensionsFromDirectory(builtInExtensionsPath);
346+
const userExtensions = userExtensionsPath ? this.readExtensionsFromDirectory(userExtensionsPath) : [];
347+
const builtInExtensions = builtInExtensionsPath ? this.readExtensionsFromDirectory(builtInExtensionsPath) : [];
348348

349349
// Add all installed extensions (including built-in ones) into the extensions array.
350350
// If running WSL, these will be the WSL-installed extensions.
@@ -372,7 +372,7 @@ export class Configuration {
372372
// If the langId already exists...
373373
if (this.languageConfigFilePaths.has(langId)) {
374374
// Push the new config path into the array of the existing langId.
375-
this.languageConfigFilePaths.get(langId).push(configPath);
375+
this.languageConfigFilePaths.get(langId)?.push(configPath);
376376
}
377377
// Otherwise, if the langId doesn't exist...
378378
else {
@@ -412,7 +412,7 @@ export class Configuration {
412412
// Define a new array as the new AutoClosingPair.
413413
const autoClosingPairsArray: vscode.AutoClosingPair[] = [];
414414
// Loop through the config's autoClosingPairs...
415-
config.autoClosingPairs.forEach((item) => {
415+
(config.autoClosingPairs ?? []).forEach((item) => {
416416
// If the item is an array...
417417
if (Array.isArray(item)) {
418418
// Create a new object with the 1st array element [0] as the
@@ -439,7 +439,7 @@ export class Configuration {
439439
const existingConfig = this.languageConfigs.get(langId);
440440

441441
// Only merge if both configs have comments
442-
if (existingConfig.comments && config.comments) {
442+
if (existingConfig?.comments && config.comments) {
443443
// Start with existing comments as base
444444
const mergedComments = {...existingConfig.comments};
445445

@@ -449,7 +449,9 @@ export class Configuration {
449449
if (Array.isArray(value) && value.length === 0) {
450450
return;
451451
}
452-
mergedComments[key] = value;
452+
if (key === "lineComment" || key === "blockComment") {
453+
mergedComments[key] = value;
454+
}
453455
});
454456

455457
// Update the config with merged comments
@@ -555,7 +557,7 @@ export class Configuration {
555557
this.languageConfigs.forEach((config: vscode.LanguageConfiguration, langId: LanguageId) => {
556558
// If the config object has own property of comments AND the comments key has
557559
// own property of blockComment...
558-
if (Object.hasOwn(config, "comments") && Object.hasOwn(config.comments, "blockComment")) {
560+
if (config.comments && Object.hasOwn(config.comments, "blockComment") && config.comments.blockComment) {
559561
// If the blockComment array includes the multi-line start of "/*"...
560562
if (config.comments.blockComment.includes("/*")) {
561563
// console.log(langId, config.comments);
@@ -627,7 +629,7 @@ export class Configuration {
627629

628630
// If the config object has own property of comments AND the comments key has
629631
// own property of lineComment...
630-
if (Object.hasOwn(config, "comments") && Object.hasOwn(config.comments, "lineComment")) {
632+
if (config.comments && Object.hasOwn(config.comments, "lineComment")) {
631633
let lineComment = config.comments.lineComment;
632634

633635
// Line comments can be a string or an object with a "comment" key.
@@ -752,21 +754,19 @@ export class Configuration {
752754

753755
// Deep-clone the internalLangConfig so modifications never write back
754756
// into the cached `languageConfigs` Map by accident.
755-
let langConfig: vscode.LanguageConfiguration = internalLangConfig
756-
? structuredClone(internalLangConfig)
757-
: {};
757+
let langConfig: vscode.LanguageConfiguration = internalLangConfig ? structuredClone(internalLangConfig) : {};
758758

759759
if (multiLine) {
760760
langConfig.autoClosingPairs = utils.mergeArraysBy<vscode.AutoClosingPair>(
761-
this.defaultMultiLineConfig.autoClosingPairs,
762-
internalLangConfig?.autoClosingPairs,
761+
this.defaultMultiLineConfig.autoClosingPairs ?? [],
762+
internalLangConfig?.autoClosingPairs ?? [],
763763
"open"
764764
);
765765

766766
// Add the multi-line onEnter rules to the langConfig.
767767
langConfig.onEnterRules = utils.mergeArraysBy<vscode.OnEnterRule>(
768768
Rules.multilineEnterRules,
769-
internalLangConfig?.onEnterRules,
769+
internalLangConfig?.onEnterRules ?? [],
770770
"beforeText"
771771
);
772772

@@ -780,7 +780,7 @@ export class Configuration {
780780
if (this.isLangIdMultiLineCommentOverridden(langId) && langConfig.comments?.blockComment) {
781781
langConfig.comments.blockComment = [
782782
this.getOverriddenMultiLineComment(langId),
783-
langConfig.comments.blockComment[1]
783+
langConfig.comments.blockComment[1],
784784
];
785785
}
786786

@@ -792,6 +792,7 @@ export class Configuration {
792792

793793
// If bladeComments has a value...
794794
if (bladeComments) {
795+
langConfig.comments ??= {};
795796
langConfig.comments.blockComment = bladeComments;
796797
}
797798
}
@@ -805,22 +806,26 @@ export class Configuration {
805806
if (isOnEnter && singleLineStyle) {
806807
// //-style comments
807808
if (singleLineStyle === "//") {
808-
langConfig.onEnterRules = utils.mergeArraysBy<vscode.OnEnterRule>(Rules.slashEnterRules, langConfig?.onEnterRules, "beforeText");
809+
langConfig.onEnterRules = utils.mergeArraysBy<vscode.OnEnterRule>(Rules.slashEnterRules, langConfig.onEnterRules ?? [], "beforeText");
809810
}
810811
// #-style comments
811812
else if (singleLineStyle === "#") {
812-
langConfig.onEnterRules = utils.mergeArraysBy<vscode.OnEnterRule>(Rules.hashEnterRules, langConfig?.onEnterRules, "beforeText");
813+
langConfig.onEnterRules = utils.mergeArraysBy<vscode.OnEnterRule>(Rules.hashEnterRules, langConfig.onEnterRules ?? [], "beforeText");
813814
}
814815
// ;-style comments
815816
else if (singleLineStyle === ";") {
816-
langConfig.onEnterRules = utils.mergeArraysBy<vscode.OnEnterRule>(Rules.semicolonEnterRules, langConfig?.onEnterRules, "beforeText");
817+
langConfig.onEnterRules = utils.mergeArraysBy<vscode.OnEnterRule>(
818+
Rules.semicolonEnterRules,
819+
langConfig.onEnterRules ?? [],
820+
"beforeText"
821+
);
817822
}
818823
}
819824
// If isOnEnter is false AND singleLineStyle isn't false, i.e. a string.
820825
else if (!isOnEnter && singleLineStyle) {
821826
// If langConfig does NOT have a comments key OR
822827
// the comments key exists but does NOT have the lineComment key...
823-
if (!Object.hasOwn(langConfig, "comments") || !Object.hasOwn(langConfig.comments, "lineComment")) {
828+
if (!langConfig.comments || !Object.hasOwn(langConfig.comments, "lineComment")) {
824829
// Add the singleLineStyle to the lineComments key and make sure any
825830
// blockComments aren't overwritten.
826831
langConfig.comments = {...langConfig.comments, lineComment: singleLineStyle};
@@ -838,7 +843,7 @@ export class Configuration {
838843

839844
// Check if isOnEnter OR multiline is true.
840845
if (isOnEnter || multiLine) {
841-
langConfig.onEnterRules.forEach((item) => {
846+
(langConfig.onEnterRules ?? []).forEach((item) => {
842847
// Check if the item has a "beforeText" property and reconstruct its regex pattern.
843848
if (Object.hasOwn(item, "beforeText")) {
844849
item.beforeText = utils.reconstructRegex(item, "beforeText");
@@ -941,12 +946,13 @@ export class Configuration {
941946

942947
// Get the langId from the auto-supported langs. If it doesn't exist, try getting it from
943948
// the custom-supported langs instead.
944-
let style: SingleLineCommentStyle | ExtraSingleLineCommentStyles = singleLineLangs.get(langId) ?? customSingleLineLangs.get(langId);
949+
let style: SingleLineCommentStyle | ExtraSingleLineCommentStyles | undefined =
950+
singleLineLangs.get(langId) ?? customSingleLineLangs.get(langId);
945951

946952
if (style && textEditor.selection.isEmpty) {
947953
let line = textEditor.document.lineAt(textEditor.selection.active);
948954
let isCommentLine = true;
949-
let indentRegex: RegExp;
955+
let indentRegex: RegExp | undefined;
950956

951957
if (style === "//" && line.text.search(/^\s*\/\/\s*/) !== -1) {
952958
indentRegex = /\//;
@@ -972,7 +978,7 @@ export class Configuration {
972978
isCommentLine = false;
973979
}
974980

975-
if (!isCommentLine) {
981+
if (!isCommentLine || !indentRegex) {
976982
return;
977983
}
978984

src/extension.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,6 @@ import {addDevEnvVariables} from "./utils";
99
import {LogLevel} from "./interfaces/utils";
1010

1111
export function activate(context: vscode.ExtensionContext) {
12-
// Setup logger first
13-
logger.setupOutputChannel();
14-
1512
const initialLogLevel = vscode.workspace.getConfiguration("auto-comment-blocks").get<LogLevel>("logLevel", "debug");
1613
logger.setLogLevel(initialLogLevel);
1714

src/extensionData.ts

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,9 @@ export class ExtensionData {
6161
/**
6262
* The package.json data for this extension.
6363
*
64-
* @type {IPackageJson}
64+
* @type {IPackageJson | null}
6565
*/
66-
private packageJsonData: IPackageJson;
66+
private packageJsonData: IPackageJson | null;
6767

6868
/**
6969
* Create an instance of the ExtensionData class, which retrieves and stores metadata
@@ -90,18 +90,15 @@ export class ExtensionData {
9090

9191
this.packageJsonData = this.getExtensionPackageJsonData();
9292

93-
// Only proceed with extension data setup if packageJsonData is NOT null.
94-
if (this.packageJsonData !== null) {
95-
this.setExtensionData();
96-
}
93+
this.setExtensionData();
9794

9895
this.setExtensionDiscoveryPaths();
9996
}
10097

10198
/**
10299
* Get the names, id, and version of this extension from package.json.
103100
*
104-
* @returns {IPackageJson | null} The package.json data for this extension, with extra custom keys.
101+
* @returns {IPackageJson | null} The package.json data for this extension.
105102
*/
106103
private getExtensionPackageJsonData(): IPackageJson | null {
107104
// Get the package.json file path.
@@ -113,26 +110,32 @@ export class ExtensionData {
113110
* Set the extension data into the extensionData Map.
114111
*/
115112
private setExtensionData() {
113+
// Only proceed if packageJsonData is NOT falsy, otherwise return early.
114+
const packageJsonData = this.packageJsonData;
115+
if (!packageJsonData) {
116+
return;
117+
}
118+
116119
// Create the extension ID (publisher.name).
117-
const id = `${this.packageJsonData.publisher}.${this.packageJsonData.name}`;
120+
const id = `${packageJsonData.publisher}.${packageJsonData.name}`;
118121

119122
// Set each key-value pair directly into the Map
120123
this.extensionData.set("id", id);
121-
this.extensionData.set("name", this.packageJsonData.name);
124+
this.extensionData.set("name", packageJsonData.name);
122125

123126
// Only set the namespace if it dealing with this extension.
124-
if (this.packageJsonData.name === "automatic-comment-blocks") {
127+
if (packageJsonData.name === "automatic-comment-blocks") {
125128
// The configuration settings namespace is a shortened version of the extension name.
126129
// We just need to replace "automatic" with "auto" in the name.
127-
const settingsNamespace: string = this.packageJsonData.name.replace("automatic", "auto");
130+
const settingsNamespace: string = packageJsonData.name.replace("automatic", "auto");
128131

129132
this.extensionData.set("namespace", settingsNamespace);
130133
}
131134

132-
this.extensionData.set("displayName", this.packageJsonData.displayName);
133-
this.extensionData.set("version", this.packageJsonData.version);
135+
this.extensionData.set("displayName", packageJsonData.displayName);
136+
this.extensionData.set("version", packageJsonData.version);
134137
this.extensionData.set("extensionPath", this.extensionPath);
135-
this.extensionData.set("packageJSON", this.packageJsonData);
138+
this.extensionData.set("packageJSON", packageJsonData);
136139
}
137140

138141
/**

src/interfaces/commands.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@ export interface CommandRegistration {
1818
* command is executed.
1919
*
2020
* @param textEditor The text editor
21-
* @param edit The text editor edits. Optional because some commands may not need it.
21+
* @param edit The text editor edits.
2222
* @returns void
2323
*/
24-
handler: (textEditor: vscode.TextEditor, edit?: vscode.TextEditorEdit) => void;
24+
handler: (textEditor: vscode.TextEditor, edit: vscode.TextEditorEdit) => void;
2525
}
2626

2727
/**

src/logger.ts

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {LogLevel, logLevels} from "./interfaces/utils";
55
/**
66
* Logger class for the Auto Comment Blocks extension.
77
* This class handles logging messages of differing log levels to the output channel.
8+
* Logger is a singleton class, and should only be instantiated once (in this file).
89
*
910
* @class Logger
1011
*/
@@ -41,15 +42,10 @@ class Logger {
4142
***********/
4243

4344
/**
44-
* Override the output channel
45-
*
46-
* @param {OutputChannel} channelOverride A vscode output channel.
45+
* Constructor for the Logger class, which
46+
* initialises the output channel for logging.
4747
*/
48-
public setupOutputChannel(channelOverride?: OutputChannel): void {
49-
if (channelOverride) {
50-
this.outputChannel = channelOverride;
51-
return;
52-
}
48+
constructor() {
5349
this.outputChannel = window.createOutputChannel("Auto Comment Blocks", "log");
5450
}
5551

@@ -90,9 +86,7 @@ class Logger {
9086
* Show the output channel to the user.
9187
*/
9288
public showChannel(): void {
93-
if (this.outputChannel) {
94-
this.outputChannel.show();
95-
}
89+
this.outputChannel.show();
9690
}
9791

9892
/**
@@ -190,10 +184,6 @@ class Logger {
190184
* @param {unknown} meta Extra data as needed.
191185
*/
192186
private logMessage(level: string, message: string, meta?: unknown): void {
193-
if (!this.outputChannel) {
194-
this.setupOutputChannel();
195-
}
196-
197187
message = this.redactUsername(message);
198188

199189
const time = new Date().toLocaleTimeString();
@@ -310,4 +300,5 @@ class Logger {
310300
}
311301
}
312302

303+
// Create and export the singleton instance of the Logger class.
313304
export const logger = new Logger();

0 commit comments

Comments
 (0)