Skip to content

Commit 65a4a3c

Browse files
authored
feat: support to modify configurations of cli (#94)
1 parent a4bf365 commit 65a4a3c

9 files changed

Lines changed: 203 additions & 132 deletions

File tree

packages/cli/src/commander.ts

Lines changed: 2 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,3 @@
1-
import program, { Option } from 'commander';
2-
import { install, uninstall, installLocalExtensions } from './extension';
3-
import { log } from './util/log';
1+
import { runCommand } from "./factory";
42

5-
program.version(`alex ${require('../package').version}`).usage('<command> [options]');
6-
7-
const extensionProgram = program
8-
.command('extension')
9-
.alias('ext')
10-
.description(
11-
`
12-
install opensumi extensions, you should config in package.json firstly, example:
13-
{
14-
"cloudideExtensions": {
15-
"publisher": "kaitian",
16-
"name": "ide-dark-theme",
17-
"version": "2.0.0"
18-
}
19-
}
20-
version can be ignored, then will use latest version under current opensumi framework version
21-
`
22-
)
23-
.action((__, command) => {
24-
if (command.args.length) {
25-
log.warn(
26-
'You provided more than one argument. if you want to install extensions, you should use install command'
27-
);
28-
return;
29-
}
30-
install().catch((err) => console.error(err));
31-
});
32-
33-
extensionProgram
34-
.command('install <extensions...>')
35-
.alias('i')
36-
.addOption(new Option('-m, --mode [type]', 'extension env mode').choices(['internal', 'public']))
37-
.description(
38-
'install single or multiple extension, eg. kaitian.ide-dark-theme, kaitian.ide-dark-theme@2.0.0'
39-
)
40-
.action((extensions: string[], options) => {
41-
install(extensions, options).catch((err) => console.error(err));
42-
});
43-
44-
extensionProgram
45-
.command('uninstall <extensions...>')
46-
.description('uninstall single or multiple extension, eg. kaitian.ide-dark-theme')
47-
.action((extensions) => {
48-
uninstall(extensions).catch((err) => console.error(err));
49-
});
50-
51-
extensionProgram
52-
.command('link <extensionDirs...>')
53-
.description('link local extension for dev')
54-
.option('-h, --host [value]', 'local extension static file service host, default: `localhost`')
55-
.action((extensionDirs, options) => {
56-
installLocalExtensions(extensionDirs, options).catch((err) => console.error(err));
57-
});
58-
59-
program.parse(process.argv);
3+
runCommand();

packages/cli/src/extension.ts

Lines changed: 30 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ import * as path from 'path';
22
import * as os from 'os';
33
import * as fse from 'fs-extra';
44
import { from, of } from 'rxjs';
5-
import { mergeMap, filter, map } from 'rxjs/operators';
5+
import { mergeMap, filter } from 'rxjs/operators';
66
import { IExtensionMode } from '@codeblitzjs/ide-common';
7-
import { EXTENSION_DIR, EXTENSION_METADATA_DIR, EXTENSION_FIELD, resolveMarketplaceConfig } from './util/constant';
7+
import { IExtensionInstallationConfig, kExtensionConfig, resolveExtensionInstallationConfig, resolveMarketplaceConfig } from './util/constant';
88
import { ExtensionInstaller, Extension } from './util/installer';
99
import { getExtension } from './extension/scanner';
1010
import {
@@ -31,21 +31,23 @@ export const install = async (
3131

3232
createInstaller();
3333

34+
const installationConfig = resolveExtensionInstallationConfig();
35+
3436
let extensions: IExtensionDesc[] = [];
3537

3638
if (extensionId?.length) {
3739
extensions = parseExtensionId(extensionId, options?.mode);
3840
shouldWriteConfig = true;
39-
await Promise.all(extensions.map((ext) => removeExtensionById(ext)));
41+
await Promise.all(extensions.map((ext) => removeExtensionById(installationConfig, ext)));
4042
} else {
4143
const extensionConfig = await getExtensionFromPackage();
4244
if (!extensionConfig.length && !options?.silent) {
43-
log.warn(`当前未配置 ${EXTENSION_FIELD} npx alex ext -h 查看帮助`);
45+
log.warn(`当前未配置 ${kExtensionConfig.extensionField} npx ${kExtensionConfig.product} ext -h 查看帮助`);
4446
return;
4547
}
4648
checkExtensionConfig(extensionConfig);
4749
extensions = extensionConfig;
48-
await removeAllExtension();
50+
await removeAllExtension(installationConfig);
4951
}
5052

5153
if (!extensions.length) return;
@@ -67,7 +69,7 @@ export const install = async (
6769
),
6870
mergeMap(([extPath, mode]) => getExtension(extPath, mode), 5),
6971
filter((data) => !!data),
70-
mergeMap(writeMetadata)
72+
mergeMap((meta) => writeMetadata(installationConfig, meta!))
7173
)
7274
.subscribe(
7375
(ext) => {
@@ -101,6 +103,8 @@ export const installLocalExtensions = async (dirs: string[], options?: IExtensio
101103
return;
102104
}
103105

106+
const installationConfig = resolveExtensionInstallationConfig();
107+
104108
const absoluteDirs = dirs.map((dir) => path.resolve(dir));
105109

106110
log.start('开始安装本地扩展\n');
@@ -119,7 +123,7 @@ export const installLocalExtensions = async (dirs: string[], options?: IExtensio
119123
.pipe(
120124
mergeMap((localExtPath) => getExtension(localExtPath, 'local', httpUri), 5),
121125
filter((data) => !!data),
122-
mergeMap(writeMetadata)
126+
mergeMap((meta) => writeMetadata(installationConfig, meta!))
123127
)
124128
.subscribe(
125129
(ext) => {
@@ -140,40 +144,40 @@ async function createInstaller() {
140144
const pkgJSON = fse.readJSONSync(path.join(__dirname, '../package.json'));
141145

142146
const marketplaceConfig = resolveMarketplaceConfig();
143-
147+
const installConfig = resolveExtensionInstallationConfig();
144148
extensionInstaller = new ExtensionInstaller({
145149
api: marketplaceConfig.endpoint,
146150
accountId: marketplaceConfig.accountId,
147151
masterKey: marketplaceConfig.masterKey,
148152
frameworkVersion: pkgJSON.engines.opensumi,
149-
dist: EXTENSION_DIR,
153+
dist: installConfig.extensionDir,
150154
ignoreIncreaseCount: true,
151155
retry: 3, // 失败重试
152156
});
153157
}
154158

155-
async function removeAllExtension() {
156-
await fse.remove(EXTENSION_DIR);
157-
await fse.remove(EXTENSION_METADATA_DIR);
158-
await fse.ensureDir(EXTENSION_DIR);
159-
await fse.ensureDir(EXTENSION_METADATA_DIR);
159+
async function removeAllExtension(installationConfig: IExtensionInstallationConfig) {
160+
await fse.remove(installationConfig.extensionDir);
161+
await fse.remove(installationConfig.extensionMetadataDir);
162+
await fse.ensureDir(installationConfig.extensionDir);
163+
await fse.ensureDir(installationConfig.extensionMetadataDir);
160164
}
161165

162-
async function removeExtensionById(ext: IExtensionDesc) {
166+
async function removeExtensionById(installationConfig: IExtensionInstallationConfig, ext: IExtensionDesc) {
163167
const extensionId = `${ext.publisher}.${ext.name}`;
164168
return Promise.all([
165169
await fse.remove(
166-
path.join(`${EXTENSION_DIR}`, `${extensionId}${ext.version ? `-${ext.version}` : ''}`)
170+
path.join(`${installationConfig.extensionDir}`, `${extensionId}${ext.version ? `-${ext.version}` : ''}`)
167171
),
168-
await fse.remove(path.join(EXTENSION_METADATA_DIR, `${extensionId}.js`)),
169-
await fse.remove(path.join(EXTENSION_METADATA_DIR, `${extensionId}.d.ts`)),
172+
await fse.remove(path.join(installationConfig.extensionMetadataDir, `${extensionId}.js`)),
173+
await fse.remove(path.join(installationConfig.extensionMetadataDir, `${extensionId}.d.ts`)),
170174
]);
171175
}
172176

173177
async function getExtensionFromPackage(): Promise<IExtensionDesc[]> {
174178
try {
175179
const projectPkgJSON = await fse.readJSON(resolveCWDPkgJSON());
176-
return projectPkgJSON?.[EXTENSION_FIELD] ?? [];
180+
return projectPkgJSON?.[kExtensionConfig.extensionField] ?? [];
177181
} catch (err) {
178182
return [];
179183
}
@@ -183,7 +187,7 @@ async function setExtensionFromPackage(config: any) {
183187
try {
184188
const pkgPath = resolveCWDPkgJSON();
185189
const projectPkgJSON = await fse.readJSON(pkgPath);
186-
projectPkgJSON[EXTENSION_FIELD] = config;
190+
projectPkgJSON[kExtensionConfig.extensionField] = config;
187191
await fse.writeJSON(pkgPath, projectPkgJSON, { spaces: 2 });
188192
} catch (err) {}
189193
}
@@ -233,13 +237,13 @@ async function installExtension(extension: IExtensionDesc) {
233237
return [extensionPath, extension.mode] as const;
234238
}
235239

236-
async function writeMetadata(metadata: IExtensionBasicMetadata) {
237-
await fse.ensureDir(EXTENSION_METADATA_DIR);
240+
async function writeMetadata(installationConfig: IExtensionInstallationConfig, metadata: IExtensionBasicMetadata) {
241+
await fse.ensureDir(installationConfig.extensionMetadataDir);
238242

239243
const { extension } = metadata;
240244
const extensionId = `${extension.publisher}.${extension.name}`;
241245
await fse.writeFile(
242-
path.join(EXTENSION_METADATA_DIR, `${extensionId}.js`),
246+
path.join(installationConfig.extensionMetadataDir, `${extensionId}.js`),
243247
`
244248
module.exports = ${JSON.stringify(metadata, null, 2)}
245249
`.trim() + '\n'
@@ -266,6 +270,8 @@ async function modifyPkgJSON(extensions: IExtensionIdentity[]) {
266270

267271
// uninstall
268272
export async function uninstall(extensionId: string[]) {
273+
const installationConfig = resolveExtensionInstallationConfig();
274+
269275
const extensions = await getExtensionFromPackage();
270276
const removeExtensions: IExtensionDesc[] = [];
271277
const remainExtensions: IExtensionDesc[] = [];
@@ -290,7 +296,7 @@ export async function uninstall(extensionId: string[]) {
290296
throw new Error('error');
291297
});
292298
}
293-
await Promise.all(removeExtensions.map((ext) => removeExtensionById(ext)));
299+
await Promise.all(removeExtensions.map((ext) => removeExtensionById(installationConfig, ext)));
294300
await setExtensionFromPackage(remainExtensions);
295301

296302
log.success('卸载扩展成功');

packages/cli/src/extension/metadata-type.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import * as fse from 'fs-extra';
22
import * as path from 'path';
3-
import { EXTENSION_METADATA_DIR } from '../util/constant';
3+
import { IExtensionInstallationConfig, resolveExtensionInstallationConfig } from '../util/constant';
44

55
const referenceFile = '_reference.d.ts';
66

7-
async function createReference() {
8-
const refPath = path.join(EXTENSION_METADATA_DIR, referenceFile);
7+
async function createReference(config: IExtensionInstallationConfig) {
8+
const refPath = path.join(config.extensionMetadataDir, referenceFile);
99
if (!(await fse.pathExists(refPath))) {
1010
await fse.writeFile(
1111
refPath,
@@ -19,11 +19,12 @@ export { metadata }
1919
}
2020

2121
export async function createMetadataType(extensionId: string) {
22+
const config = resolveExtensionInstallationConfig();
2223
const content =
2324
`
2425
import { metadata } from './_reference';
2526
export = metadata;
2627
`.trim() + '\n';
27-
await createReference();
28-
return fse.writeFile(path.join(EXTENSION_METADATA_DIR, `${extensionId}.d.ts`), content);
28+
await createReference(config);
29+
return fse.writeFile(path.join(config.extensionMetadataDir, `${extensionId}.d.ts`), content);
2930
}

packages/cli/src/factory.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import program, { Option } from 'commander';
2+
import { install, uninstall, installLocalExtensions } from './extension';
3+
import { init, log } from './util/log';
4+
import { kExtensionConfig } from './util/constant'
5+
6+
export const runCommand = () => {
7+
init();
8+
program.version(`${kExtensionConfig.product} ${require('../package').version}`).usage('<command> [options]');
9+
10+
const extensionProgram = program
11+
.command('extension')
12+
.alias('ext')
13+
.description(
14+
`
15+
install opensumi extensions, you should config in package.json firstly, example:
16+
{
17+
"cloudideExtensions": {
18+
"publisher": "kaitian",
19+
"name": "ide-dark-theme",
20+
"version": "2.0.0"
21+
}
22+
}
23+
version can be ignored, then will use latest version under current opensumi framework version
24+
`
25+
)
26+
.action((__, command) => {
27+
if (command.args.length) {
28+
log.warn(
29+
'You provided more than one argument. if you want to install extensions, you should use install command'
30+
);
31+
return;
32+
}
33+
install().catch((err) => console.error(err));
34+
});
35+
36+
extensionProgram
37+
.command('install <extensions...>')
38+
.alias('i')
39+
.addOption(new Option('-m, --mode [type]', 'extension env mode').choices(['internal', 'public']))
40+
.description(
41+
'install single or multiple extension, eg. kaitian.ide-dark-theme, kaitian.ide-dark-theme@2.0.0'
42+
)
43+
.action((extensions: string[], options) => {
44+
install(extensions, options).catch((err) => console.error(err));
45+
});
46+
47+
extensionProgram
48+
.command('uninstall <extensions...>')
49+
.description('uninstall single or multiple extension, eg. kaitian.ide-dark-theme')
50+
.action((extensions) => {
51+
uninstall(extensions).catch((err) => console.error(err));
52+
});
53+
54+
extensionProgram
55+
.command('link <extensionDirs...>')
56+
.description('link local extension for dev')
57+
.option('-h, --host [value]', 'local extension static file service host, default: `localhost`')
58+
.action((extensionDirs, options) => {
59+
installLocalExtensions(extensionDirs, options).catch((err) => console.error(err));
60+
});
61+
62+
program.parse(process.argv);
63+
}
Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
import { FRAMEWORK_PATH, FRAMEWORK_NAME } from './constant';
1+
import { kExtensionConfig, resolveFrameworkPath } from './constant';
22
import { log } from './log';
33

44
export default function checkFramework() {
5-
if (!FRAMEWORK_PATH) {
6-
log.error(`cli 无法单独使用,请直接安装 ${FRAMEWORK_NAME}`);
5+
const frameworkPath = resolveFrameworkPath();
6+
if (!frameworkPath) {
7+
log.error(`cli 无法单独使用,需要与 ${kExtensionConfig.frameworkPackageName} 一起安装使用`);
78
throw new Error('error');
89
}
910
}

0 commit comments

Comments
 (0)