@@ -4,14 +4,29 @@ import isWsl from "is-wsl";
44import { IPackageJson } from "package-json-type" ;
55
66import { readJsonFile } from "./utils" ;
7+ import { ExtensionMetaData , ExtensionPaths , ExtensionMetaDataValue } from "./interfaces/extensionMetaData" ;
78
89export class ExtensionData {
910 /**
10- * This extension details in the form of a key:value Map object.
11+ * Extension data in the form of a key:value Map object.
1112 *
12- * @type {Map<string, string > }
13+ * @type {Map<keyof ExtensionMetaData, ExtensionMetaDataValue > }
1314 */
14- private extensionData = new Map < string , string > ( ) ;
15+ private extensionData = new Map < keyof ExtensionMetaData , ExtensionMetaDataValue > ( ) ;
16+
17+ /**
18+ * Extension discovery paths in the form of a key:value Map object.
19+ *
20+ * @type {Map<keyof ExtensionPaths, string> }
21+ */
22+ private extensionDiscoveryPaths = new Map < keyof ExtensionPaths , string > ( ) ;
23+
24+ /**
25+ * The absolute path of the requested extension.
26+ *
27+ * @type {string }
28+ */
29+ private readonly extensionPath : string ;
1530
1631 /**
1732 * The package.json data for this extension.
@@ -20,93 +35,125 @@ export class ExtensionData {
2035 */
2136 private packageJsonData : IPackageJson ;
2237
23- public constructor ( ) {
38+ public constructor ( extensionPath : string | null = null ) {
39+ // Set the path if provided, otherwise default to this extension's path.
40+ //
41+ // For this extension's path, we use `__dirname` and go up two levels
42+ // (from "out/src" to the extension root). This path is also used to locate all other
43+ // user-installed extensions later for the `userExtensionsPath` discovery path.
44+ this . extensionPath = extensionPath ?? path . join ( __dirname , "../../" ) ;
45+
2446 this . packageJsonData = this . getExtensionPackageJsonData ( ) ;
25- this . setExtensionData ( ) ;
47+
48+ // Only proceed with extension data setup if packageJsonData is NOT null.
49+ if ( this . packageJsonData !== null ) {
50+ this . setExtensionData ( ) ;
51+ }
52+
53+ this . setExtensionDiscoveryPaths ( ) ;
2654 }
2755
2856 /**
2957 * Get the names, id, and version of this extension from package.json.
3058 *
31- * @returns {IPackageJson } The package.json data for this extension, with extra custom keys.
59+ * @returns {IPackageJson | null } The package.json data for this extension, with extra custom keys.
3260 */
33- private getExtensionPackageJsonData ( ) : IPackageJson {
34- const extensionPath = path . join ( __dirname , "../../" ) ;
35-
36- const packageJSON : IPackageJson = readJsonFile ( path . join ( extensionPath , "package.json" ) ) ;
37-
38- // Set the id (publisher.name) into the packageJSON object as a new `id` key.
39- packageJSON . id = `${ packageJSON . publisher } .${ packageJSON . name } ` ;
40- packageJSON . extensionPath = extensionPath ;
41-
42- // The configuration settings namespace is a shortened version of the extension name.
43- // We just need to replace "automatic" with "auto" in the name.
44- const settingsNamespace : string = packageJSON . name . replace ( "automatic" , "auto" ) ;
45- // Set the namespace to the packageJSON `configuration` object as a new `namespace` key.
46- packageJSON . contributes . configuration . namespace = settingsNamespace ;
47-
48- return packageJSON ;
61+ private getExtensionPackageJsonData ( ) : IPackageJson | null {
62+ // Get the package.json file path.
63+ const packageJSONPath = path . join ( this . extensionPath , "package.json" ) ;
64+ return readJsonFile < IPackageJson > ( packageJSONPath , false ) ;
4965 }
5066
5167 /**
5268 * Set the extension data into the extensionData Map.
5369 */
5470 private setExtensionData ( ) {
55- // Set all entries in the extensionData Map.
56- Object . entries ( this . createExtensionData ( ) ) . forEach ( ( [ key , value ] ) => {
57- this . extensionData . set ( key , value ) ;
58- } ) ;
71+ // Create the extension ID (publisher.name).
72+ const id = `${ this . packageJsonData . publisher } .${ this . packageJsonData . name } ` ;
73+
74+ // Set each key-value pair directly into the Map
75+ this . extensionData . set ( "id" , id ) ;
76+ this . extensionData . set ( "name" , this . packageJsonData . name ) ;
77+
78+ // Only set the namespace if it dealing with this extension.
79+ if ( this . packageJsonData . name === "automatic-comment-blocks" ) {
80+ // The configuration settings namespace is a shortened version of the extension name.
81+ // We just need to replace "automatic" with "auto" in the name.
82+ const settingsNamespace : string = this . packageJsonData . name . replace ( "automatic" , "auto" ) ;
83+
84+ this . extensionData . set ( "namespace" , settingsNamespace ) ;
85+ }
86+
87+ this . extensionData . set ( "displayName" , this . packageJsonData . displayName ) ;
88+ this . extensionData . set ( "version" , this . packageJsonData . version ) ;
89+ this . extensionData . set ( "extensionPath" , this . extensionPath ) ;
90+ this . extensionData . set ( "packageJSON" , this . packageJsonData ) ;
5991 }
6092
6193 /**
62- * Create the extension data object for the extensionData Map.
63- * It also helps for type inference intellisense in the get method.
64- *
65- * @returns The extension data object with keys and values.
94+ * Set the extension discovery paths into the extensionDiscoveryPaths Map.
6695 */
67- private createExtensionData ( ) {
96+ private setExtensionDiscoveryPaths ( ) {
6897 // The path to the user extensions.
69- const userExtensionsPath = isWsl
70- ? path . join ( vscode . env . appRoot , "../../" , "extensions" )
71- : path . join ( this . packageJsonData . extensionPath , "../" ) ;
72-
73- // Set the keys and values for the Map.
74- // The keys will also be used for type inference in VSCode intellisense.
75- return {
76- id : this . packageJsonData . id ,
77- name : this . packageJsonData . contributes . configuration . namespace ,
78- displayName : this . packageJsonData . displayName ,
79- version : this . packageJsonData . version ,
80- userExtensionsPath : userExtensionsPath ,
81- // The path to the built-in extensions.
82- // This env variable changes when on WSL to it's WSL-built-in extensions path.
83- builtInExtensionsPath : path . join ( vscode . env . appRoot , "extensions" ) ,
84-
85- // Only set these if running in WSL.
86- ...( isWsl && {
87- WindowsUserExtensionsPathFromWsl : path . dirname ( process . env . VSCODE_WSL_EXT_LOCATION ! ) ,
88- WindowsBuiltInExtensionsPathFromWsl : path . join ( process . env . VSCODE_CWD ! , "resources/app/extensions" ) ,
89- } ) ,
90- } as const ;
98+ //
99+ // On Windows/Linux/Mac: ~/.vscode[-server|remote]/extensions
100+ // On WSL: ~/.vscode-[server|remote]/extensions
101+ const userExtensionsPath = isWsl ? path . join ( vscode . env . appRoot , "../../" , "extensions" ) : path . join ( this . extensionPath , "../" ) ;
102+
103+ this . extensionDiscoveryPaths . set ( "userExtensionsPath" , userExtensionsPath ) ;
104+ // The path to the built-in extensions.
105+ // This env variable changes when on WSL to it's WSL-built-in extensions path.
106+ this . extensionDiscoveryPaths . set ( "builtInExtensionsPath" , path . join ( vscode . env . appRoot , "extensions" ) ) ;
107+
108+ // Only set these if running in WSL
109+ if ( isWsl ) {
110+ this . extensionDiscoveryPaths . set ( "WindowsUserExtensionsPathFromWsl" , path . dirname ( process . env . VSCODE_WSL_EXT_LOCATION ! ) ) ;
111+ this . extensionDiscoveryPaths . set ( "WindowsBuiltInExtensionsPathFromWsl" , path . join ( process . env . VSCODE_CWD ! , "resources/app/extensions" ) ) ;
112+ }
91113 }
92114
93115 /**
94116 * Get the extension's data by a specified key.
95117 *
96118 * @param {K } key The key of the extension detail to get.
97119 *
98- * @returns {ReturnType<typeof this.createExtensionData>[K] | undefined } The value of the extension detail, or undefined if the key does not exist.
120+ * @returns {ExtensionMetaData[K] | undefined } The value of the extension detail, or undefined if the key does not exist.
121+ */
122+ public get < K extends keyof ExtensionMetaData > ( key : K ) : ExtensionMetaData [ K ] | undefined {
123+ return this . extensionData . get ( key ) as ExtensionMetaData [ K ] | undefined ;
124+ }
125+
126+ /**
127+ * Get all extension data as a plain object.
128+ *
129+ * @returns {ExtensionMetaData } A plain object containing all extension details.
130+ */
131+ public getAll ( ) : ExtensionMetaData | null {
132+ // If no data, return null
133+ if ( this . extensionData . size === 0 ) {
134+ return null ;
135+ }
136+
137+ return Object . fromEntries ( this . extensionData ) as unknown as ExtensionMetaData ;
138+ }
139+
140+ /**
141+ * Get the extension discovery paths by a specified key.
142+ *
143+ * @param {K } key The key of the specific path to get.
144+ *
145+ * @returns {ExtensionPaths[K] | undefined } The value of the extension detail, or undefined if the key does not exist.
99146 */
100- public get < K extends keyof ReturnType < typeof this . createExtensionData > > ( key : K ) : ReturnType < typeof this . createExtensionData > [ K ] | undefined {
101- return this . extensionData . get ( key ) as ReturnType < typeof this . createExtensionData > [ K ] | undefined ;
147+ public getExtensionDiscoveryPath < K extends keyof ExtensionPaths > ( key : K ) : ExtensionPaths [ K ] | undefined {
148+ return this . extensionDiscoveryPaths . get ( key ) as ExtensionPaths [ K ] | undefined ;
102149 }
103150
104151 /**
105- * Get all extension data .
152+ * Get all extension discovery paths .
106153 *
107- * @returns {ReadonlyMap<string , string> } A read-only Map containing all extension details .
154+ * @returns {ReadonlyMap<keyof ExtensionPaths , string> } A read-only Map containing all extension discovery paths .
108155 */
109- public getAll ( ) : ReadonlyMap < string , string > {
110- return this . extensionData ;
156+ public getAllExtensionDiscoveryPaths ( ) : ReadonlyMap < keyof ExtensionPaths , string > {
157+ return this . extensionDiscoveryPaths ;
111158 }
112159}
0 commit comments