From a7322caeb21ca306471179ce1e08683e8b11327b Mon Sep 17 00:00:00 2001 From: DavidJones Date: Wed, 21 Feb 2024 22:06:45 +0800 Subject: [PATCH] feat: default close outdate checker --- @cli/seed-cli/src/tryAction.ts | 11 +- .../src/NextDynamicClientWebpackPlugin.ts | 4 +- @next-plugin/next-auto-dynamic/src/index.ts | 4 +- @vite-plugin/vite-plugin-seed/src/index.ts | 13 +- .../{plugins => }/SeedWebpackPlugin.spec.ts | 15 +- .../seed-webpack-plugin/src/index.ts | 229 +++++++++++++++++- .../src/plugins/OutdatedWebpackPlugin.ts | 46 ---- .../src/plugins/SeedWebpackPlugin.ts | 180 -------------- .../seed-webpack-plugin/src/plugins/index.ts | 2 - 9 files changed, 254 insertions(+), 250 deletions(-) rename @webpack-plugin/seed-webpack-plugin/__tests__/{plugins => }/SeedWebpackPlugin.spec.ts (74%) delete mode 100644 @webpack-plugin/seed-webpack-plugin/src/plugins/OutdatedWebpackPlugin.ts delete mode 100644 @webpack-plugin/seed-webpack-plugin/src/plugins/SeedWebpackPlugin.ts delete mode 100644 @webpack-plugin/seed-webpack-plugin/src/plugins/index.ts diff --git a/@cli/seed-cli/src/tryAction.ts b/@cli/seed-cli/src/tryAction.ts index ec764285..865612b0 100644 --- a/@cli/seed-cli/src/tryAction.ts +++ b/@cli/seed-cli/src/tryAction.ts @@ -1,9 +1,16 @@ import { fail } from '@dumlj/feature-pretty' import { yellOutdateds } from './utils/yellOutdateds' -export function tryAction Promise>(handle: A) { +const DEFAULT_CHECK_OUTDATED = process.argv.includes('--check-outdated') || false + +export interface Options { + checkOutdated?: boolean +} + +export function tryAction Promise>(handle: A, options?: Options) { return async function execute(...args: Parameters) { - await yellOutdateds() + const { checkOutdated = DEFAULT_CHECK_OUTDATED } = options || {} + checkOutdated === true && (await yellOutdateds()) try { return handle(...args) diff --git a/@next-plugin/next-auto-dynamic/src/NextDynamicClientWebpackPlugin.ts b/@next-plugin/next-auto-dynamic/src/NextDynamicClientWebpackPlugin.ts index 0ce055b3..a3bcdaa9 100644 --- a/@next-plugin/next-auto-dynamic/src/NextDynamicClientWebpackPlugin.ts +++ b/@next-plugin/next-auto-dynamic/src/NextDynamicClientWebpackPlugin.ts @@ -13,7 +13,7 @@ export const LOADER_PATH = require.resolve('./dynamicClientLoader') export interface NextDynamicClientWebpackPluginOptions extends SeedWebpackPluginOptions { /** 目标文件夹 */ src: string | string[] - /** 默认值为 .dynamic */ + /** 默认值为 .dynamic-client-virtual */ suffix?: string /** 包含文件 */ include?: string | string[] @@ -67,7 +67,7 @@ export class NextDynamicClientWebpackPlugin extends SeedWebpackPlugin { } protected applyVirtualModules(compiler: Compiler) { - const fs = this.utilizeFSByCompiler(compiler) + const fs = this.utilizeFSB(compiler) const VM = new VirtualModulesPlugin() VM.apply(compiler) diff --git a/@next-plugin/next-auto-dynamic/src/index.ts b/@next-plugin/next-auto-dynamic/src/index.ts index e0224ada..ce451caf 100644 --- a/@next-plugin/next-auto-dynamic/src/index.ts +++ b/@next-plugin/next-auto-dynamic/src/index.ts @@ -4,10 +4,10 @@ import { NextDynamicClientWebpackPlugin, type NextDynamicClientWebpackPluginOpti export function nextDynamicClient(options?: NextDynamicClientWebpackPluginOptions) { return function withDynamicClient(nextConfig: NextConfig) { const noConflit = nextConfig.webpack - nextConfig.webpack = (config, nextOptions) => { + nextConfig.webpack = (config, context) => { config.plugins.push(new NextDynamicClientWebpackPlugin(options)) if (typeof noConflit === 'function') { - return noConflit(config, nextOptions) + return noConflit(config, context) } return config diff --git a/@vite-plugin/vite-plugin-seed/src/index.ts b/@vite-plugin/vite-plugin-seed/src/index.ts index 7864c8c5..be15f16c 100644 --- a/@vite-plugin/vite-plugin-seed/src/index.ts +++ b/@vite-plugin/vite-plugin-seed/src/index.ts @@ -18,9 +18,17 @@ globalThis.hasChecked = false /** 名称 */ export const PLUGIN_NAME = 'dumlj-vite-plugin' +const DEFAULT_CHECK_OUTDATED = !!process.env.DUMLJ_CHECK_OUTDATED || false + +export interface Options { + /** 是否检测过期 */ + checkOutdatd?: boolean +} + /** 基础插件 */ export const vitePlugin = connect( - createVitePlugin(PLUGIN_NAME, () => ({ helper }) => { + createVitePlugin(PLUGIN_NAME, (options?: Options) => ({ helper }) => { + const { checkOutdatd = DEFAULT_CHECK_OUTDATED } = options || {} const { notifications } = helper /** 检测版本过期 */ @@ -52,7 +60,8 @@ export const vitePlugin = connect( return { async buildEnd() { - await Promise.all([checkOutdated.call(this), applyNotify.call(this)]) + checkOutdatd && (await checkOutdated.call(this)) + await applyNotify.call(this) }, } }) diff --git a/@webpack-plugin/seed-webpack-plugin/__tests__/plugins/SeedWebpackPlugin.spec.ts b/@webpack-plugin/seed-webpack-plugin/__tests__/SeedWebpackPlugin.spec.ts similarity index 74% rename from @webpack-plugin/seed-webpack-plugin/__tests__/plugins/SeedWebpackPlugin.spec.ts rename to @webpack-plugin/seed-webpack-plugin/__tests__/SeedWebpackPlugin.spec.ts index 7d7c89fe..c3c1a1de 100644 --- a/@webpack-plugin/seed-webpack-plugin/__tests__/plugins/SeedWebpackPlugin.spec.ts +++ b/@webpack-plugin/seed-webpack-plugin/__tests__/SeedWebpackPlugin.spec.ts @@ -1,9 +1,8 @@ -import { OutdatedWebpackPlugin } from '@/plugins/OutdatedWebpackPlugin' -import { SeedWebpackPlugin } from '@/plugins/SeedWebpackPlugin' +import { SeedWebpackPlugin } from '@/index' import { mockWebpack } from '@dumlj/mock-lib' import type { Compiler } from 'webpack' -describe('test plugins/SeedWebpackPlugin', () => { +describe('test SeedWebpackPlugin', () => { const env = process.env beforeEach(() => { process.env.CI = undefined @@ -27,16 +26,6 @@ describe('test plugins/SeedWebpackPlugin', () => { expect(instance).toHaveProperty('messages') }) - it('is will push outdate plugin.', async () => { - /** - * process.env.CI must be equal undefined - * OutdatedWebpackPlugin can not push to compiler.options.plugins in CI env. - */ - expect(process.env.CI).toBeUndefined() - const { compiler } = await webpack({ plugins: [new SeedWebpackPlugin()] }) - expect(compiler.options.plugins.find((plugin) => plugin instanceof OutdatedWebpackPlugin)).not.toBeUndefined() - }) - it('can be inherited by custom plugin.', async () => { class CustomWebpackPlugin extends SeedWebpackPlugin { static PLUGIN_NAME = 'custom-webpack-plugin' diff --git a/@webpack-plugin/seed-webpack-plugin/src/index.ts b/@webpack-plugin/seed-webpack-plugin/src/index.ts index f9c03499..e5331c8d 100644 --- a/@webpack-plugin/seed-webpack-plugin/src/index.ts +++ b/@webpack-plugin/seed-webpack-plugin/src/index.ts @@ -1 +1,228 @@ -export * from './plugins' +import { findOutdateds } from '@dumlj/feature-updater' +import { promisify } from 'util' +import chalk from 'chalk' +import type { Compiler } from 'webpack' +import type { Class } from 'utility-types' + +const DEFAULT_CHECK_OUTDATED = !!process.env.DUMLJ_CHECK_OUTDATED || false + +/** 基础插件配置项 */ +export interface SeedWebpackPluginOptions { + /** 打印详细信息 */ + verbose?: boolean + /** 过滤所有打印信息 */ + silence?: boolean + /** 是否检测过期 */ + checkOutdatd?: boolean +} + +/** 基础插件 */ +export class SeedWebpackPlugin

{ + /** + * 插件名称 + * @description + * 继承时请保证名称不统一 + */ + static PLUGIN_NAME = 'seed-webpack-plugin' + + public options?: P + public verbose: boolean + public silence: boolean + public checkOutdatd?: boolean + + /** 通知信息 */ + protected messages: Array<{ type: 'info' | 'warn' | 'error'; message: string }> + /** 记录 */ + protected logger!: ReturnType + + /** + * 获取当前插件名称 + * @description + * 该方法可以获得继承后的插件名称,可通过`PLUGIN_NAME`静态属性设置 + */ + public get pluginName() { + const { PLUGIN_NAME = 'anonymous-plugin' } = Object.getPrototypeOf(this)?.constructor + return PLUGIN_NAME + } + + constructor(options?: P) { + this.options = options + this.verbose = typeof options?.verbose === 'boolean' ? options.verbose : false + this.silence = typeof options?.silence === 'boolean' ? options.silence : false + this.checkOutdatd = typeof options?.checkOutdatd === 'boolean' ? options.checkOutdatd : DEFAULT_CHECK_OUTDATED + this.messages = [] + } + + public apply(compiler: Compiler) { + this.applyNotify(compiler) + + if (this.checkOutdatd === true) { + this.applyOutdated(compiler) + } + } + + /** + * 注册 Logger + * @description + * 注册的 logger 与 notify 关联, + * 结束的时候统一打印日志,这样可以比较好输出。 + */ + public applyNotify(compiler: Compiler) { + const name = this.pluginName.replace('-plugin', '') + /** webpack 自带的 logger */ + this.logger = compiler.getInfrastructureLogger(name) + + compiler.hooks.afterEmit.tap(this.pluginName, () => { + if (Array.isArray(this.messages) && this.messages.length > 0) { + this.messages.forEach(({ type, message }) => { + if (this.logger && 'type' in this.logger && typeof this.logger[type] === 'function') { + this.logger[type](message) + } + }) + } + }) + } + + /** + * 注册"过期通知"能力 + * @description + * 因为迭代原因,插件更新需要通知。 + * 这里整合 OutdatedWebpackPlugin 来完成更新提醒。 + */ + public applyOutdated(compiler: Compiler) { + const index = compiler.options.plugins.findIndex((plugin) => { + if (plugin && 'pluginName' in plugin && typeof plugin.pluginName === 'string') { + return plugin.pluginName === OutdatedWebpackPlugin.PLUGIN_NAME + } + }) + + if (-1 === index && process.env.NODE_ENV !== 'production' && !process.env.CI) { + compiler.options.plugins.push(new OutdatedWebpackPlugin()) + } + } + + /** + * 传入数据不完整跳过 + * @description + * 辅助函数。 + * 用于部分传入配置不完整时跳过某些逻辑。 + */ + protected isSkipIncomplete(title: string, variables: Record) { + const names = Object.keys(variables) + const invalids = names.filter((name) => { + const value = variables[name] + return typeof value === 'string' && value.length > 0 + }) + + if (invalids.length > 0) { + this.notify('warn', `${title}\nThe following options are missing: ${['', ...invalids].join('\n - ')}`) + return true + } + + return false + } + + /** + * 通知 + * @description + * 因为 webpack 具有很多种时机, + * 因此这里只会将信息预先保存起来, + * 待到指定时机时可手动输出。 + */ + protected notify(type: 'info' | 'warn' | 'error', message: string) { + this.messages.push({ type, message }) + } + + /** 工具化 fs */ + protected utilizeFSB(compiler: Compiler) { + const { inputFileSystem: inputFS, outputFileSystem: outputFS } = compiler + const pathExists = async (file: string) => { + const promisifyStat = promisify(inputFS.stat) + await promisifyStat(file).catch((error) => { + if ('code' in error && error.code === 'ENOENT') { + return false + } + + return Promise.reject(error) + }) + + return true + } + + const promisifyReaddir = promisify(inputFS.readdir.bind(inputFS)) + const readdir = async (dir: string) => { + const files = await promisifyReaddir(dir) + if (!Array.isArray(files)) { + return [] + } + + return files.filter((file): file is string => typeof file === 'string') + } + + const readFile = promisify(inputFS.readFile.bind(inputFS)) + const writeFile = promisify(outputFS.writeFile.bind(outputFS)) + + return { pathExists, readdir, readFile, writeFile } + } + + /** + * 插件能力使用 + * @param Plugin 必须为继承 `SeedWebpackPlugin` 的插件类 + * @description + * 很多时候我们并不是简单继承某个插件的功能, + * 而是通过合并不同的插件功能来达到新的效果。 + * 该方法主要帮助我们整合插件,暂时只是名称上整合; + * 若每个功能都拥有自己的名字则无法很好找到对应插件的信息。 + */ + protected use> & { PLUGIN_NAME: string }>(Plugin: T) { + const { pluginName } = this + + const OverridePlugin = class OverridePlugin extends Plugin { + static PLUGIN_NAME = pluginName + } + + return OverridePlugin + } +} + +/** + * 版本过期通知插件 + * @description + * 与 SeedWebpackPlugin 相互引用 + * - OutdatedWebpackPlugin 继承 SeedWebpackPlugin + * - SeedWebpackPlugin 在执行时才会调用 OutdatedWebpackPlugin + */ +export class OutdatedWebpackPlugin extends SeedWebpackPlugin { + static PLUGIN_NAME = 'outdated-webpack-plugin' + + /** 是否已经检测过 */ + protected hasChecked?: boolean + + /** 版本过期警告 */ + public async yellOutdateds() { + // 检查过就不在检查 + if (this.hasChecked) { + return + } + + // 有缘提示无缘略过 + this.hasChecked = true + + const outdates = await findOutdateds() + outdates.forEach(({ name, updateType, version, latestVersion }) => { + const message = [`${chalk.bold(name)}@${chalk.bold(version)} has a new ${chalk.bold(updateType)} version,`, `please update to ${chalk.bold(latestVersion)}.`] + this.logger.warn(...message) + }) + } + + public async apply(compiler: Compiler) { + super.applyNotify(compiler) + + // 结束时提示 + compiler.hooks.afterDone.tap(this.pluginName, () => { + this.yellOutdateds().catch((error) => { + this.logger.error(error) + }) + }) + } +} diff --git a/@webpack-plugin/seed-webpack-plugin/src/plugins/OutdatedWebpackPlugin.ts b/@webpack-plugin/seed-webpack-plugin/src/plugins/OutdatedWebpackPlugin.ts deleted file mode 100644 index 30e40794..00000000 --- a/@webpack-plugin/seed-webpack-plugin/src/plugins/OutdatedWebpackPlugin.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { findOutdateds } from '@dumlj/feature-updater' -import chalk from 'chalk' -import type { Compiler } from 'webpack' -import { SeedWebpackPlugin } from './SeedWebpackPlugin' - -/** - * 版本过期通知插件 - * @description - * 与 SeedWebpackPlugin 相互引用 - * - OutdatedWebpackPlugin 继承 SeedWebpackPlugin - * - SeedWebpackPlugin 在执行时才会调用 OutdatedWebpackPlugin - */ -export class OutdatedWebpackPlugin extends SeedWebpackPlugin { - static PLUGIN_NAME = 'outdated-webpack-plugin' - - /** 是否已经检测过 */ - protected hasChecked?: boolean - - /** 版本过期警告 */ - public async yellOutdateds() { - // 检查过就不在检查 - if (this.hasChecked) { - return - } - - // 有缘提示无缘略过 - this.hasChecked = true - - const outdates = await findOutdateds() - outdates.forEach(({ name, updateType, version, latestVersion }) => { - const message = [`${chalk.bold(name)}@${chalk.bold(version)} has a new ${chalk.bold(updateType)} version,`, `please update to ${chalk.bold(latestVersion)}.`] - this.logger.warn(...message) - }) - } - - public async apply(compiler: Compiler) { - super.applyNotify(compiler) - - // 结束时提示 - compiler.hooks.afterDone.tap(this.pluginName, () => { - this.yellOutdateds().catch((error) => { - this.logger.error(error) - }) - }) - } -} diff --git a/@webpack-plugin/seed-webpack-plugin/src/plugins/SeedWebpackPlugin.ts b/@webpack-plugin/seed-webpack-plugin/src/plugins/SeedWebpackPlugin.ts deleted file mode 100644 index 1e3ccc9f..00000000 --- a/@webpack-plugin/seed-webpack-plugin/src/plugins/SeedWebpackPlugin.ts +++ /dev/null @@ -1,180 +0,0 @@ -import { promisify } from 'util' -import type { Compiler } from 'webpack' -import type { Class } from 'utility-types' - -/** 基础插件配置项 */ -export interface SeedWebpackPluginOptions { - /** 打印详细信息 */ - verbose?: boolean - /** 过滤所有打印信息 */ - silence?: boolean -} - -/** 基础插件 */ -export class SeedWebpackPlugin

{ - /** - * 插件名称 - * @description - * 继承时请保证名称不统一 - */ - static PLUGIN_NAME = 'seed-webpack-plugin' - - /** 传入配置项 */ - public options?: P - /** 打印详细信息 */ - public verbose: boolean - /** 过滤所有打印信息 */ - public silence: boolean - /** 通知信息 */ - protected messages: Array<{ type: 'info' | 'warn' | 'error'; message: string }> - /** 记录 */ - protected logger!: ReturnType - - /** - * 获取当前插件名称 - * @description - * 该方法可以获得继承后的插件名称,可通过`PLUGIN_NAME`静态属性设置 - */ - public get pluginName() { - const { PLUGIN_NAME = 'anonymous-plugin' } = Object.getPrototypeOf(this)?.constructor - return PLUGIN_NAME - } - - constructor(options?: P) { - this.options = options - this.verbose = typeof options?.verbose === 'boolean' ? options.verbose : false - this.silence = typeof options?.silence === 'boolean' ? options.silence : false - this.messages = [] - } - - /** - * 传入数据不完整跳过 - * @description - * 辅助函数。 - * 用于部分传入配置不完整时跳过某些逻辑。 - */ - protected isSkipIncomplete(title: string, variables: Record) { - const names = Object.keys(variables) - const invalids = names.filter((name) => { - const value = variables[name] - return typeof value === 'string' && value.length > 0 - }) - - if (invalids.length > 0) { - this.notify('warn', `${title}\nThe following options are missing: ${['', ...invalids].join('\n - ')}`) - return true - } - - return false - } - - /** - * 通知 - * @description - * 因为 webpack 具有很多种时机, - * 因此这里只会将信息预先保存起来, - * 待到指定时机时可手动输出。 - */ - protected notify(type: 'info' | 'warn' | 'error', message: string) { - this.messages.push({ type, message }) - } - - /** 工具化 fs */ - protected utilizeFSByCompiler(compiler: Compiler) { - const { inputFileSystem: inputFS, outputFileSystem: outputFS } = compiler - const pathExists = async (file: string) => { - const promisifyStat = promisify(inputFS.stat) - await promisifyStat(file).catch((error) => { - if ('code' in error && error.code === 'ENOENT') { - return false - } - - return Promise.reject(error) - }) - - return true - } - - const promisifyReaddir = promisify(inputFS.readdir.bind(inputFS)) - const readdir = async (dir: string) => { - const files = await promisifyReaddir(dir) - if (!Array.isArray(files)) { - return [] - } - - return files.filter((file): file is string => typeof file === 'string') - } - - const readFile = promisify(inputFS.readFile.bind(inputFS)) - const writeFile = promisify(outputFS.writeFile.bind(outputFS)) - - return { pathExists, readdir, readFile, writeFile } - } - - /** - * 插件能力使用 - * @param Plugin 必须为继承 `SeedWebpackPlugin` 的插件类 - * @description - * 很多时候我们并不是简单继承某个插件的功能, - * 而是通过合并不同的插件功能来达到新的效果。 - * 该方法主要帮助我们整合插件,暂时只是名称上整合; - * 若每个功能都拥有自己的名字则无法很好找到对应插件的信息。 - */ - protected use> & { PLUGIN_NAME: string }>(Plugin: T) { - const { pluginName } = this - - const OverridePlugin = class OverridePlugin extends Plugin { - static PLUGIN_NAME = pluginName - } - - return OverridePlugin - } - - /** - * 注册 Logger - * @description - * 注册的 logger 与 notify 关联, - * 结束的时候统一打印日志,这样可以比较好输出。 - */ - public applyNotify(compiler: Compiler) { - const name = this.pluginName.replace('-plugin', '') - /** webpack 自带的 logger */ - this.logger = compiler.getInfrastructureLogger(name) - - compiler.hooks.afterEmit.tap(this.pluginName, () => { - if (Array.isArray(this.messages) && this.messages.length > 0) { - this.messages.forEach(({ type, message }) => { - if (this.logger && 'type' in this.logger && typeof this.logger[type] === 'function') { - this.logger[type](message) - } - }) - } - }) - } - - /** - * 注册"过期通知"能力 - * @description - * 因为迭代原因,插件更新需要通知。 - * 这里整合 OutdatedWebpackPlugin 来完成更新提醒。 - */ - public applyOutdated(compiler: Compiler) { - // eslint-disable-next-line @typescript-eslint/no-var-requires - const { OutdatedWebpackPlugin } = require('./OutdatedWebpackPlugin') - const index = compiler.options.plugins.findIndex((plugin) => { - if (plugin && 'pluginName' in plugin) { - return plugin['pluginName'] === OutdatedWebpackPlugin.pluginName - } - }) - - if (-1 === index && process.env.NODE_ENV !== 'production' && !process.env.CI) { - compiler.options.plugins.push(new OutdatedWebpackPlugin()) - } - } - - /** 注册 */ - public apply(compiler: Compiler) { - this.applyNotify(compiler) - this.applyOutdated(compiler) - } -} diff --git a/@webpack-plugin/seed-webpack-plugin/src/plugins/index.ts b/@webpack-plugin/seed-webpack-plugin/src/plugins/index.ts deleted file mode 100644 index 8e1ff1ef..00000000 --- a/@webpack-plugin/seed-webpack-plugin/src/plugins/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './OutdatedWebpackPlugin' -export * from './SeedWebpackPlugin'