Skip to content

Commit

Permalink
feat: default close outdate checker
Browse files Browse the repository at this point in the history
  • Loading branch information
DavidKk committed Feb 21, 2024
1 parent 9b89b81 commit a7322ca
Show file tree
Hide file tree
Showing 9 changed files with 254 additions and 250 deletions.
11 changes: 9 additions & 2 deletions @cli/seed-cli/src/tryAction.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
import { fail } from '@dumlj/feature-pretty'
import { yellOutdateds } from './utils/yellOutdateds'

export function tryAction<A extends (...args: any[]) => Promise<any>>(handle: A) {
const DEFAULT_CHECK_OUTDATED = process.argv.includes('--check-outdated') || false

export interface Options {
checkOutdated?: boolean
}

export function tryAction<A extends (...args: any[]) => Promise<any>>(handle: A, options?: Options) {
return async function execute(...args: Parameters<A>) {
await yellOutdateds()
const { checkOutdated = DEFAULT_CHECK_OUTDATED } = options || {}
checkOutdated === true && (await yellOutdateds())

try {
return handle(...args)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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[]
Expand Down Expand Up @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions @next-plugin/next-auto-dynamic/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
13 changes: 11 additions & 2 deletions @vite-plugin/vite-plugin-seed/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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

/** 检测版本过期 */
Expand Down Expand Up @@ -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)
},
}
})
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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'
Expand Down
229 changes: 228 additions & 1 deletion @webpack-plugin/seed-webpack-plugin/src/index.ts
Original file line number Diff line number Diff line change
@@ -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<P extends SeedWebpackPluginOptions = SeedWebpackPluginOptions> {
/**
* 插件名称
* @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<Compiler['getInfrastructureLogger']>

/**
* 获取当前插件名称
* @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<string, string | undefined>) {
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<T extends Class<Pick<SeedWebpackPlugin, 'apply'>> & { 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)
})
})
}
}

This file was deleted.

Loading

0 comments on commit a7322ca

Please sign in to comment.