forked from material-theme/vsc-material-theme
-
Notifications
You must be signed in to change notification settings - Fork 0
/
extension-manager.ts
149 lines (124 loc) · 4.99 KB
/
extension-manager.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
import {extensions, workspace, window, Uri, ExtensionContext, FileSystemError} from 'vscode';
import {posix} from 'path';
import {CONFIG_FILE_NAME, USER_CONFIG_FILE_NAME, MATERIAL_THEME_EXT_ID} from '../env';
type MaterialThemeConfig = {
accents: Record<string, string>;
accentsProperties: Record<string, {alpha: number; value: null }>;
changelog?: { lastversion?: string };
};
type PackageJSON = {
version: string;
contributes: {
themes: Array<{
label: string;
}>;
};
};
type InstallationType = {
firstInstall: boolean;
update: boolean;
};
export interface IExtensionManager {
init: (context: ExtensionContext) => Promise<void>;
getPackageJSON: () => PackageJSON;
getConfig: () => MaterialThemeConfig;
getInstallationType: () => Record<string, unknown>;
updateConfig: (config: Partial<MaterialThemeConfig>) => Promise<void>;
VERSION_KEY: string;
}
class ExtensionManager implements IExtensionManager {
get VERSION_KEY() {
return 'vsc-material-theme.version';
}
installationType: InstallationType;
private readonly configFileUri: Uri;
private readonly userConfigFileUri: Uri;
private configJSON: MaterialThemeConfig;
constructor() {
const extensionFolderUri = Uri.file(extensions.getExtension(MATERIAL_THEME_EXT_ID).extensionPath);
this.configFileUri = extensionFolderUri.with({path: posix.join(extensionFolderUri.path, CONFIG_FILE_NAME)});
this.userConfigFileUri = extensionFolderUri.with({
path: this.migrateFileLocation(
posix.join(extensionFolderUri.path, USER_CONFIG_FILE_NAME),
posix.join(ExtensionContext.globalStorageUri.fsPath, USER_CONFIG_FILE_NAME)
)
});
}
getPackageJSON(): PackageJSON {
return extensions.getExtension(MATERIAL_THEME_EXT_ID).packageJSON;
}
getConfig(): MaterialThemeConfig {
return this.configJSON;
}
getInstallationType(): InstallationType {
return this.installationType;
}
async updateConfig(config: Partial<MaterialThemeConfig>): Promise<void> {
const newConfig = {...this.configJSON, ...config};
await workspace.fs.writeFile(this.configFileUri, Buffer.from(JSON.stringify(newConfig), 'utf-8'));
}
async init(context: ExtensionContext): Promise<void> {
try {
const packageJSON = this.getPackageJSON();
const userConfig = await this.getUserConfig();
const mementoStateVersion = context.globalState.get(this.VERSION_KEY);
const themeNeverUsed = mementoStateVersion === undefined || typeof mementoStateVersion !== 'string';
this.installationType = {
update: userConfig && this.isVersionUpdate(userConfig, packageJSON),
firstInstall: !userConfig && themeNeverUsed
};
// Theme not used before across devices
if (themeNeverUsed) {
await context.globalState.update(this.VERSION_KEY, packageJSON.version);
}
const configBuffer = await workspace.fs.readFile(this.configFileUri);
const configContent = Buffer.from(configBuffer).toString('utf8');
this.configJSON = JSON.parse(configContent) as MaterialThemeConfig;
const userConfigUpdate = {...this.configJSON, changelog: {lastversion: packageJSON.version}};
await workspace.fs.writeFile(
this.userConfigFileUri,
Buffer.from(JSON.stringify(userConfigUpdate), 'utf-8')
);
} catch (error) {
this.configJSON = {accentsProperties: {}, accents: {}};
await window
.showErrorMessage(`Material Theme: there was an error while loading the configuration. Please retry or open an issue: ${String(error)}`);
}
}
private isVersionUpdate(userConfig: MaterialThemeConfig, packageJSON: PackageJSON): boolean {
const splitVersion = (input: string): {major: number; minor: number; patch: number} => {
const [major, minor, patch] = input.split('.').map(i => parseInt(i, 10));
return {major, minor, patch};
};
const versionCurrent = splitVersion(packageJSON.version);
const versionOld = splitVersion(userConfig.changelog.lastversion);
const update = (
versionCurrent.major > versionOld.major ||
versionCurrent.minor > versionOld.minor ||
versionCurrent.patch > versionOld.patch
);
return update;
}
private async getUserConfig(): Promise<MaterialThemeConfig | undefined> {
try {
const configBuffer = await workspace.fs.readFile(this.userConfigFileUri);
const configContent = Buffer.from(configBuffer).toString('utf8');
return JSON.parse(configContent) as MaterialThemeConfig;
} catch {}
}
private async migrateFileLocation(oldLocation: string, newLocation: string): Promise<string> {
try {
// Check if the old location exists
await workspace.fs.stat(oldLocation);
await workspace.fs.rename(oldLocation, newLocation, { overwrite: true });
} catch (error) {
if (error instanceof FileSystemError) {
console.error('Error moving location:', error.message);
} else {
throw error;
}
}
return newLocation;
}
}
export const extensionManager = new ExtensionManager();