diff --git a/docs/Camera.md b/docs/Camera.md index c68535dffe..9408680a54 100644 --- a/docs/Camera.md +++ b/docs/Camera.md @@ -50,7 +50,10 @@ The location on which the map should center. ### bounds ```tsx -intersection +type Bounds = { + ne: Position; /* FIX ME NO DESCRIPTION */ + sw: Position; /* FIX ME NO DESCRIPTION */ +} ``` The corners of a box around which the map should bound. Contains padding props for backwards compatibility; the root `padding` prop should be used instead. @@ -217,7 +220,7 @@ compatibility; the root `padding` prop should be used instead. */ zoomLevel: number; /* The zoom level of the map. */ padding: signature; /* The viewport padding in points. */ animationDuration: number; /* The duration the map takes to animate to a new configuration. */ - animationMode: union; /* The easing or path the camera uses to animate to a new configuration. */ + animationMode: 'flyTo' \| 'easeTo' \| 'linearTo' \| 'moveTo' \| 'none'; /* The easing or path the camera uses to animate to a new configuration. */ } ``` The configuration that the camera falls back on, if no other values are specified. diff --git a/docs/Images.md b/docs/Images.md index 4936a8d5ab..964865fea4 100644 --- a/docs/Images.md +++ b/docs/Images.md @@ -17,7 +17,7 @@ Images defines the images used in Symbol etc. layers. ```tsx type Images = { - [object Object]: union; /* FIX ME NO DESCRIPTION */ + [key: string]: string \| ImageSourcePropType \| ImageEntryData; /* FIX ME NO DESCRIPTION */ } ``` Specifies the external images in key-value pairs required for the shape source. diff --git a/docs/Models.md b/docs/Models.md index 344d3a17f3..5711373a05 100644 --- a/docs/Models.md +++ b/docs/Models.md @@ -17,7 +17,7 @@ Name of 3D model assets to be used in the map ```tsx type Models = { - [object Object]: union; /* FIX ME NO DESCRIPTION */ + [key: string]: string \| number; /* FIX ME NO DESCRIPTION */ } ``` _required_ diff --git a/docs/StyleImport.md b/docs/StyleImport.md index 92e3eda9f3..6c45a143b7 100644 --- a/docs/StyleImport.md +++ b/docs/StyleImport.md @@ -39,12 +39,38 @@ existing is now always required as true ```tsx type Config = { - [object Object]: string; /* FIX ME NO DESCRIPTION */ + lightPreset: 'dawn' \| 'day' \| 'dusk' \| 'night'; /* Light preset: controls time-of-day lighting */ + theme: 'default' \| 'faded' \| 'monochrome'; /* Color theme */ + font: string; /* Font family for labels (e.g., 'Montserrat') */ + show3dObjects: boolean; /* Show generic 3D objects */ + show3dBuildings: boolean; /* Show extruded 3D buildings */ + show3dFacades: boolean; /* Show 3D building facades (v11.16+) */ + show3dLandmarks: boolean; /* Show 3D landmarks (v11.16+) */ + show3dTrees: boolean; /* Show 3D trees (v11.17+) */ + showPointOfInterestLabels: boolean; /* Show point of interest labels */ + showTransitLabels: boolean; /* Show transit labels */ + showPlaceLabels: boolean; /* Show place labels */ + showRoadLabels: boolean; /* Show road labels */ + showPedestrianRoads: boolean; /* Show pedestrian roads */ + colorBuildings: string; /* Color override for buildings (v11.18+) */ + colorCommercial: string; /* Color override for commercial areas (v11.18+) */ + colorEducation: string; /* Color override for education areas (v11.18+) */ + colorIndustrial: string; /* Color override for industrial areas (v11.18+) */ + colorLand: string; /* Color override for land (v11.18+) */ + colorMedical: string; /* Color override for medical areas (v11.18+) */ + colorRoads: string; /* Color override for roads (v11.18+) */ + colorMotorways: string; /* Color override for motorways (v11.18+) */ + colorWater: string; /* Color override for water (v11.18+) */ + colorGreenspaces: string; /* Color override for greenspaces (v11.18+) */ + colorBoundaries: string; /* Color override for boundaries (v11.18+) */ } ``` _required_ config is a dictionary of configuration options for the style import. +When using the Mapbox Standard style with `id="basemap"`, use {@link StandardStyleConfig} +keys for autocomplete. Arbitrary keys are also accepted for forward compatibility. + See https://github.com/mapbox/mapbox-maps-ios/blob/main/Sources/MapboxMaps/Documentation.docc/Migrate%20to%20v11.md#21-the-mapbox-standard-style diff --git a/docs/docs.json b/docs/docs.json index 399a40f854..1e6e20971e 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -747,7 +747,25 @@ { "name": "bounds", "required": false, - "type": "intersection", + "type": { + "name": "shape", + "value": [ + { + "name": "ne", + "required": true, + "type": "Position", + "default": "none", + "description": "FIX ME NO DESCRIPTION" + }, + { + "name": "sw", + "required": true, + "type": "Position", + "default": "none", + "description": "FIX ME NO DESCRIPTION" + } + ] + }, "default": "none", "description": "The corners of a box around which the map should bound. Contains padding props for backwards\ncompatibility; the root `padding` prop should be used instead." }, @@ -971,7 +989,7 @@ { "name": "animationMode", "required": false, - "type": "union", + "type": "'flyTo' \\| 'easeTo' \\| 'linearTo' \\| 'moveTo' \\| 'none'", "default": "none", "description": "The easing or path the camera uses to animate to a new configuration." } @@ -3479,11 +3497,9 @@ "name": "shape", "value": [ { - "name": { - "name": "string" - }, + "name": "[key: string]", "required": true, - "type": "union", + "type": "string \\| ImageSourcePropType \\| ImageEntryData", "default": "none", "description": "FIX ME NO DESCRIPTION" } @@ -6360,11 +6376,9 @@ "name": "shape", "value": [ { - "name": { - "name": "string" - }, + "name": "[key: string]", "required": true, - "type": "union", + "type": "string \\| number", "default": "none", "description": "FIX ME NO DESCRIPTION" } @@ -8130,18 +8144,177 @@ "name": "shape", "value": [ { - "name": { - "name": "string" - }, - "required": true, + "name": "lightPreset", + "required": false, + "type": "'dawn' \\| 'day' \\| 'dusk' \\| 'night'", + "default": "none", + "description": "Light preset: controls time-of-day lighting" + }, + { + "name": "theme", + "required": false, + "type": "'default' \\| 'faded' \\| 'monochrome'", + "default": "none", + "description": "Color theme" + }, + { + "name": "font", + "required": false, "type": "string", "default": "none", - "description": "FIX ME NO DESCRIPTION" + "description": "Font family for labels (e.g., 'Montserrat')" + }, + { + "name": "show3dObjects", + "required": false, + "type": "boolean", + "default": "none", + "description": "Show generic 3D objects" + }, + { + "name": "show3dBuildings", + "required": false, + "type": "boolean", + "default": "none", + "description": "Show extruded 3D buildings" + }, + { + "name": "show3dFacades", + "required": false, + "type": "boolean", + "default": "none", + "description": "Show 3D building facades (v11.16+)" + }, + { + "name": "show3dLandmarks", + "required": false, + "type": "boolean", + "default": "none", + "description": "Show 3D landmarks (v11.16+)" + }, + { + "name": "show3dTrees", + "required": false, + "type": "boolean", + "default": "none", + "description": "Show 3D trees (v11.17+)" + }, + { + "name": "showPointOfInterestLabels", + "required": false, + "type": "boolean", + "default": "none", + "description": "Show point of interest labels" + }, + { + "name": "showTransitLabels", + "required": false, + "type": "boolean", + "default": "none", + "description": "Show transit labels" + }, + { + "name": "showPlaceLabels", + "required": false, + "type": "boolean", + "default": "none", + "description": "Show place labels" + }, + { + "name": "showRoadLabels", + "required": false, + "type": "boolean", + "default": "none", + "description": "Show road labels" + }, + { + "name": "showPedestrianRoads", + "required": false, + "type": "boolean", + "default": "none", + "description": "Show pedestrian roads" + }, + { + "name": "colorBuildings", + "required": false, + "type": "string", + "default": "none", + "description": "Color override for buildings (v11.18+)" + }, + { + "name": "colorCommercial", + "required": false, + "type": "string", + "default": "none", + "description": "Color override for commercial areas (v11.18+)" + }, + { + "name": "colorEducation", + "required": false, + "type": "string", + "default": "none", + "description": "Color override for education areas (v11.18+)" + }, + { + "name": "colorIndustrial", + "required": false, + "type": "string", + "default": "none", + "description": "Color override for industrial areas (v11.18+)" + }, + { + "name": "colorLand", + "required": false, + "type": "string", + "default": "none", + "description": "Color override for land (v11.18+)" + }, + { + "name": "colorMedical", + "required": false, + "type": "string", + "default": "none", + "description": "Color override for medical areas (v11.18+)" + }, + { + "name": "colorRoads", + "required": false, + "type": "string", + "default": "none", + "description": "Color override for roads (v11.18+)" + }, + { + "name": "colorMotorways", + "required": false, + "type": "string", + "default": "none", + "description": "Color override for motorways (v11.18+)" + }, + { + "name": "colorWater", + "required": false, + "type": "string", + "default": "none", + "description": "Color override for water (v11.18+)" + }, + { + "name": "colorGreenspaces", + "required": false, + "type": "string", + "default": "none", + "description": "Color override for greenspaces (v11.18+)" + }, + { + "name": "colorBoundaries", + "required": false, + "type": "string", + "default": "none", + "description": "Color override for boundaries (v11.18+)" } ] }, "default": "none", - "description": "config is a dictionary of configuration options for the style import.\n\nSee https://github.com/mapbox/mapbox-maps-ios/blob/main/Sources/MapboxMaps/Documentation.docc/Migrate%20to%20v11.md#21-the-mapbox-standard-style" + "description": "config is a dictionary of configuration options for the style import.\n\nWhen using the Mapbox Standard style with `id=\"basemap\"`, use {@link StandardStyleConfig}\nkeys for autocomplete. Arbitrary keys are also accepted for forward compatibility.\n\nSee https://github.com/mapbox/mapbox-maps-ios/blob/main/Sources/MapboxMaps/Documentation.docc/Migrate%20to%20v11.md#21-the-mapbox-standard-style" } ], "fileNameWithExt": "StyleImport.tsx", diff --git a/example/src/examples/V11/StyleImportConfig.tsx b/example/src/examples/V11/StyleImportConfig.tsx index b30d410708..030c61140d 100644 --- a/example/src/examples/V11/StyleImportConfig.tsx +++ b/example/src/examples/V11/StyleImportConfig.tsx @@ -3,7 +3,7 @@ import { useState } from 'react'; import { MapView, Camera, StyleImport } from '@rnmapbox/maps'; const StyleImportConfig = () => { - const [lightPreset, setLightPreset] = useState('night'); + const [lightPreset, setLightPreset] = useState<'day' | 'night'>('night'); const nextLightPreset = lightPreset === 'night' ? 'day' : 'night'; return ( <> diff --git a/package.json b/package.json index 2f685de63e..ee00c6e5c9 100644 --- a/package.json +++ b/package.json @@ -116,7 +116,7 @@ "node-dir": "0.1.17", "prettier": "^3.6.2", "react": "19.1.0", - "react-docgen": "rnmapbox/react-docgen#rnmapbox-dist-react-docgen-v6", + "react-docgen": "rnmapbox/react-docgen#rnmapbox-dist-react-docgen-v8", "react-native": "0.81.1", "react-native-builder-bob": "^0.40.13", "react-test-renderer": "19.1.0", diff --git a/scripts/autogenHelpers/DocJSONBuilder.mjs b/scripts/autogenHelpers/DocJSONBuilder.mjs index 1128952f68..23be1a0a92 100644 --- a/scripts/autogenHelpers/DocJSONBuilder.mjs +++ b/scripts/autogenHelpers/DocJSONBuilder.mjs @@ -133,6 +133,9 @@ class DocJSONBuilder { if (propMeta.value) { result.type.value = propMeta.value; } + if (propMeta.raw) { + result.type.raw = propMeta.raw; + } return result; } @@ -217,9 +220,13 @@ class DocJSONBuilder { const { properties } = tsType.signature; if (properties) { const value = properties.map((kv) => { + const key = + typeof kv.key === 'object' + ? `[key: ${kv.key.name}]` + : kv.key; return mapProp( mapNestedProp({ ...kv.value, description: kv.description }), - kv.key, + key, false, ); }); @@ -233,6 +240,14 @@ class DocJSONBuilder { } } else if (tsType.name === 'signature' && tsType.type === 'function') { return { name: 'func', funcSignature: tsTypeDump(tsType) }; + } else if (tsType.name === 'intersection' && tsType.elements) { + const objectElement = tsType.elements.find( + (e) => e.name === 'signature' && e.type === 'object', + ); + if (objectElement) { + return tsTypeDescType(objectElement); + } + return tsType.raw?.replace(/\|/g, '\\|') || tsType.name; } else if (tsType.name === 'union') { if (tsType.raw) { // Props @@ -259,16 +274,28 @@ class DocJSONBuilder { return result; } + function resolveType(propMeta) { + if (propMeta.type?.name === 'union' && propMeta.type?.raw) { + return propMeta.type.raw + .replace(/\n\s*/g, ' ') + .replace(/^\s*\|\s*/, '') + .replace(/\|/g, '\\|') + .trim(); + } + return ( + propMeta.type?.name || + tsTypeDescType(propMeta.tsType) || + 'FIX ME UNKNOWN TYPE' + ); + } + function mapProp(propMeta, propName, array) { let result = {}; if (!array) { result = { name: propName || 'FIX ME NO NAME', required: propMeta.required || false, - type: - propMeta.type?.name || - tsTypeDescType(propMeta.tsType) || - 'FIX ME UNKNOWN TYPE', + type: resolveType(propMeta), default: !propMeta.defaultValue ? 'none' : propMeta.defaultValue.value.replace(/\n/g, ''), diff --git a/src/Mapbox.native.ts b/src/Mapbox.native.ts index 81abb78aff..c23a059069 100644 --- a/src/Mapbox.native.ts +++ b/src/Mapbox.native.ts @@ -21,6 +21,7 @@ export { default as PointAnnotation } from './components/PointAnnotation'; export { default as Annotation } from './components/Annotation'; export { default as Callout } from './components/Callout'; export { default as StyleImport } from './components/StyleImport'; +export type { StandardStyleConfig } from './components/StyleImport'; export { default as UserLocation, UserLocationRenderMode, diff --git a/src/components/StyleImport.tsx b/src/components/StyleImport.tsx index d7de5035da..fcc2d458af 100644 --- a/src/components/StyleImport.tsx +++ b/src/components/StyleImport.tsx @@ -2,6 +2,67 @@ import { memo } from 'react'; import NativeStyleImport from '../specs/RNMBXStyleImportNativeComponent'; +/** + * Configuration options for the Mapbox Standard style. + * + * These options are available when using `StyleImport` with `id="basemap"` and + * a Standard style URL (e.g., `mapbox://styles/mapbox/standard`). + */ +export type StandardStyleConfig = { + /** Light preset: controls time-of-day lighting */ + lightPreset?: 'dawn' | 'day' | 'dusk' | 'night'; + /** Color theme */ + theme?: 'default' | 'faded' | 'monochrome'; + /** Font family for labels (e.g., 'Montserrat') */ + font?: string; + /** Show generic 3D objects */ + show3dObjects?: boolean; + /** Show extruded 3D buildings */ + show3dBuildings?: boolean; + /** Show 3D building facades (v11.16+) */ + show3dFacades?: boolean; + /** Show 3D landmarks (v11.16+) */ + show3dLandmarks?: boolean; + /** Show 3D trees (v11.17+) */ + show3dTrees?: boolean; + /** Show point of interest labels */ + showPointOfInterestLabels?: boolean; + /** Show transit labels */ + showTransitLabels?: boolean; + /** Show place labels */ + showPlaceLabels?: boolean; + /** Show road labels */ + showRoadLabels?: boolean; + /** Show pedestrian roads */ + showPedestrianRoads?: boolean; + /** Color override for buildings (v11.18+) */ + colorBuildings?: string; + /** Color override for commercial areas (v11.18+) */ + colorCommercial?: string; + /** Color override for education areas (v11.18+) */ + colorEducation?: string; + /** Color override for industrial areas (v11.18+) */ + colorIndustrial?: string; + /** Color override for land (v11.18+) */ + colorLand?: string; + /** Color override for medical areas (v11.18+) */ + colorMedical?: string; + /** Color override for roads (v11.18+) */ + colorRoads?: string; + /** Color override for motorways (v11.18+) */ + colorMotorways?: string; + /** Color override for water (v11.18+) */ + colorWater?: string; + /** Color override for greenspaces (v11.18+) */ + colorGreenspaces?: string; + /** Color override for boundaries (v11.18+) */ + colorBoundaries?: string; +}; + +type StyleImportConfig = StandardStyleConfig & { + [key: string]: string | boolean | undefined; +}; + type Props = { /** * id of the style import (eg. basemap) @@ -16,11 +77,12 @@ type Props = { /** * config is a dictionary of configuration options for the style import. * + * When using the Mapbox Standard style with `id="basemap"`, use {@link StandardStyleConfig} + * keys for autocomplete. Arbitrary keys are also accepted for forward compatibility. + * * See https://github.com/mapbox/mapbox-maps-ios/blob/main/Sources/MapboxMaps/Documentation.docc/Migrate%20to%20v11.md#21-the-mapbox-standard-style */ - config: { - [key: string]: string; - }; + config: StyleImportConfig; }; /** @@ -29,5 +91,10 @@ type Props = { * See https://github.com/mapbox/mapbox-maps-ios/blob/main/Sources/MapboxMaps/Documentation.docc/Migrate%20to%20v11.md#21-the-mapbox-standard-style */ export default memo((props: Props) => { - return ; + return ( + + ); }); diff --git a/yarn.lock b/yarn.lock index bad742cc5e..61933d2b99 100644 --- a/yarn.lock +++ b/yarn.lock @@ -105,7 +105,7 @@ __metadata: languageName: node linkType: hard -"@babel/core@npm:^7.11.6, @babel/core@npm:^7.12.3, @babel/core@npm:^7.18.10, @babel/core@npm:^7.18.9, @babel/core@npm:^7.20.0, @babel/core@npm:^7.23.9, @babel/core@npm:^7.25.2, @babel/core@npm:^7.27.4, @babel/core@npm:^7.28.3": +"@babel/core@npm:^7.11.6, @babel/core@npm:^7.12.3, @babel/core@npm:^7.18.10, @babel/core@npm:^7.20.0, @babel/core@npm:^7.23.9, @babel/core@npm:^7.25.2, @babel/core@npm:^7.27.4, @babel/core@npm:^7.28.3": version: 7.28.4 resolution: "@babel/core@npm:7.28.4" dependencies: @@ -128,7 +128,7 @@ __metadata: languageName: node linkType: hard -"@babel/core@npm:^7.24.4": +"@babel/core@npm:^7.24.4, @babel/core@npm:^7.28.0": version: 7.29.0 resolution: "@babel/core@npm:7.29.0" dependencies: @@ -1808,7 +1808,7 @@ __metadata: languageName: node linkType: hard -"@babel/traverse--for-generate-function-map@npm:@babel/traverse@^7.25.3, @babel/traverse@npm:^7.10.5, @babel/traverse@npm:^7.18.11, @babel/traverse@npm:^7.18.9, @babel/traverse@npm:^7.25.3, @babel/traverse@npm:^7.27.1, @babel/traverse@npm:^7.28.0, @babel/traverse@npm:^7.28.3, @babel/traverse@npm:^7.28.4": +"@babel/traverse--for-generate-function-map@npm:@babel/traverse@^7.25.3, @babel/traverse@npm:^7.10.5, @babel/traverse@npm:^7.18.11, @babel/traverse@npm:^7.25.3, @babel/traverse@npm:^7.27.1, @babel/traverse@npm:^7.28.0, @babel/traverse@npm:^7.28.3, @babel/traverse@npm:^7.28.4": version: 7.28.4 resolution: "@babel/traverse@npm:7.28.4" dependencies: @@ -1838,7 +1838,7 @@ __metadata: languageName: node linkType: hard -"@babel/types@npm:^7.0.0, @babel/types@npm:^7.18.10, @babel/types@npm:^7.18.9, @babel/types@npm:^7.20.7, @babel/types@npm:^7.25.2, @babel/types@npm:^7.26.0, @babel/types@npm:^7.27.1, @babel/types@npm:^7.27.3, @babel/types@npm:^7.28.2, @babel/types@npm:^7.28.4, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4": +"@babel/types@npm:^7.0.0, @babel/types@npm:^7.18.10, @babel/types@npm:^7.20.7, @babel/types@npm:^7.25.2, @babel/types@npm:^7.26.0, @babel/types@npm:^7.27.1, @babel/types@npm:^7.27.3, @babel/types@npm:^7.28.2, @babel/types@npm:^7.28.4, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4": version: 7.28.4 resolution: "@babel/types@npm:7.28.4" dependencies: @@ -4989,7 +4989,7 @@ __metadata: node-dir: 0.1.17 prettier: ^3.6.2 react: 19.1.0 - react-docgen: "rnmapbox/react-docgen#rnmapbox-dist-react-docgen-v6" + react-docgen: "rnmapbox/react-docgen#rnmapbox-dist-react-docgen-v8" react-native: 0.81.1 react-native-builder-bob: ^0.40.13 react-test-renderer: 19.1.0 @@ -5375,7 +5375,7 @@ __metadata: languageName: node linkType: hard -"@types/babel__core@npm:^7.1.14, @types/babel__core@npm:^7.18.0, @types/babel__core@npm:^7.20.5": +"@types/babel__core@npm:^7.1.14, @types/babel__core@npm:^7.20.5": version: 7.20.5 resolution: "@types/babel__core@npm:7.20.5" dependencies: @@ -5407,7 +5407,7 @@ __metadata: languageName: node linkType: hard -"@types/babel__traverse@npm:*, @types/babel__traverse@npm:^7.0.6, @types/babel__traverse@npm:^7.18.0": +"@types/babel__traverse@npm:*, @types/babel__traverse@npm:^7.0.6, @types/babel__traverse@npm:^7.20.7": version: 7.28.0 resolution: "@types/babel__traverse@npm:7.28.0" dependencies: @@ -5444,10 +5444,10 @@ __metadata: languageName: node linkType: hard -"@types/doctrine@npm:^0.0.7": - version: 0.0.7 - resolution: "@types/doctrine@npm:0.0.7" - checksum: 7d34920ea10a785aafb55a93e30ed8f71dfc05952441550f838e89e47d6af9717909e728bc795d49ff802f03fb88f21b139730a82f91015176680cb67ab9c5b4 +"@types/doctrine@npm:^0.0.9": + version: 0.0.9 + resolution: "@types/doctrine@npm:0.0.9" + checksum: 3909eaca42e7386b2ab866f082b78da3e00718d2fa323597e254feb0556c678b41f2c490729067433630083ac9c806ec6ae1e146754f7f8ba7d3e43ed68d6500 languageName: node linkType: hard @@ -18397,21 +18397,21 @@ __metadata: languageName: node linkType: hard -"react-docgen@rnmapbox/react-docgen#rnmapbox-dist-react-docgen-v6": - version: 6.0.4 - resolution: "react-docgen@https://github.com/rnmapbox/react-docgen.git#commit=6eb6a672cfd8fe2cb2c3f507bf1c48d1c20b4eed" - dependencies: - "@babel/core": ^7.18.9 - "@babel/traverse": ^7.18.9 - "@babel/types": ^7.18.9 - "@types/babel__core": ^7.18.0 - "@types/babel__traverse": ^7.18.0 - "@types/doctrine": ^0.0.7 +"react-docgen@rnmapbox/react-docgen#rnmapbox-dist-react-docgen-v8": + version: 8.0.3 + resolution: "react-docgen@https://github.com/rnmapbox/react-docgen.git#commit=444f1b499bb5db2c07fb11eecefdbeb1252bea14" + dependencies: + "@babel/core": ^7.28.0 + "@babel/traverse": ^7.28.0 + "@babel/types": ^7.28.2 + "@types/babel__core": ^7.20.5 + "@types/babel__traverse": ^7.20.7 + "@types/doctrine": ^0.0.9 "@types/resolve": ^1.20.2 doctrine: ^3.0.0 resolve: ^1.22.1 strip-indent: ^4.0.0 - checksum: f63d2a1e8dd0eae787281da9372c234059cfdbced797ead0e5493bb350d0ee5d67323feabdf734210b62ce7478355a1e7747ce4a1531373b727e17e1f74fb2d2 + checksum: 0265fa8ee37d36c9c503f0fce3f9b5555308059f40f1bc814ce3135cf4661395972b449ca8c45dec78d692fbdd593bf2cbdeb1aa57001a523f5ad324b65c6c06 languageName: node linkType: hard