From 836139d1cfec7d6c386bce8201c5a1ede4b1487b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=9C=E7=81=AF?= Date: Thu, 8 Feb 2024 21:31:19 +0800 Subject: [PATCH] feat: extension system (#145) --- index.js | 9 +++++- raw/gulpfile.js | 11 +++++-- src/base/io.js | 2 ++ src/executer/cli.js | 20 ++++++++++++- src/executer/ext.js | 16 ++++++++++ src/executer/rescue.js | 19 ++++++++++++ src/handlers/extension.js | 58 +++++++++++++++++++++++++++++++++++++ src/handlers/information.js | 7 ++++- src/handlers/npm.js | 20 ++++++++++++- 9 files changed, 155 insertions(+), 7 deletions(-) create mode 100644 src/executer/ext.js create mode 100644 src/executer/rescue.js create mode 100644 src/handlers/extension.js diff --git a/index.js b/index.js index 7eade16..79deff1 100755 --- a/index.js +++ b/index.js @@ -1,5 +1,12 @@ #!/usr/bin/env node import program from './src/executer/cli.js'; +import rescue from './src/executer/rescue.js'; +import ExtensionHandler from './src/handlers/extension.js'; -program.parse(process.argv); \ No newline at end of file +if (['rescue', 'r'].includes(process.argv[2])) await rescue(); +else { + ExtensionHandler.init(); + ExtensionHandler.load(process); + program.parse(process.argv); +} diff --git a/raw/gulpfile.js b/raw/gulpfile.js index e19b817..a26c0cd 100644 --- a/raw/gulpfile.js +++ b/raw/gulpfile.js @@ -1,4 +1,4 @@ -// === REFRESH TAG = ERROR +// === REFRESH TAG = ERROR // === CONFIGURABLE VARIABLES import config from './.serein.json' assert { type: 'json' }; const output = config.output; @@ -207,6 +207,11 @@ const deploy = gulp.series( const default_action = gulp.series(build, deploy); +async function extension() { + const [name, task] = [process.argv[3], process.argv[4]]; + await (await import(name))[task](config); +} + export { build, bundle, @@ -214,6 +219,6 @@ export { deploy, default_action as default, watch, - compile_scripts as cs + compile_scripts as cs, + extension }; - diff --git a/src/base/io.js b/src/base/io.js index 375bf5a..0f270aa 100644 --- a/src/base/io.js +++ b/src/base/io.js @@ -13,6 +13,8 @@ const IO = { } }, + exists: (filename) => existsSync(filename), + writeText: (filename, text) => { writeFileSync(filename, text); done('Create ' + filename); diff --git a/src/executer/cli.js b/src/executer/cli.js index 1461325..480b8bd 100644 --- a/src/executer/cli.js +++ b/src/executer/cli.js @@ -5,6 +5,7 @@ import adaptProject from './adapt.js'; import moduleManage from './module.js'; import switchVersion from './switch.js'; import { CLI_VERSION } from '../base/constants.js'; +import { install, uninstall } from './ext.js'; program .name('serein') @@ -30,7 +31,7 @@ program .alias('m') .description('managing current dependencies') .option('-y --yes', 'switch to latest version directly') - .action((option) => moduleManage()); + .action(() => moduleManage()); program .command('adapt') @@ -62,4 +63,21 @@ program .description('listen for file changes and deploy project automatically') .action(() => IO.exec('gulp watch')); +program + .command('install ') + .alias('ins') + .description('install an extension for serein') + .action((...packageName) => install(packageName)); + +program + .command('uninstall ') + .alias('unins') + .description('uninstall an extension') + .action((...packageName) => uninstall(packageName)); + +program + .command('rescue') + .alias('r') + .description('recovering a project with an exception dependency'); + export default program; diff --git a/src/executer/ext.js b/src/executer/ext.js new file mode 100644 index 0000000..f319966 --- /dev/null +++ b/src/executer/ext.js @@ -0,0 +1,16 @@ +import InfoHandler from '../handlers/information.js'; +import ExtensionHandler from '../handlers/extension.js'; + +async function install(packageName) { + InfoHandler.bind('ext'); + + await ExtensionHandler.install(packageName); +} + +async function uninstall(packageName) { + InfoHandler.bind('ext'); + + await ExtensionHandler.uninstall(packageName); +} + +export { install, uninstall }; diff --git a/src/executer/rescue.js b/src/executer/rescue.js new file mode 100644 index 0000000..e08d2cd --- /dev/null +++ b/src/executer/rescue.js @@ -0,0 +1,19 @@ +import IO from '../base/io.js'; +import NpmHandler from '../handlers/npm.js'; +import { error, start } from '../base/console.js'; + +async function rescue() { + start('project rescue...'); + if (!(IO.exists('package.json') && IO.exists('.serein.json'))) + console.log( + error( + 'Fix failed, make sure you have serein.json with package.json!' + ) + ); + else { + await NpmHandler.install(); + done('project rescue.'); + } +} + +export default rescue; diff --git a/src/handlers/extension.js b/src/handlers/extension.js new file mode 100644 index 0000000..a0b773d --- /dev/null +++ b/src/handlers/extension.js @@ -0,0 +1,58 @@ +import { done, error, start } from '../base/console.js'; +import IO from '../base/io.js'; +import NpmHandler from './npm.js'; +import InfoHandler from './information.js'; + +class ExtensionClass { + constructor() { + this.context = {}; + this.extList = []; + } + + init() { + if (IO.exists('.serein.json')) { + this.context = IO.readJSON('.serein.json'); + if (!this.context['extensions']) this.context['extension'] = []; + else this.extList = this.context['extension']; + } + } + + async install(packageNames) { + start('Install extensions...'); + + await NpmHandler.add(packageNames.join(' ')); + this.context.extension.push(...packageNames); + IO.writeJSON('.serein.json', this.context); + + done('Intstall extension.'); + } + + async uninstall(packageNames) { + start('Uninstall extensions...'); + + await NpmHandler.del(packageNames.join(' ')); + this.context.extension = this.context.extension.filter( + (v) => !packageNames.includes(v) + ); + IO.writeJSON('.serein.json', this.context); + + done('Uninstall extensions.'); + } + + async load(program) { + if (this.extList.length) { + start('Load extensions...'); + try { + for (const packageName in this.extList) + (await import(packageName)).cli((program, InfoHandler)); + } catch (e) { + console.log(error('Failed to load extension!'), e); + } + done('Load extensions.'); + } + } +} + +const ExtensionHandler = new ExtensionClass(); + +export default ExtensionHandler; diff --git a/src/handlers/information.js b/src/handlers/information.js index 98b15e0..489c13a 100644 --- a/src/handlers/information.js +++ b/src/handlers/information.js @@ -93,7 +93,12 @@ class InfoClass extends DelayHanlder { behManifestPath: 'behavior_packs/manifest.json', resManifestPath: 'resource_packs/manifest.json' }); - } else if (this.info.mode === 'switch' || this.info.mode === 'module') { + } else if ( + this.info.mode === 'switch' || + this.info.mode === 'module' || + this.info.mode === 'ext' || + this.info.mode === 'rescue' + ) { Object.assign(this.info, { mode: this.info.mode, ...(await ConfigRender.getConfig()) diff --git a/src/handlers/npm.js b/src/handlers/npm.js index 9aa7850..496ce4a 100644 --- a/src/handlers/npm.js +++ b/src/handlers/npm.js @@ -3,8 +3,8 @@ import { existsSync } from 'fs'; import { deleteSync } from 'del'; import MirrorHandler from './mirror.js'; import { ALL } from '../base/constants.js'; -import { accept } from '../base/console.js'; import DelayHanlderWithInfo from './delayInfo.js'; +import { accept, start, done } from '../base/console.js'; function checkPnpm() { try { @@ -140,6 +140,24 @@ class NpmClass extends DelayHanlderWithInfo { } else IO.exec(`npm install --registry=${this.mirror} ${android_suffix}`); } + + async add(packageName) { + await this.check(); + start(`Install extension "${packageName}"...`); + if (this.pnpm) { + IO.exec(`pnpm install ${packageName}`); + } else IO.exec(`npm install ${packageName}`); + done(`Install extension "${packageName}".`); + } + + async del(packageName) { + await this.check(); + start(`Uninstall extension "${packageName}"...`); + if (this.pnpm) { + IO.exec(`pnpm remove ${packageName}`); + } else IO.exec(`npm uninstall ${packageName}`); + done(`Uninstall extension "${packageName}".`); + } } const NpmHandler = new NpmClass();