diff --git a/.yarn/patches/typescript-npm-5.3.1-rc-6c4278ffd3.patch b/.yarn/patches/typescript-npm-5.3.1-rc-6c4278ffd3.patch deleted file mode 100644 index fb3879286f14..000000000000 --- a/.yarn/patches/typescript-npm-5.3.1-rc-6c4278ffd3.patch +++ /dev/null @@ -1,84 +0,0 @@ -diff --git a/lib/typescript.d.ts b/lib/typescript.d.ts -index 5ee1d5258cb019bddb259c7d31b1ae0156a98f0c..fc5f4d2a757a9fef92b2135148c22bb0cc81b2c1 100644 ---- a/lib/typescript.d.ts -+++ b/lib/typescript.d.ts -@@ -4491,8 +4491,8 @@ declare namespace ts { - JSDocFunctionType = 324, - JSDocVariadicType = 325, - JSDocNamepathType = 326, -+ /** @deprecated This was only added in 4.7 */ - JSDoc = 327, -- /** @deprecated Use SyntaxKind.JSDoc */ - JSDocComment = 327, - JSDocText = 328, - JSDocTypeLiteral = 329, -@@ -5066,6 +5066,8 @@ declare namespace ts { - readonly name: PropertyName; - readonly questionToken?: QuestionToken; - readonly type?: TypeNode; -+ /** @deprecated removed in 5.0 but we want to keep it for backwards compatibility checks! */ -+ readonly initializer?: Expression | undefined; - } - interface PropertyDeclaration extends ClassElement, JSDocContainer { - readonly kind: SyntaxKind.PropertyDeclaration; -@@ -5091,6 +5093,10 @@ declare namespace ts { - readonly parent: ObjectLiteralExpression; - readonly name: PropertyName; - readonly initializer: Expression; -+ /** @deprecated removed in 5.0 but we want to keep it for backwards compatibility checks! */ -+ readonly questionToken?: QuestionToken | undefined; -+ /** @deprecated removed in 5.0 but we want to keep it for backwards compatibility checks! */ -+ readonly exclamationToken?: ExclamationToken | undefined; - } - interface ShorthandPropertyAssignment extends ObjectLiteralElement, JSDocContainer { - readonly kind: SyntaxKind.ShorthandPropertyAssignment; -@@ -5098,6 +5104,12 @@ declare namespace ts { - readonly name: Identifier; - readonly equalsToken?: EqualsToken; - readonly objectAssignmentInitializer?: Expression; -+ /** @deprecated removed in 5.0 but we want to keep it for backwards compatibility checks! */ -+ readonly modifiers?: NodeArray | undefined; -+ /** @deprecated removed in 5.0 but we want to keep it for backwards compatibility checks! */ -+ readonly questionToken?: QuestionToken | undefined; -+ /** @deprecated removed in 5.0 but we want to keep it for backwards compatibility checks! */ -+ readonly exclamationToken?: ExclamationToken | undefined; - } - interface SpreadAssignment extends ObjectLiteralElement, JSDocContainer { - readonly kind: SyntaxKind.SpreadAssignment; -@@ -5222,6 +5234,8 @@ declare namespace ts { - } - interface FunctionTypeNode extends FunctionOrConstructorTypeNodeBase, LocalsContainer { - readonly kind: SyntaxKind.FunctionType; -+ /** @deprecated removed in 5.0 but we want to keep it for backwards compatibility checks! */ -+ readonly modifiers?: NodeArray | undefined; - } - interface ConstructorTypeNode extends FunctionOrConstructorTypeNodeBase, LocalsContainer { - readonly kind: SyntaxKind.ConstructorType; -@@ -8991,7 +9005,13 @@ declare namespace ts { - function symbolName(symbol: Symbol): string; - function getNameOfJSDocTypedef(declaration: JSDocTypedefTag): Identifier | PrivateIdentifier | undefined; - function getNameOfDeclaration(declaration: Declaration | Expression | undefined): DeclarationName | undefined; -+ /** -+ * @deprecated don't use this directly as it does not exist pre-4.8; instead use getDecorators from `@typescript-eslint/type-utils`. -+ */ - function getDecorators(node: HasDecorators): readonly Decorator[] | undefined; -+ /** -+ * @deprecated don't use this directly as it does not exist pre-4.8; instead use getModifiers from `@typescript-eslint/type-utils`. -+ */ - function getModifiers(node: HasModifiers): readonly Modifier[] | undefined; - /** - * Gets the JSDoc parameter tags for the node if present. -@@ -9521,7 +9541,13 @@ declare namespace ts { - function isModuleName(node: Node): node is ModuleName; - function isBinaryOperatorToken(node: Node): node is BinaryOperatorToken; - function setTextRange(range: T, location: TextRange | undefined): T; -+ /** -+ * @deprecated don't use this directly as it does not exist pre-4.8; instead use getModifiers from `@typescript-eslint/type-utils`. -+ */ - function canHaveModifiers(node: Node): node is HasModifiers; -+ /** -+ * @deprecated don't use this directly as it does not exist pre-4.8; instead use getDecorators from `@typescript-eslint/type-utils`. -+ */ - function canHaveDecorators(node: Node): node is HasDecorators; - /** - * Invokes a callback for each child of the given node. The 'cbNode' callback is invoked for all child nodes diff --git a/.yarn/patches/typescript-npm-5.4.0-dev.20231117-78a04a34c5.patch b/.yarn/patches/typescript-npm-5.4.0-dev.20231117-78a04a34c5.patch deleted file mode 100644 index 311b09c310c3..000000000000 --- a/.yarn/patches/typescript-npm-5.4.0-dev.20231117-78a04a34c5.patch +++ /dev/null @@ -1,84 +0,0 @@ -diff --git a/lib/typescript.d.ts b/lib/typescript.d.ts -index 7135bb786332478763097e4fa2274ac0222ecbb2..446baab7b0d29f1e88842f97f58d343b816c8df0 100644 ---- a/lib/typescript.d.ts -+++ b/lib/typescript.d.ts -@@ -4493,8 +4493,8 @@ declare namespace ts { - JSDocFunctionType = 324, - JSDocVariadicType = 325, - JSDocNamepathType = 326, -+ /** @deprecated This was only added in 4.7 */ - JSDoc = 327, -- /** @deprecated Use SyntaxKind.JSDoc */ - JSDocComment = 327, - JSDocText = 328, - JSDocTypeLiteral = 329, -@@ -5068,6 +5068,8 @@ declare namespace ts { - readonly name: PropertyName; - readonly questionToken?: QuestionToken; - readonly type?: TypeNode; -+ /** @deprecated removed in 5.0 but we want to keep it for backwards compatibility checks! */ -+ readonly initializer?: Expression | undefined; - } - interface PropertyDeclaration extends ClassElement, JSDocContainer { - readonly kind: SyntaxKind.PropertyDeclaration; -@@ -5093,6 +5095,10 @@ declare namespace ts { - readonly parent: ObjectLiteralExpression; - readonly name: PropertyName; - readonly initializer: Expression; -+ /** @deprecated removed in 5.0 but we want to keep it for backwards compatibility checks! */ -+ readonly questionToken?: QuestionToken | undefined; -+ /** @deprecated removed in 5.0 but we want to keep it for backwards compatibility checks! */ -+ readonly exclamationToken?: ExclamationToken | undefined; - } - interface ShorthandPropertyAssignment extends ObjectLiteralElement, JSDocContainer { - readonly kind: SyntaxKind.ShorthandPropertyAssignment; -@@ -5100,6 +5106,12 @@ declare namespace ts { - readonly name: Identifier; - readonly equalsToken?: EqualsToken; - readonly objectAssignmentInitializer?: Expression; -+ /** @deprecated removed in 5.0 but we want to keep it for backwards compatibility checks! */ -+ readonly modifiers?: NodeArray | undefined; -+ /** @deprecated removed in 5.0 but we want to keep it for backwards compatibility checks! */ -+ readonly questionToken?: QuestionToken | undefined; -+ /** @deprecated removed in 5.0 but we want to keep it for backwards compatibility checks! */ -+ readonly exclamationToken?: ExclamationToken | undefined; - } - interface SpreadAssignment extends ObjectLiteralElement, JSDocContainer { - readonly kind: SyntaxKind.SpreadAssignment; -@@ -5224,6 +5236,8 @@ declare namespace ts { - } - interface FunctionTypeNode extends FunctionOrConstructorTypeNodeBase, LocalsContainer { - readonly kind: SyntaxKind.FunctionType; -+ /** @deprecated removed in 5.0 but we want to keep it for backwards compatibility checks! */ -+ readonly modifiers?: NodeArray | undefined; - } - interface ConstructorTypeNode extends FunctionOrConstructorTypeNodeBase, LocalsContainer { - readonly kind: SyntaxKind.ConstructorType; -@@ -8995,7 +9009,13 @@ declare namespace ts { - function symbolName(symbol: Symbol): string; - function getNameOfJSDocTypedef(declaration: JSDocTypedefTag): Identifier | PrivateIdentifier | undefined; - function getNameOfDeclaration(declaration: Declaration | Expression | undefined): DeclarationName | undefined; -+ /** -+ * @deprecated don't use this directly as it does not exist pre-4.8; instead use getDecorators from `@typescript-eslint/type-utils`. -+ */ - function getDecorators(node: HasDecorators): readonly Decorator[] | undefined; -+ /** -+ * @deprecated don't use this directly as it does not exist pre-4.8; instead use getModifiers from `@typescript-eslint/type-utils`. -+ */ - function getModifiers(node: HasModifiers): readonly Modifier[] | undefined; - /** - * Gets the JSDoc parameter tags for the node if present. -@@ -9525,7 +9545,13 @@ declare namespace ts { - function isModuleName(node: Node): node is ModuleName; - function isBinaryOperatorToken(node: Node): node is BinaryOperatorToken; - function setTextRange(range: T, location: TextRange | undefined): T; -+ /** -+ * @deprecated don't use this directly as it does not exist pre-4.8; instead use getModifiers from `@typescript-eslint/type-utils`. -+ */ - function canHaveModifiers(node: Node): node is HasModifiers; -+ /** -+ * @deprecated don't use this directly as it does not exist pre-4.8; instead use getDecorators from `@typescript-eslint/type-utils`. -+ */ - function canHaveDecorators(node: Node): node is HasDecorators; - /** - * Invokes a callback for each child of the given node. The 'cbNode' callback is invoked for all child nodes diff --git a/docs/maintenance/Dependency_Version_Upgrades.mdx b/docs/maintenance/Dependency_Version_Upgrades.mdx index a0308b145b0a..ec0137f53b02 100644 --- a/docs/maintenance/Dependency_Version_Upgrades.mdx +++ b/docs/maintenance/Dependency_Version_Upgrades.mdx @@ -51,7 +51,7 @@ See [chore: drop support for ESLint v6](https://github.com/typescript-eslint/typ The typescript-eslint repository contains three kinds of version ranges for Node: - [`.github/workflows/ci.yml`](https://github.com/typescript-eslint/typescript-eslint/blob/main/.github/workflows/ci.yml)'s `PRIMARY_NODE_VERSION`: Set to the highest Node version we support -- `node-version`: Set to a tuple of our [lowest, highest] supported versions for our unit tests in CI +- `node-version`: Set to a tuple of our `[lowest, highest]` supported versions for our unit tests in CI - `engines` field in all `package.json`s: explicitly lists all supported Node ranges Change those numbers accordingly when adding support for a new Node version or removing support for an old Node version. @@ -97,8 +97,6 @@ We generally start the process of supporting a new TypeScript version just after - In the root `package.json`, change the `devDependency` on `typescript` to `~X.Y.3` - Rename and update `patches/typescript*` to the new TypeScript version - Any other changes made necessary due to changes in TypeScript between the RC version and stable version - - Update the supported version range in [Users > Dependency Versions](../users/Dependency_Versions.mdx) -1. Update [Users > Dependency Versions > TypeScript](../users/Dependency_Versions.mdx#typescript) 1. Send a PR that updates this documentation page to point to your newer issues and PRs - Also update any of these steps if you go with a different process diff --git a/docs/users/Dependency_Versions.mdx b/docs/users/Dependency_Versions.mdx index ff1cad710d73..8f0094ce1561 100644 --- a/docs/users/Dependency_Versions.mdx +++ b/docs/users/Dependency_Versions.mdx @@ -7,7 +7,7 @@ import packageJson from '../../package.json'; ## ESLint -> The version range of ESLint currently supported is `^6.0.0 || ^7.0.0 || ^8.0.0`. +> The version range of ESLint currently supported is `^7.0.0 || ^8.0.0`. We generally support at least the latest two major versions of ESLint. diff --git a/package.json b/package.json index c18fa662a27e..8202bc8f9b4d 100644 --- a/package.json +++ b/package.json @@ -114,7 +114,7 @@ "ts-node": "10.7.0", "tslint": "^6.1.3", "tsx": "^3.12.7", - "typescript": ">=4.3.5 <5.4.0 || 5.3.0-beta || 5.3.1-rc || 5.4.0-dev.20231117" + "typescript": ">=4.3.5 <5.4.0" }, "resolutions": { "@jest/create-cache-key-function": "^29", @@ -141,7 +141,7 @@ "pretty-format": "^29", "react-split-pane@^0.1.92": "patch:react-split-pane@npm%3A0.1.92#./.yarn/patches/react-split-pane-npm-0.1.92-93dbf51dff.patch", "tsx": "^3.12.7", - "typescript": "patch:typescript@npm%3A5.4.0-dev.20231117#./.yarn/patches/typescript-npm-5.4.0-dev.20231117-78a04a34c5.patch" + "typescript": "5.3.2" }, "packageManager": "yarn@3.7.0" } diff --git a/packages/repo-tools/src/generate-lib.ts b/packages/repo-tools/src/generate-lib.ts index dc397d4e2289..c9a0a722a02c 100644 --- a/packages/repo-tools/src/generate-lib.ts +++ b/packages/repo-tools/src/generate-lib.ts @@ -211,7 +211,7 @@ async function main(): Promise { // import and spread all of the references const imports = [ - "import { ImplicitLibVariableOptions } from '../variable';", + "import type { ImplicitLibVariableOptions } from '../variable';", ]; for (const reference of references) { const name = sanitize(reference); @@ -245,9 +245,9 @@ async function main(): Promise { if (requiredBaseImports.size > 0) { imports.push( - `import {${Array.from(requiredBaseImports).join( - ',', - )}} from './${BASE_CONFIG_MODULE_NAME}';`, + `import {${Array.from(requiredBaseImports) + .sort() + .join(',')}} from './${BASE_CONFIG_MODULE_NAME}';`, ); } diff --git a/packages/typescript-estree/src/node-utils.ts b/packages/typescript-estree/src/node-utils.ts index 0a382248c369..219f850aa03d 100644 --- a/packages/typescript-estree/src/node-utils.ts +++ b/packages/typescript-estree/src/node-utils.ts @@ -191,6 +191,7 @@ export function isComment(node: ts.Node): boolean { * @returns is JSDoc comment */ function isJSDocComment(node: ts.Node): node is ts.JSDoc { + // eslint-disable-next-line deprecation/deprecation -- SyntaxKind.JSDoc was only added in TS4.7 so we can't use it yet return node.kind === SyntaxKind.JSDocComment; } diff --git a/packages/typescript-estree/src/parseSettings/warnAboutTSVersion.ts b/packages/typescript-estree/src/parseSettings/warnAboutTSVersion.ts index b7d54b42e367..f860ae6f1004 100644 --- a/packages/typescript-estree/src/parseSettings/warnAboutTSVersion.ts +++ b/packages/typescript-estree/src/parseSettings/warnAboutTSVersion.ts @@ -6,13 +6,13 @@ import type { ParseSettings } from './index'; * This needs to be kept in sync with /docs/users/Versioning.mdx * in the typescript-eslint monorepo */ -const SUPPORTED_TYPESCRIPT_VERSIONS = '>=4.3.5 <5.3.0'; +const SUPPORTED_TYPESCRIPT_VERSIONS = '>=4.3.5 <5.4.0'; /* * The semver package will ignore prerelease ranges, and we don't want to explicitly document every one * List them all separately here, so we can automatically create the full string */ -const SUPPORTED_PRERELEASE_RANGES: string[] = ['5.3.1-rc']; +const SUPPORTED_PRERELEASE_RANGES: string[] = []; const ACTIVE_TYPESCRIPT_VERSION = ts.version; const isRunningSupportedTypeScriptVersion = semver.satisfies( ACTIVE_TYPESCRIPT_VERSION, diff --git a/packages/typescript-estree/src/ts-estree/ts-nodes.ts b/packages/typescript-estree/src/ts-estree/ts-nodes.ts index 51a990b3e812..8e5fc4c82131 100644 --- a/packages/typescript-estree/src/ts-estree/ts-nodes.ts +++ b/packages/typescript-estree/src/ts-estree/ts-nodes.ts @@ -4,9 +4,9 @@ import type * as ts from 'typescript'; // Eg: https://github.com/typescript-eslint/typescript-eslint/issues/2388, https://github.com/typescript-eslint/typescript-eslint/issues/2784 /* eslint-disable @typescript-eslint/ban-ts-comment, @typescript-eslint/prefer-ts-expect-error, @typescript-eslint/no-empty-interface */ declare module 'typescript' { - // @ts-ignore - added in TS 4.5 + /** @ts-ignore - added in TS 4.5, deprecated and converted to a type-alias in TS 5.3 */ export interface AssertClause extends ts.Node {} - // @ts-ignore - added in TS 4.5 + /** @ts-ignore - added in TS 4.5, deprecated and converted to a type-alias in TS 5.3 */ export interface AssertEntry extends ts.Node {} // added in TS 4.9 export interface SatisfiesExpression extends ts.Node {} @@ -25,6 +25,10 @@ export type TSNode = | ts.Identifier | ts.ImportAttribute | ts.ImportAttributes + /* eslint-disable-next-line deprecation/deprecation, @typescript-eslint/no-duplicate-type-constituents -- intentional for old TS versions */ + | ts.AssertClause + /* eslint-disable-next-line deprecation/deprecation, @typescript-eslint/no-duplicate-type-constituents -- intentional for old TS versions */ + | ts.AssertEntry | ts.PrivateIdentifier | ts.QualifiedName | ts.ComputedPropertyName diff --git a/packages/typescript-estree/tsconfig.build.json b/packages/typescript-estree/tsconfig.build.json index 84504e296c84..191f1df748a9 100644 --- a/packages/typescript-estree/tsconfig.build.json +++ b/packages/typescript-estree/tsconfig.build.json @@ -4,7 +4,8 @@ "composite": true, "outDir": "./dist", "rootDir": "./src", - "resolveJsonModule": true + "resolveJsonModule": true, + "removeComments": false }, "include": ["src", "typings"], "references": [ diff --git a/packages/typescript-estree/typings/typescript.d.ts b/packages/typescript-estree/typings/typescript.d.ts index a247b2808841..de19fdb5a447 100644 --- a/packages/typescript-estree/typings/typescript.d.ts +++ b/packages/typescript-estree/typings/typescript.d.ts @@ -10,4 +10,48 @@ declare module 'typescript' { interface JSDocContainer { jsDoc?: JSDoc[]; } + + // add back the deprecated properties that were removed in newer TS versions + // make sure these properties are marked as @ deprecated so they're flagged + // by the `deprecation/deprecation` lint rule + + interface PropertySignature { + /** @deprecated removed in 5.0 but we want to keep it for backwards compatibility checks! */ + readonly initializer?: Expression | undefined; + } + interface PropertyAssignment { + /** @deprecated removed in 5.0 but we want to keep it for backwards compatibility checks! */ + readonly questionToken?: QuestionToken | undefined; + /** @deprecated removed in 5.0 but we want to keep it for backwards compatibility checks! */ + readonly exclamationToken?: ExclamationToken | undefined; + } + interface ShorthandPropertyAssignment { + /** @deprecated removed in 5.0 but we want to keep it for backwards compatibility checks! */ + readonly modifiers?: NodeArray | undefined; + /** @deprecated removed in 5.0 but we want to keep it for backwards compatibility checks! */ + readonly questionToken?: QuestionToken | undefined; + /** @deprecated removed in 5.0 but we want to keep it for backwards compatibility checks! */ + readonly exclamationToken?: ExclamationToken | undefined; + } + interface FunctionTypeNode { + /** @deprecated removed in 5.0 but we want to keep it for backwards compatibility checks! */ + readonly modifiers?: NodeArray | undefined; + } + + /** + * @deprecated don't use this directly as it does not exist pre-4.8; instead use getDecorators from `src/getModifiers.ts`. + */ + function getDecorators(node: HasDecorators): readonly Decorator[] | undefined; + /** + * @deprecated don't use this directly as it does not exist pre-4.8; instead use getModifiers from `src/getModifiers.ts`. + */ + function getModifiers(node: HasModifiers): readonly Modifier[] | undefined; + /** + * @deprecated don't use this directly as it does not exist pre-4.8; instead use getModifiers from `src/getModifiers.ts`. + */ + function canHaveModifiers(node: Node): node is HasModifiers; + /** + * @deprecated don't use this directly as it does not exist pre-4.8; instead use getDecorators from `src/getModifiers.ts`. + */ + function canHaveDecorators(node: Node): node is HasDecorators; } diff --git a/yarn.lock b/yarn.lock index 7e39a3012b81..01d16c1be6af 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6120,7 +6120,7 @@ __metadata: ts-node: 10.7.0 tslint: ^6.1.3 tsx: ^3.12.7 - typescript: ">=4.3.5 <5.4.0 || 5.3.0-beta || 5.3.1-rc || 5.4.0-dev.20231117" + typescript: ">=4.3.5 <5.4.0" languageName: unknown linkType: soft @@ -20161,33 +20161,23 @@ __metadata: languageName: node linkType: hard -"typescript@npm:5.4.0-dev.20231117": - version: 5.4.0-dev.20231117 - resolution: "typescript@npm:5.4.0-dev.20231117" - bin: - tsc: bin/tsc - tsserver: bin/tsserver - checksum: c538a15c3c528275591f40a0d3db191be6410d0da91fa24bd78376b0b674721382ae80f30f1625f565d41c755fab4659e8c6af5072a695401ff6cfb849b3e06e - languageName: node - linkType: hard - -"typescript@patch:typescript@npm%3A5.4.0-dev.20231117#./.yarn/patches/typescript-npm-5.4.0-dev.20231117-78a04a34c5.patch::locator=%40typescript-eslint%2Ftypescript-eslint%40workspace%3A.": - version: 5.4.0-dev.20231117 - resolution: "typescript@patch:typescript@npm%3A5.4.0-dev.20231117#./.yarn/patches/typescript-npm-5.4.0-dev.20231117-78a04a34c5.patch::version=5.4.0-dev.20231117&hash=f57760&locator=%40typescript-eslint%2Ftypescript-eslint%40workspace%3A." +"typescript@npm:5.3.2": + version: 5.3.2 + resolution: "typescript@npm:5.3.2" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: eb20effe30527f1a08981f985cd002519d52a5314cd4feca5e145987bea8ac09e023b95c5d82fddb12bedc8401255209ec6e076a48aab6519dbcafce60f5f219 + checksum: d92534dda639eb825db013203404c1fabca8ac630564283c9e7dc9e64fd9c9346c2de95ecebdf3e6e8c1c32941bca1cfe0da37877611feb9daf8feeaea58d230 languageName: node linkType: hard -"typescript@patch:typescript@patch%3Atypescript@npm%253A5.4.0-dev.20231117%23./.yarn/patches/typescript-npm-5.4.0-dev.20231117-78a04a34c5.patch%3A%3Alocator=%2540typescript-eslint%252Ftypescript-eslint%2540workspace%253A.#~builtin": - version: 5.4.0-dev.20231117 - resolution: "typescript@patch:typescript@patch%3Atypescript@npm%253A5.4.0-dev.20231117%23./.yarn/patches/typescript-npm-5.4.0-dev.20231117-78a04a34c5.patch%3A%3Aversion=5.4.0-dev.20231117&hash=f57760&locator=%2540typescript-eslint%252Ftypescript-eslint%2540workspace%253A.#~builtin::version=5.4.0-dev.20231117&hash=e012d7" +"typescript@patch:typescript@npm%3A5.3.2#~builtin": + version: 5.3.2 + resolution: "typescript@patch:typescript@npm%3A5.3.2#~builtin::version=5.3.2&hash=e012d7" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 923f407da16df26c7810244c3455cc88e1d9db20067fbac7f988f484fea6239f89da9d41ae5ebbf7e0b86e096c4d809cb4b0732c6d40a634cb0fdbefd210bc18 + checksum: 47b2e63418ea7ef7dd0bf359ee41c6fca852c680826089269c5d03673ead471e052b172561f377f9e51212054e5f0fe19dd58085ff7db7b168449a6af704c784 languageName: node linkType: hard