diff --git a/build-tests/eslint-9-test/src/__snapshots__/sarif.test.ts.snap b/build-tests/eslint-9-test/src/__snapshots__/sarif.test.ts.snap index 0ddfa4d6a6f..e765ef493d8 100644 --- a/build-tests/eslint-9-test/src/__snapshots__/sarif.test.ts.snap +++ b/build-tests/eslint-9-test/src/__snapshots__/sarif.test.ts.snap @@ -28,7 +28,7 @@ Object { "uri": "src/index.ts", }, "region": Object { - "endColumn": 24, + "endColumn": 16, "endLine": 6, "startColumn": 3, "startLine": 6, @@ -37,7 +37,7 @@ Object { }, ], "message": Object { - "text": "Expected _bar to have a type annotation.", + "text": "Expected a type annotation.", }, "ruleId": "@typescript-eslint/typedef", "ruleIndex": 0, diff --git a/build-tests/eslint-9-test/src/index.ts b/build-tests/eslint-9-test/src/index.ts index 549373093be..92ad52c9244 100644 --- a/build-tests/eslint-9-test/src/index.ts +++ b/build-tests/eslint-9-test/src/index.ts @@ -3,8 +3,8 @@ export class Foo { // eslint-disable-next-line @typescript-eslint/typedef - private _bar = 'bar'; - public baz: string = this._bar; + #bar = 'bar'; + public baz: string = this.#bar; } export const Bad_Name: string = '37'; diff --git a/common/changes/@rushstack/eslint-config/native-private-fields_2026-08-18-12-00-00.json b/common/changes/@rushstack/eslint-config/native-private-fields_2026-08-18-12-00-00.json new file mode 100644 index 00000000000..4aabe1f11ca --- /dev/null +++ b/common/changes/@rushstack/eslint-config/native-private-fields_2026-08-18-12-00-00.json @@ -0,0 +1,9 @@ +{ + "changes": [ + { + "packageName": "@rushstack/eslint-config", + "comment": "Enable the rule that prefers ECMAScript private class members.", + "type": "minor" + } + ] +} diff --git a/common/changes/@rushstack/eslint-plugin/native-private-fields_2026-08-18-12-00-00.json b/common/changes/@rushstack/eslint-plugin/native-private-fields_2026-08-18-12-00-00.json new file mode 100644 index 00000000000..1bfe58309c1 --- /dev/null +++ b/common/changes/@rushstack/eslint-plugin/native-private-fields_2026-08-18-12-00-00.json @@ -0,0 +1,9 @@ +{ + "changes": [ + { + "packageName": "@rushstack/eslint-plugin", + "comment": "Add a rule that requires ECMAScript private syntax for class fields, methods, and accessors.", + "type": "minor" + } + ] +} diff --git a/common/changes/@rushstack/node-core-library/native-private-members_2026-09-10-11-51.json b/common/changes/@rushstack/node-core-library/native-private-members_2026-09-10-11-51.json new file mode 100644 index 00000000000..346255bb411 --- /dev/null +++ b/common/changes/@rushstack/node-core-library/native-private-members_2026-09-10-11-51.json @@ -0,0 +1,9 @@ +{ + "changes": [ + { + "packageName": "@rushstack/node-core-library", + "comment": "Target ES2022 and use native ECMAScript private class members.", + "type": "minor" + } + ] +} diff --git a/eslint/eslint-config/flat/profile/_common.js b/eslint/eslint-config/flat/profile/_common.js index 4513f97c146..5cf7eebe575 100644 --- a/eslint/eslint-config/flat/profile/_common.js +++ b/eslint/eslint-config/flat/profile/_common.js @@ -220,6 +220,9 @@ const commonConfig = [ // RATIONALE: See the @rushstack/eslint-plugin documentation '@rushstack/no-new-null': 'warn', + // RATIONALE: See the @rushstack/eslint-plugin documentation + '@rushstack/prefer-ecmascript-private-members': 'warn', + // RATIONALE: See the @rushstack/eslint-plugin documentation '@rushstack/typedef-var': 'warn', diff --git a/eslint/eslint-config/profile/_common.js b/eslint/eslint-config/profile/_common.js index 584628cd15f..6b49372fd0f 100644 --- a/eslint/eslint-config/profile/_common.js +++ b/eslint/eslint-config/profile/_common.js @@ -236,6 +236,9 @@ function buildRules(profile) { // RATIONALE: See the @rushstack/eslint-plugin documentation '@rushstack/no-new-null': 'warn', + // RATIONALE: See the @rushstack/eslint-plugin documentation + '@rushstack/prefer-ecmascript-private-members': 'warn', + // RATIONALE: See the @rushstack/eslint-plugin documentation '@rushstack/typedef-var': 'warn', diff --git a/eslint/eslint-plugin/README.md b/eslint/eslint-plugin/README.md index b76d3142ea2..04dc53c9127 100644 --- a/eslint/eslint-plugin/README.md +++ b/eslint/eslint-plugin/README.md @@ -425,6 +425,41 @@ enum E { let e: E._PrivateMember = E._PrivateMember; // okay, because _PrivateMember is declared by E ``` +## `@rushstack/prefer-ecmascript-private-members` + +Require ECMAScript private syntax for fields, methods, and accessors declared with TypeScript's `private` +modifier. + +#### Rule Details + +ECMAScript `#` members provide runtime privacy. TypeScript's `private` modifier is erased during compilation, +allowing the member to be accessed through JavaScript, bracket notation, or type assertions. + +This rule applies to class fields, methods, and accessors. Private constructors and constructor parameter +properties are not affected. The rule does not provide an autofix because converting a member requires updating +every reference and may change runtime behavior for reflection or objects created without invoking the +constructor. + +#### Examples + +The following pattern is considered a problem: + +```ts +class Example { + private value: string = ''; // error + private calculate(): number {} // error +} +``` + +The following pattern is NOT considered a problem: + +```ts +class Example { + #value: string = ''; + #calculate(): number {} +} +``` + ## `@rushstack/normalized-imports` Require relative import paths to be written in a normalized minimal form and autofix unnecessary directory traversals. diff --git a/eslint/eslint-plugin/src/index.ts b/eslint/eslint-plugin/src/index.ts index 61f0c64f23e..a3d2d7288a4 100644 --- a/eslint/eslint-plugin/src/index.ts +++ b/eslint/eslint-plugin/src/index.ts @@ -14,6 +14,7 @@ import { normalizedImportsRule } from './normalized-imports'; import { typedefVar } from './typedef-var'; import { importRequiresChunkNameRule } from './import-requires-chunk-name'; import { pairReactDomRenderUnmountRule } from './pair-react-dom-render-unmount'; +import { preferEcmascriptPrivateMembersRule } from './prefer-ecmascript-private-members'; interface IPlugin { rules: { [ruleName: string]: TSESLint.RuleModule }; @@ -52,7 +53,10 @@ const plugin: IPlugin = { 'import-requires-chunk-name': importRequiresChunkNameRule, // Full name: "@rushstack/pair-react-dom-render-unmount" - 'pair-react-dom-render-unmount': pairReactDomRenderUnmountRule + 'pair-react-dom-render-unmount': pairReactDomRenderUnmountRule, + + // Full name: "@rushstack/prefer-ecmascript-private-members" + 'prefer-ecmascript-private-members': preferEcmascriptPrivateMembersRule } }; diff --git a/eslint/eslint-plugin/src/prefer-ecmascript-private-members.ts b/eslint/eslint-plugin/src/prefer-ecmascript-private-members.ts new file mode 100644 index 00000000000..f5e6f52244a --- /dev/null +++ b/eslint/eslint-plugin/src/prefer-ecmascript-private-members.ts @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; + +type MessageIds = 'use-ecmascript-private-member'; +type Options = []; + +const preferEcmascriptPrivateMembersRule: TSESLint.RuleModule = { + defaultOptions: [], + meta: { + type: 'suggestion', + messages: { + 'use-ecmascript-private-member': + 'Use ECMAScript private syntax ("#member") instead of the TypeScript "private" modifier.' + }, + schema: [], + docs: { + description: 'Require ECMAScript private syntax for private class fields, methods, and accessors', + recommended: 'recommended', + url: 'https://www.npmjs.com/package/@rushstack/eslint-plugin' + } as TSESLint.RuleMetaDataDocs + }, + create: (context: TSESLint.RuleContext) => ({ + PropertyDefinition(node: TSESTree.PropertyDefinition): void { + if (node.accessibility === 'private') { + context.report({ + node, + messageId: 'use-ecmascript-private-member' + }); + } + }, + MethodDefinition(node: TSESTree.MethodDefinition): void { + if (node.accessibility === 'private' && node.kind !== 'constructor') { + context.report({ + node, + messageId: 'use-ecmascript-private-member' + }); + } + } + }) +}; + +export { preferEcmascriptPrivateMembersRule }; diff --git a/eslint/eslint-plugin/src/test/prefer-ecmascript-private-members.test.ts b/eslint/eslint-plugin/src/test/prefer-ecmascript-private-members.test.ts new file mode 100644 index 00000000000..e26ab3dae58 --- /dev/null +++ b/eslint/eslint-plugin/src/test/prefer-ecmascript-private-members.test.ts @@ -0,0 +1,80 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { RuleTester } from '@typescript-eslint/rule-tester'; + +import { preferEcmascriptPrivateMembersRule } from '../prefer-ecmascript-private-members'; +import { getRuleTesterWithoutProject } from './ruleTester'; + +const ruleTester: RuleTester = getRuleTesterWithoutProject(); + +ruleTester.run('prefer-ecmascript-private-members', preferEcmascriptPrivateMembersRule, { + invalid: [ + { + code: 'class Example { private value: string = ""; }', + errors: [{ messageId: 'use-ecmascript-private-member' }] + }, + { + code: 'class Example { private static readonly values: Set = new Set(); }', + errors: [{ messageId: 'use-ecmascript-private-member' }] + }, + { + code: 'class Example { private optional?: string; private assigned!: string; }', + errors: [ + { messageId: 'use-ecmascript-private-member' }, + { messageId: 'use-ecmascript-private-member' } + ] + }, + { + code: 'class Example { declare private value: string; }', + errors: [{ messageId: 'use-ecmascript-private-member' }] + }, + { + code: 'class Example { private ["value"]: string = ""; }', + errors: [{ messageId: 'use-ecmascript-private-member' }] + }, + { + code: 'class Example { private calculate(): number { return 1; } }', + errors: [{ messageId: 'use-ecmascript-private-member' }] + }, + { + code: [ + 'class Example {', + ' private get value(): string { return ""; }', + ' private set value(value: string) {}', + '}' + ].join('\n'), + errors: [ + { messageId: 'use-ecmascript-private-member' }, + { messageId: 'use-ecmascript-private-member' } + ] + } + ], + valid: [ + { + code: 'class Example { #value: string = ""; static #values: Set = new Set(); }' + }, + { + code: [ + 'class Example {', + ' #calculate(): number { return 1; }', + ' get #value(): string { return ""; }', + ' set #value(value: string) {}', + '}' + ].join('\n') + }, + { + code: 'class Example { public value: string = ""; protected otherValue: string = ""; }' + }, + { + code: [ + 'class Example {', + ' public constructor(private readonly parameter: string) {}', + '}' + ].join('\n') + }, + { + code: 'class Example { private constructor() {} }' + } + ] +}); diff --git a/libraries/node-core-library/src/Async.ts b/libraries/node-core-library/src/Async.ts index 29f8669badb..647cdaa144b 100644 --- a/libraries/node-core-library/src/Async.ts +++ b/libraries/node-core-library/src/Async.ts @@ -460,15 +460,15 @@ function getSignal(): [Promise, () => void, (err: Error) => void] { * @public */ export class AsyncQueue implements AsyncIterable<[T, () => void]> { - private _queue: T[]; - private _onPushSignal: Promise; - private _onPushResolve: () => void; + #queue: T[]; + #onPushSignal: Promise; + #onPushResolve: () => void; public constructor(iterable?: Iterable) { - this._queue = iterable ? Array.from(iterable) : []; + this.#queue = iterable ? Array.from(iterable) : []; const [promise, resolver] = getSignal(); - this._onPushSignal = promise; - this._onPushResolve = resolver; + this.#onPushSignal = promise; + this.#onPushResolve = resolver; } public async *[Symbol.asyncIterator](): AsyncIterableIterator<[T, () => void]> { @@ -485,16 +485,16 @@ export class AsyncQueue implements AsyncIterable<[T, () => void]> { }; let position: number = 0; - while (this._queue.length > position || activeIterations > 0) { - if (this._queue.length > position) { + while (this.#queue.length > position || activeIterations > 0) { + if (this.#queue.length > position) { activeIterations++; - yield [this._queue[position++], callback]; + yield [this.#queue[position++], callback]; } else { // On push, the item will be added to the queue and the onPushSignal will be resolved. // On calling the callback, active iterations will be decremented by the callback and the // callbackSignal will be resolved. This means that the loop will continue if there are // active iterations or if there are items in the queue that haven't been yielded yet. - await Promise.race([this._onPushSignal, callbackSignal]); + await Promise.race([this.#onPushSignal, callbackSignal]); } } } @@ -505,10 +505,10 @@ export class AsyncQueue implements AsyncIterable<[T, () => void]> { * @param item - The item to push into the queue. */ public push(item: T): void { - this._queue.push(item); - this._onPushResolve(); + this.#queue.push(item); + this.#onPushResolve(); const [onPushSignal, onPushResolve] = getSignal(); - this._onPushSignal = onPushSignal; - this._onPushResolve = onPushResolve; + this.#onPushSignal = onPushSignal; + this.#onPushResolve = onPushResolve; } } diff --git a/libraries/node-core-library/src/EnvironmentMap.ts b/libraries/node-core-library/src/EnvironmentMap.ts index c672f1be36b..f81be99512b 100644 --- a/libraries/node-core-library/src/EnvironmentMap.ts +++ b/libraries/node-core-library/src/EnvironmentMap.ts @@ -27,7 +27,7 @@ export interface IEnvironmentEntry { * @public */ export class EnvironmentMap { - private readonly _map: Map = new Map(); + readonly #map: Map = new Map(); /** * Whether the environment variable names are case-sensitive. @@ -57,7 +57,7 @@ export class EnvironmentMap { * Clears all entries, resulting in an empty map. */ public clear(): void { - this._map.clear(); + this.#map.clear(); } /** @@ -69,7 +69,7 @@ export class EnvironmentMap { */ public set(name: string, value: string): void { const key: string = this.caseSensitive ? name : name.toUpperCase(); - this._map.set(key, { name: name, value }); + this.#map.set(key, { name: name, value }); } /** @@ -77,7 +77,7 @@ export class EnvironmentMap { */ public unset(name: string): void { const key: string = this.caseSensitive ? name : name.toUpperCase(); - this._map.delete(key); + this.#map.delete(key); } /** @@ -85,7 +85,7 @@ export class EnvironmentMap { */ public get(name: string): string | undefined { const key: string = this.caseSensitive ? name : name.toUpperCase(); - const entry: IEnvironmentEntry | undefined = this._map.get(key); + const entry: IEnvironmentEntry | undefined = this.#map.get(key); if (entry === undefined) { return undefined; } @@ -96,14 +96,14 @@ export class EnvironmentMap { * Returns the map keys, which are environment variable names. */ public names(): IterableIterator { - return this._map.keys(); + return this.#map.keys(); } /** * Returns the map entries. */ public entries(): IterableIterator { - return this._map.values(); + return this.#map.values(); } /** diff --git a/libraries/node-core-library/src/FileError.ts b/libraries/node-core-library/src/FileError.ts index 4de451cd7ab..0ec1974ab72 100644 --- a/libraries/node-core-library/src/FileError.ts +++ b/libraries/node-core-library/src/FileError.ts @@ -140,7 +140,7 @@ export class FileError extends Error { public getFormattedErrorMessage(options?: IFileErrorFormattingOptions): string { return Path.formatFileLocation({ format: options?.format || 'Unix', - baseFolder: this._evaluateBaseFolder(), + baseFolder: this.#evaluateBaseFolder(), pathToFormat: this.absolutePath, message: this.message, line: this.line, @@ -166,7 +166,7 @@ export class FileError extends Error { } } - private _evaluateBaseFolder(): string | undefined { + #evaluateBaseFolder(): string | undefined { // Cache the sanitized environment variable. This means that we don't support changing // the environment variable mid-execution. This is a reasonable tradeoff for the benefit // of being able to cache absolute paths, since that is only able to be determined after diff --git a/libraries/node-core-library/src/FileWriter.ts b/libraries/node-core-library/src/FileWriter.ts index 70db42a79d4..12a3c35c5bf 100644 --- a/libraries/node-core-library/src/FileWriter.ts +++ b/libraries/node-core-library/src/FileWriter.ts @@ -57,10 +57,10 @@ export class FileWriter { */ public readonly filePath: string; - private _fileDescriptor: number | undefined; + #fileDescriptor: number | undefined; private constructor(fileDescriptor: number, filePath: string) { - this._fileDescriptor = fileDescriptor; + this.#fileDescriptor = fileDescriptor; this.filePath = filePath; } @@ -82,11 +82,11 @@ export class FileWriter { * @param text - The text to write to the file. */ public write(text: string): void { - if (!this._fileDescriptor) { + if (!this.#fileDescriptor) { throw new Error(`Cannot write to file, file descriptor has already been released.`); } - fs.writeSync(this._fileDescriptor, text); + fs.writeSync(this.#fileDescriptor, text); } /** @@ -97,9 +97,9 @@ export class FileWriter { * The `close()` method can be called more than once; additional calls are ignored. */ public close(): void { - const fd: number | undefined = this._fileDescriptor; + const fd: number | undefined = this.#fileDescriptor; if (fd) { - this._fileDescriptor = undefined; + this.#fileDescriptor = undefined; fs.closeSync(fd); } } @@ -109,10 +109,10 @@ export class FileWriter { * Behind the scenes it uses `fs.statSync()`. */ public getStatistics(): FileSystemStats { - if (!this._fileDescriptor) { + if (!this.#fileDescriptor) { throw new Error(`Cannot get file statistics, file descriptor has already been released.`); } - return fs.fstatSync(this._fileDescriptor); + return fs.fstatSync(this.#fileDescriptor); } } diff --git a/libraries/node-core-library/src/JsonSchema.ts b/libraries/node-core-library/src/JsonSchema.ts index 8864740ef41..d9adb68fa7d 100644 --- a/libraries/node-core-library/src/JsonSchema.ts +++ b/libraries/node-core-library/src/JsonSchema.ts @@ -201,15 +201,15 @@ function _inferJsonSchemaVersion({ $schema }: JsonObject): JsonSchemaVersion | u * @public */ export class JsonSchema { - private _dependentSchemas: JsonSchema[] = []; - private _filename: string = ''; - private _validator: ValidateFunction | undefined = undefined; - private _schemaObject: JsonObject | undefined = undefined; - private _schemaVersion: JsonSchemaVersion | undefined = undefined; - private _customFormats: + #dependentSchemas: JsonSchema[] = []; + #filename: string = ''; + #validator: ValidateFunction | undefined = undefined; + #schemaObject: JsonObject | undefined = undefined; + #schemaVersion: JsonSchemaVersion | undefined = undefined; + #customFormats: | Record | IJsonSchemaCustomFormat> | undefined = undefined; - private _rejectVendorExtensionKeywords: boolean = false; + #rejectVendorExtensionKeywords: boolean = false; private constructor() {} @@ -227,13 +227,13 @@ export class JsonSchema { } const schema: JsonSchema = new JsonSchema(); - schema._filename = filename; + schema.#filename = filename; if (options) { - schema._dependentSchemas = options.dependentSchemas || []; - schema._schemaVersion = options.schemaVersion; - schema._customFormats = options.customFormats; - schema._rejectVendorExtensionKeywords = options.rejectVendorExtensionKeywords ?? false; + schema.#dependentSchemas = options.dependentSchemas || []; + schema.#schemaVersion = options.schemaVersion; + schema.#customFormats = options.customFormats; + schema.#rejectVendorExtensionKeywords = options.rejectVendorExtensionKeywords ?? false; } return schema; @@ -247,13 +247,13 @@ export class JsonSchema { options?: IJsonSchemaFromObjectOptions ): JsonSchema { const schema: JsonSchema = new JsonSchema(); - schema._schemaObject = schemaObject; + schema.#schemaObject = schemaObject; if (options) { - schema._dependentSchemas = options.dependentSchemas || []; - schema._schemaVersion = options.schemaVersion; - schema._customFormats = options.customFormats; - schema._rejectVendorExtensionKeywords = options.rejectVendorExtensionKeywords ?? false; + schema.#dependentSchemas = options.dependentSchemas || []; + schema.#schemaVersion = options.schemaVersion; + schema.#customFormats = options.customFormats; + schema.#rejectVendorExtensionKeywords = options.rejectVendorExtensionKeywords ?? false; } return schema; @@ -266,9 +266,9 @@ export class JsonSchema { * field is used if available. */ public get shortName(): string { - if (!this._filename) { - if (this._schemaObject) { - const schemaWithId: ISchemaWithId = this._schemaObject as ISchemaWithId; + if (!this.#filename) { + if (this.#schemaObject) { + const schemaWithId: ISchemaWithId = this.#schemaObject as ISchemaWithId; if (schemaWithId.id) { return schemaWithId.id; } else if (schemaWithId.$id) { @@ -277,7 +277,7 @@ export class JsonSchema { } return '(anonymous schema)'; } else { - return path.basename(this._filename); + return path.basename(this.#filename); } } @@ -287,11 +287,11 @@ export class JsonSchema { * Any dependencies will be compiled as well. */ public ensureCompiled(): void { - this._ensureLoaded(); + this.#ensureLoaded(); - if (!this._validator) { + if (!this.#validator) { const targetSchemaVersion: JsonSchemaVersion | undefined = - this._schemaVersion ?? _inferJsonSchemaVersion(this._schemaObject); + this.#schemaVersion ?? _inferJsonSchemaVersion(this.#schemaObject); const validatorOptions: AjvOptions = { strictSchema: true, allowUnionTypes: true @@ -315,8 +315,8 @@ export class JsonSchema { // Enable json-schema format validation // https://ajv.js.org/packages/ajv-formats.html addFormats(validator); - if (this._customFormats) { - for (const [name, format] of Object.entries(this._customFormats)) { + if (this.#customFormats) { + for (const [name, format] of Object.entries(this.#customFormats)) { validator.addFormat(name, { ...format, async: false }); } } @@ -325,16 +325,16 @@ export class JsonSchema { const seenObjects: Set = new Set(); const seenIds: Set = new Set(); - this._collectDependentSchemas(collectedSchemas, this._dependentSchemas, seenObjects, seenIds); + this.#collectDependentSchemas(collectedSchemas, this.#dependentSchemas, seenObjects, seenIds); // Unless explicitly rejected, scan the top-level keys of each schema for vendor // extension keys matching the x-- pattern and register them with // AJV so that strict mode does not reject them as unknown keywords. - if (!this._rejectVendorExtensionKeywords) { + if (!this.#rejectVendorExtensionKeywords) { const vendorKeywords: Set = new Set(); - _collectVendorExtensionKeywords(this._schemaObject, vendorKeywords); + _collectVendorExtensionKeywords(this.#schemaObject, vendorKeywords); for (const collectedSchema of collectedSchemas) { - _collectVendorExtensionKeywords(collectedSchema._schemaObject, vendorKeywords); + _collectVendorExtensionKeywords(collectedSchema.#schemaObject, vendorKeywords); } for (const keyword of vendorKeywords) { validator.addKeyword(keyword); @@ -344,7 +344,7 @@ export class JsonSchema { // Validate each schema in order. We specifically do not supply them all together, because we want // to make sure that circular references will fail to validate. for (const collectedSchema of collectedSchemas) { - validator.validateSchema(collectedSchema._schemaObject) as boolean; + validator.validateSchema(collectedSchema.#schemaObject) as boolean; if (validator.errors && validator.errors.length > 0) { throw new Error( `Failed to validate schema "${collectedSchema.shortName}":` + @@ -352,10 +352,10 @@ export class JsonSchema { _formatErrorDetails(validator.errors) ); } - validator.addSchema(collectedSchema._schemaObject); + validator.addSchema(collectedSchema.#schemaObject); } - this._validator = validator.compile(this._schemaObject); + this.#validator = validator.compile(this.#schemaObject); } } @@ -403,8 +403,8 @@ export class JsonSchema { jsonObject = remainder; } - if (this._validator && !this._validator(jsonObject)) { - const errorDetails: string = _formatErrorDetails(this._validator.errors!); + if (this.#validator && !this.#validator(jsonObject)) { + const errorDetails: string = _formatErrorDetails(this.#validator.errors!); const args: IJsonSchemaErrorInfo = { details: errorDetails @@ -413,14 +413,14 @@ export class JsonSchema { } } - private _ensureLoaded(): string { - if (!this._schemaObject) { - this._schemaObject = JsonFile.load(this._filename); + #ensureLoaded(): string { + if (!this.#schemaObject) { + this.#schemaObject = JsonFile.load(this.#filename); } - return (this._schemaObject as ISchemaWithId).id || (this._schemaObject as ISchemaWithId).$id || ''; + return (this.#schemaObject as ISchemaWithId).id || (this.#schemaObject as ISchemaWithId).$id || ''; } - private _collectDependentSchemas( + #collectDependentSchemas( collectedSchemas: JsonSchema[], dependentSchemas: JsonSchema[], seenObjects: Set, @@ -433,7 +433,7 @@ export class JsonSchema { } seenObjects.add(dependentSchema); - const schemaId: string = dependentSchema._ensureLoaded(); + const schemaId: string = dependentSchema.#ensureLoaded(); if (schemaId === '') { throw new Error( `This schema ${dependentSchema.shortName} cannot be referenced` + @@ -450,9 +450,9 @@ export class JsonSchema { collectedSchemas.push(dependentSchema); - this._collectDependentSchemas( + this.#collectDependentSchemas( collectedSchemas, - dependentSchema._dependentSchemas, + dependentSchema.#dependentSchemas, seenObjects, seenIds ); diff --git a/libraries/node-core-library/src/LockFile.ts b/libraries/node-core-library/src/LockFile.ts index 511a5e86490..2a096679462 100644 --- a/libraries/node-core-library/src/LockFile.ts +++ b/libraries/node-core-library/src/LockFile.ts @@ -169,14 +169,14 @@ interface ITryAcquireResult { * @public */ export class LockFile { - private _fileWriter: FileWriter | undefined; - private _filePath: string; - private _dirtyWhenAcquired: boolean; + #fileWriter: FileWriter | undefined; + #filePath: string; + #dirtyWhenAcquired: boolean; private constructor(fileWriter: FileWriter | undefined, filePath: string, dirtyWhenAcquired: boolean) { - this._fileWriter = fileWriter; - this._filePath = filePath; - this._dirtyWhenAcquired = dirtyWhenAcquired; + this.#fileWriter = fileWriter; + this.#filePath = filePath; + this.#dirtyWhenAcquired = dirtyWhenAcquired; IN_PROC_LOCKS.add(filePath); } @@ -293,17 +293,17 @@ export class LockFile { */ public release(deleteFile: boolean = true): void { if (this.isReleased) { - throw new Error(`The lock for file "${path.basename(this._filePath)}" has already been released.`); + throw new Error(`The lock for file "${path.basename(this.#filePath)}" has already been released.`); } - IN_PROC_LOCKS.delete(this._filePath); + IN_PROC_LOCKS.delete(this.#filePath); - this._fileWriter!.close(); + this.#fileWriter!.close(); if (deleteFile) { - FileSystem.deleteFile(this._filePath); + FileSystem.deleteFile(this.#filePath); } - this._fileWriter = undefined; + this.#fileWriter = undefined; } /** @@ -311,21 +311,21 @@ export class LockFile { * This can be used to detect if the previous process was terminated before releasing the resource. */ public get dirtyWhenAcquired(): boolean { - return this._dirtyWhenAcquired; + return this.#dirtyWhenAcquired; } /** * Returns the absolute path to the lockfile */ public get filePath(): string { - return this._filePath; + return this.#filePath; } /** * Returns true if this lock is currently being held. */ public get isReleased(): boolean { - return this._fileWriter === undefined; + return this.#fileWriter === undefined; } } diff --git a/libraries/node-core-library/src/MinimumHeap.ts b/libraries/node-core-library/src/MinimumHeap.ts index e7406619d65..1f2d22e9b90 100644 --- a/libraries/node-core-library/src/MinimumHeap.ts +++ b/libraries/node-core-library/src/MinimumHeap.ts @@ -8,8 +8,8 @@ * @public */ export class MinimumHeap { - private readonly _items: T[] = []; - private readonly _comparator: (a: T, b: T) => number; + readonly #items: T[] = []; + readonly #comparator: (a: T, b: T) => number; /** * Constructs a new MinimumHeap instance. @@ -19,7 +19,7 @@ export class MinimumHeap { * Otherwise, `a` will be considered greater than `b`. */ public constructor(comparator: (a: T, b: T) => number) { - this._comparator = comparator; + this.#comparator = comparator; } /** @@ -27,7 +27,7 @@ export class MinimumHeap { * @returns the number of items in the heap. */ public get size(): number { - return this._items.length; + return this.#items.length; } /** @@ -35,7 +35,7 @@ export class MinimumHeap { * @returns the root item, or `undefined` if the heap is empty */ public peek(): T | undefined { - return this._items[0]; + return this.#items[0]; } /** @@ -44,8 +44,8 @@ export class MinimumHeap { */ public poll(): T | undefined { if (this.size > 0) { - const result: T = this._items[0]; - const item: T = this._items.pop()!; + const result: T = this.#items[0]; + const item: T = this.#items.pop()!; const size: number = this.size; if (size === 0) { @@ -58,20 +58,20 @@ export class MinimumHeap { let smallerChildIndex: number = 1; while (smallerChildIndex < size) { - let smallerChild: T = this._items[smallerChildIndex]; + let smallerChild: T = this.#items[smallerChildIndex]; const rightChildIndex: number = smallerChildIndex + 1; if (rightChildIndex < size) { - const rightChild: T = this._items[rightChildIndex]; - if (this._comparator(rightChild, smallerChild) < 0) { + const rightChild: T = this.#items[rightChildIndex]; + if (this.#comparator(rightChild, smallerChild) < 0) { smallerChildIndex = rightChildIndex; smallerChild = rightChild; } } - if (this._comparator(smallerChild, item) < 0) { - this._items[index] = smallerChild; + if (this.#comparator(smallerChild, item) < 0) { + this.#items[index] = smallerChild; index = smallerChildIndex; smallerChildIndex = index * 2 + 1; } else { @@ -80,7 +80,7 @@ export class MinimumHeap { } // Place the item in its final location satisfying the heap property - this._items[index] = item; + this.#items[index] = item; return result; } @@ -95,14 +95,14 @@ export class MinimumHeap { while (index > 0) { // Due to zero-based indexing the parent is not exactly a bit shift const parentIndex: number = ((index + 1) >> 1) - 1; - const parent: T = this._items[parentIndex]; - if (this._comparator(item, parent) < 0) { - this._items[index] = parent; + const parent: T = this.#items[parentIndex]; + if (this.#comparator(item, parent) < 0) { + this.#items[index] = parent; index = parentIndex; } else { break; } } - this._items[index] = item; + this.#items[index] = item; } } diff --git a/libraries/node-core-library/src/PackageJsonLookup.ts b/libraries/node-core-library/src/PackageJsonLookup.ts index 59efc981569..ca2abda3d39 100644 --- a/libraries/node-core-library/src/PackageJsonLookup.ts +++ b/libraries/node-core-library/src/PackageJsonLookup.ts @@ -78,20 +78,20 @@ export class PackageJsonLookup { return _instance; } - private _loadExtraFields: boolean = false; + #loadExtraFields: boolean = false; // Cached the return values for tryGetPackageFolder(): // sourceFilePath --> packageJsonFolder - private _packageFolderCache!: Map; + #packageFolderCache!: Map; // Cached the return values for getPackageName(): // packageJsonPath --> packageName - private _packageJsonCache!: Map; + #packageJsonCache!: Map; public constructor(parameters?: IPackageJsonLookupParameters) { if (parameters) { if (parameters.loadExtraFields) { - this._loadExtraFields = parameters.loadExtraFields; + this.#loadExtraFields = parameters.loadExtraFields; } } this.clearCache(); @@ -149,8 +149,8 @@ export class PackageJsonLookup { * Call this method if changes have been made to the package.json files on disk. */ public clearCache(): void { - this._packageFolderCache = new Map(); - this._packageJsonCache = new Map(); + this.#packageFolderCache = new Map(); + this.#packageJsonCache = new Map(); } /** @@ -177,12 +177,12 @@ export class PackageJsonLookup { // // (Two lookups are required, because get() cannot distinguish the undefined value // versus a missing key.) - if (this._packageFolderCache.has(resolvedFileOrFolderPath)) { - return this._packageFolderCache.get(resolvedFileOrFolderPath); + if (this.#packageFolderCache.has(resolvedFileOrFolderPath)) { + return this.#packageFolderCache.get(resolvedFileOrFolderPath); } // Now call the recursive part of the algorithm - return this._tryGetPackageFolderFor(resolvedFileOrFolderPath); + return this.#tryGetPackageFolderFor(resolvedFileOrFolderPath); } /** @@ -264,19 +264,19 @@ export class PackageJsonLookup { * if the `version` field is missing from the package.json file. */ public loadNodePackageJson(jsonFilename: string): INodePackageJson { - return this._loadPackageJsonInner(jsonFilename); + return this.#loadPackageJsonInner(jsonFilename); } - private _loadPackageJsonInner(jsonFilename: string): IPackageJson; - private _loadPackageJsonInner( + #loadPackageJsonInner(jsonFilename: string): IPackageJson; + #loadPackageJsonInner( jsonFilename: string, errorsToIgnore: Set ): IPackageJson | undefined; - private _loadPackageJsonInner( + #loadPackageJsonInner( jsonFilename: string, errorsToIgnore?: Set ): IPackageJson | undefined { - const loadResult: ITryLoadPackageJsonInternalResult = this._tryLoadNodePackageJsonInner(jsonFilename); + const loadResult: ITryLoadPackageJsonInternalResult = this.#tryLoadNodePackageJsonInner(jsonFilename); if (loadResult.error && errorsToIgnore?.has(loadResult.error)) { return undefined; @@ -305,7 +305,7 @@ export class PackageJsonLookup { * Try to load a package.json file as an INodePackageJson, * returning undefined if the found file does not contain a `name` field. */ - private _tryLoadNodePackageJsonInner(jsonFilename: string): ITryLoadPackageJsonInternalResult { + #tryLoadNodePackageJsonInner(jsonFilename: string): ITryLoadPackageJsonInternalResult { // Since this will be a cache key, follow any symlinks and get an absolute path // to minimize duplication. (Note that duplication can still occur due to e.g. character case.) let normalizedFilePath: string; @@ -324,7 +324,7 @@ export class PackageJsonLookup { } } - let packageJson: IPackageJson | undefined = this._packageJsonCache.get(normalizedFilePath); + let packageJson: IPackageJson | undefined = this.#packageJsonCache.get(normalizedFilePath); if (!packageJson) { const loadedPackageJson: IPackageJson = JsonFile.load(normalizedFilePath); @@ -337,7 +337,7 @@ export class PackageJsonLookup { }; } - if (this._loadExtraFields) { + if (this.#loadExtraFields) { packageJson = loadedPackageJson; } else { packageJson = {} as IPackageJson; @@ -362,7 +362,7 @@ export class PackageJsonLookup { } Object.freeze(packageJson); - this._packageJsonCache.set(normalizedFilePath, packageJson); + this.#packageJsonCache.set(normalizedFilePath, packageJson); } return { @@ -371,21 +371,21 @@ export class PackageJsonLookup { } // Recursive part of the algorithm from tryGetPackageFolderFor() - private _tryGetPackageFolderFor(resolvedFileOrFolderPath: string): string | undefined { + #tryGetPackageFolderFor(resolvedFileOrFolderPath: string): string | undefined { // Two lookups are required, because get() cannot distinguish the undefined value // versus a missing key. - if (this._packageFolderCache.has(resolvedFileOrFolderPath)) { - return this._packageFolderCache.get(resolvedFileOrFolderPath); + if (this.#packageFolderCache.has(resolvedFileOrFolderPath)) { + return this.#packageFolderCache.get(resolvedFileOrFolderPath); } // Is resolvedFileOrFolderPath itself a folder with a valid package.json file? If so, return it. const packageJsonFilePath: string = `${resolvedFileOrFolderPath}/${FileConstants.PackageJson}`; - const packageJson: IPackageJson | undefined = this._loadPackageJsonInner( + const packageJson: IPackageJson | undefined = this.#loadPackageJsonInner( packageJsonFilePath, new Set(['FILE_NOT_FOUND', 'MISSING_NAME_FIELD']) ); if (packageJson) { - this._packageFolderCache.set(resolvedFileOrFolderPath, resolvedFileOrFolderPath); + this.#packageFolderCache.set(resolvedFileOrFolderPath, resolvedFileOrFolderPath); return resolvedFileOrFolderPath; } @@ -394,14 +394,14 @@ export class PackageJsonLookup { if (!parentFolder || parentFolder === resolvedFileOrFolderPath) { // We reached the root directory without finding a package.json file, // so cache the negative result - this._packageFolderCache.set(resolvedFileOrFolderPath, undefined); + this.#packageFolderCache.set(resolvedFileOrFolderPath, undefined); return undefined; // no match } // Recurse upwards, caching every step along the way - const parentResult: string | undefined = this._tryGetPackageFolderFor(parentFolder); + const parentResult: string | undefined = this.#tryGetPackageFolderFor(parentFolder); // Cache the parent's answer as well - this._packageFolderCache.set(resolvedFileOrFolderPath, parentResult); + this.#packageFolderCache.set(resolvedFileOrFolderPath, parentResult); return parentResult; } diff --git a/libraries/node-core-library/src/PackageName.ts b/libraries/node-core-library/src/PackageName.ts index bbcc0fef328..559917becf2 100644 --- a/libraries/node-core-library/src/PackageName.ts +++ b/libraries/node-core-library/src/PackageName.ts @@ -76,10 +76,10 @@ export interface IPackageNameParserOptions { * @public */ export class PackageNameParser { - private readonly _options: IPackageNameParserOptions; + readonly #options: IPackageNameParserOptions; public constructor(options: IPackageNameParserOptions = {}) { - this._options = { ...options }; + this.#options = { ...options }; } /** @@ -150,7 +150,7 @@ export class PackageNameParser { const nameWithoutScopeSymbols: string = (result.scope ? result.scope.slice(1, -1) : '') + result.unscopedName; - if (!this._options.allowUpperCase) { + if (!this.#options.allowUpperCase) { // "New packages must not have uppercase letters in the name." // This can't be enforced because "old" packages are still actively maintained. // Example: https://www.npmjs.com/package/Base64 diff --git a/libraries/node-core-library/src/ProtectableMap.ts b/libraries/node-core-library/src/ProtectableMap.ts index 73c07f8e16e..44828ffced5 100644 --- a/libraries/node-core-library/src/ProtectableMap.ts +++ b/libraries/node-core-library/src/ProtectableMap.ts @@ -48,17 +48,17 @@ export interface IProtectableMapParameters { * @public */ export class ProtectableMap { - private readonly _protectedView: ProtectableMapView; + readonly #protectedView: ProtectableMapView; public constructor(parameters: IProtectableMapParameters) { - this._protectedView = new ProtectableMapView(this, parameters); + this.#protectedView = new ProtectableMapView(this, parameters); } /** * The owner of the protectable map should return this object via its public API. */ public get protectedView(): Map { - return this._protectedView; + return this.#protectedView; } // --------------------------------------------------------------------------- @@ -69,7 +69,7 @@ export class ProtectableMap { * This operation does NOT invoke the ProtectableMap onClear() hook. */ public clear(): void { - this._protectedView._clearUnprotected(); + this.#protectedView._clearUnprotected(); } /** @@ -77,7 +77,7 @@ export class ProtectableMap { * This operation does NOT invoke the ProtectableMap onDelete() hook. */ public delete(key: K): boolean { - return this._protectedView._deleteUnprotected(key); + return this.#protectedView._deleteUnprotected(key); } /** @@ -85,7 +85,7 @@ export class ProtectableMap { * This operation does NOT invoke the ProtectableMap onSet() hook. */ public set(key: K, value: V): this { - this._protectedView._setUnprotected(key, value); + this.#protectedView._setUnprotected(key, value); return this; } @@ -97,7 +97,7 @@ export class ProtectableMap { */ // eslint-disable-next-line @typescript-eslint/no-explicit-any public forEach(callbackfn: (value: V, key: K, map: Map) => void, thisArg?: any): void { - this._protectedView.forEach(callbackfn); + this.#protectedView.forEach(callbackfn); } /** @@ -106,20 +106,20 @@ export class ProtectableMap { * otherwise returns the value associated with the key. */ public get(key: K): V | undefined { - return this._protectedView.get(key); + return this.#protectedView.get(key); } /** * Returns true if the specified key belongs to the map. */ public has(key: K): boolean { - return this._protectedView.has(key); + return this.#protectedView.has(key); } /** * Returns the number of (key, value) entries in the map. */ public get size(): number { - return this._protectedView.size; + return this.#protectedView.size; } } diff --git a/libraries/node-core-library/src/ProtectableMapView.ts b/libraries/node-core-library/src/ProtectableMapView.ts index 02a60e5bafb..c18aab53ae2 100644 --- a/libraries/node-core-library/src/ProtectableMapView.ts +++ b/libraries/node-core-library/src/ProtectableMapView.ts @@ -11,34 +11,34 @@ import type { ProtectableMap, IProtectableMapParameters } from './ProtectableMap * NOTE: This is not a public API. */ export class ProtectableMapView extends Map { - private readonly _owner: ProtectableMap; - private readonly _parameters: IProtectableMapParameters; + readonly #owner: ProtectableMap; + readonly #parameters: IProtectableMapParameters; public constructor(owner: ProtectableMap, parameters: IProtectableMapParameters) { super(); - this._owner = owner; - this._parameters = parameters; + this.#owner = owner; + this.#parameters = parameters; } public override clear(): void { - if (this._parameters.onClear) { - this._parameters.onClear(this._owner); + if (this.#parameters.onClear) { + this.#parameters.onClear(this.#owner); } super.clear(); } public override delete(key: K): boolean { - if (this._parameters.onDelete) { - this._parameters.onDelete(this._owner, key); + if (this.#parameters.onDelete) { + this.#parameters.onDelete(this.#owner, key); } return super.delete(key); } public override set(key: K, value: V): this { let modifiedValue: V = value; - if (this._parameters.onSet) { - modifiedValue = this._parameters.onSet(this._owner, key, modifiedValue); + if (this.#parameters.onSet) { + modifiedValue = this.#parameters.onSet(this.#owner, key, modifiedValue); } super.set(key, modifiedValue); return this; diff --git a/libraries/node-core-library/src/RealNodeModulePath.ts b/libraries/node-core-library/src/RealNodeModulePath.ts index dc4c74ac345..b64b8f912d1 100644 --- a/libraries/node-core-library/src/RealNodeModulePath.ts +++ b/libraries/node-core-library/src/RealNodeModulePath.ts @@ -42,11 +42,11 @@ export class RealNodeModulePathResolver { */ public readonly realNodeModulePath: (input: string) => string; - private readonly _cache: Map; - private readonly _errorCache: Map; - private readonly _fs: Required>; - private readonly _path: Required>; - private readonly _lstatOptions: Pick; + readonly #cache: Map; + readonly #errorCache: Map; + readonly #fs: Required>; + readonly #path: Required>; + readonly #lstatOptions: Pick; public constructor(options: IRealNodeModulePathResolverOptions = {}) { const { @@ -59,19 +59,19 @@ export class RealNodeModulePathResolver { } = nodePath, ignoreMissingPaths = false } = options; - const cache: Map = (this._cache = new Map()); - this._errorCache = new Map(); - this._fs = { + const cache: Map = (this.#cache = new Map()); + this.#errorCache = new Map(); + this.#fs = { lstatSync, readlinkSync }; - this._path = { + this.#path = { isAbsolute, join, resolve, sep }; - this._lstatOptions = { + this.#lstatOptions = { throwIfNoEntry: !ignoreMissingPaths }; @@ -115,7 +115,7 @@ export class RealNodeModulePathResolver { const linkCandidate: string = input.slice(0, linkEnd); // Check if the link is a symlink - const linkTarget: string | undefined = self._tryReadLink(linkCandidate); + const linkTarget: string | undefined = self.#tryReadLink(linkCandidate); if (linkTarget && isAbsolute(linkTarget)) { // Absolute path, combine the link target with any remaining path segments // Cache the resolution to avoid the readlink call in subsequent calls @@ -159,7 +159,7 @@ export class RealNodeModulePathResolver { * @public */ public clearCache(): void { - this._cache.clear(); + this.#cache.clear(); } /** @@ -168,13 +168,13 @@ export class RealNodeModulePathResolver { * @param link - The link to try to read * @returns The target of the symbolic link, or undefined if the input is not a symbolic link */ - private _tryReadLink(link: string): string | undefined { - const cached: string | false | undefined = this._cache.get(link); + #tryReadLink(link: string): string | undefined { + const cached: string | false | undefined = this.#cache.get(link); if (cached !== undefined) { return cached || undefined; } - const cachedError: Error | undefined = this._errorCache.get(link); + const cachedError: Error | undefined = this.#errorCache.get(link); if (cachedError) { // Fill the properties but fix the stack trace. throw Object.assign(new Error(cachedError.message), cachedError); @@ -183,17 +183,17 @@ export class RealNodeModulePathResolver { // On Windows, calling `readlink` on a directory throws an EUNKOWN, not EINVAL, so just pay the cost // of an lstat call. try { - const stat: nodeFs.Stats | undefined = this._fs.lstatSync(link, this._lstatOptions); + const stat: nodeFs.Stats | undefined = this.#fs.lstatSync(link, this.#lstatOptions); if (stat?.isSymbolicLink()) { // path.join(x, '.') will trim trailing slashes, if applicable - const result: string = this._path.join(this._fs.readlinkSync(link, 'utf8'), '.'); + const result: string = this.#path.join(this.#fs.readlinkSync(link, 'utf8'), '.'); return result; } // Ensure we cache that this was not a symbolic link. - this._cache.set(link, false); + this.#cache.set(link, false); } catch (err) { - this._errorCache.set(link, err as Error); + this.#errorCache.set(link, err as Error); } } } diff --git a/libraries/node-core-library/src/StringBuilder.ts b/libraries/node-core-library/src/StringBuilder.ts index 921d4bb8332..80ccbb8d77b 100644 --- a/libraries/node-core-library/src/StringBuilder.ts +++ b/libraries/node-core-library/src/StringBuilder.ts @@ -41,29 +41,29 @@ export interface IStringBuilder { * @public */ export class StringBuilder implements IStringBuilder { - private _chunks: string[]; + #chunks: string[]; public constructor() { - this._chunks = []; + this.#chunks = []; } /** {@inheritDoc IStringBuilder.append} */ public append(text: string): void { - this._chunks.push(text); + this.#chunks.push(text); } /** {@inheritDoc IStringBuilder.toString} */ public toString(): string { - if (this._chunks.length === 0) { + if (this.#chunks.length === 0) { return ''; } - if (this._chunks.length > 1) { - const joined: string = this._chunks.join(''); - this._chunks.length = 1; - this._chunks[0] = joined; + if (this.#chunks.length > 1) { + const joined: string = this.#chunks.join(''); + this.#chunks.length = 1; + this.#chunks[0] = joined; } - return this._chunks[0]; + return this.#chunks[0]; } } diff --git a/libraries/node-core-library/src/test/ProtectableMap.test.ts b/libraries/node-core-library/src/test/ProtectableMap.test.ts index ec25a0f62f9..875283c9a40 100644 --- a/libraries/node-core-library/src/test/ProtectableMap.test.ts +++ b/libraries/node-core-library/src/test/ProtectableMap.test.ts @@ -7,10 +7,10 @@ class ExampleApi { public clearedCount: number = 0; public deletedCount: number = 0; public setCount: number = 0; - private _studentAgesByName: ProtectableMap; + #studentAgesByName: ProtectableMap; public constructor() { - this._studentAgesByName = new ProtectableMap({ + this.#studentAgesByName = new ProtectableMap({ onClear: (source: ProtectableMap) => { ++this.clearedCount; }, @@ -32,14 +32,14 @@ class ExampleApi { } public get studentAgesByName(): Map { - return this._studentAgesByName.protectedView; + return this.#studentAgesByName.protectedView; } public doUnprotectedOperations(): void { - // These are unprotected because they interact with this._studentAgesByName - // instead of this._studentAgesByName.protectedView. - this._studentAgesByName.clear(); - this._studentAgesByName.set('Dave', -123); + // These are unprotected because they interact with this.#studentAgesByName + // instead of this.#studentAgesByName.protectedView. + this.#studentAgesByName.clear(); + this.#studentAgesByName.set('Dave', -123); } } diff --git a/libraries/node-core-library/tsconfig.json b/libraries/node-core-library/tsconfig.json index 1a33d17b873..dbb708bb67b 100644 --- a/libraries/node-core-library/tsconfig.json +++ b/libraries/node-core-library/tsconfig.json @@ -1,3 +1,6 @@ { - "extends": "./node_modules/decoupled-local-node-rig/profiles/default/tsconfig-base.json" + "extends": "./node_modules/decoupled-local-node-rig/profiles/default/tsconfig-base.json", + "compilerOptions": { + "target": "es2022" + } } diff --git a/rigs/decoupled-local-node-rig/profiles/default/includes/eslint/flat/profile/_common.js b/rigs/decoupled-local-node-rig/profiles/default/includes/eslint/flat/profile/_common.js index 4b78d53b57d..739eabbcf57 100644 --- a/rigs/decoupled-local-node-rig/profiles/default/includes/eslint/flat/profile/_common.js +++ b/rigs/decoupled-local-node-rig/profiles/default/includes/eslint/flat/profile/_common.js @@ -182,11 +182,13 @@ module.exports = { message: "Use explicit named exports instead of `export * from '...'`." }, { - selector: 'MethodDefinition[accessibility="private"][static=true]', + selector: + 'MethodDefinition[static=true]:matches([accessibility="private"], [key.type="PrivateIdentifier"])', message: 'Use a module-scoped function instead of a `private static` method.' }, { - selector: 'PropertyDefinition[accessibility="private"][static=true]', + selector: + 'PropertyDefinition[static=true]:matches([accessibility="private"], [key.type="PrivateIdentifier"])', message: 'Use a module-scoped variable instead of a `private static` property.' } ]