Skip to content

Commit a7322ca

Browse files
committed
feat: default close outdate checker
1 parent 9b89b81 commit a7322ca

File tree

9 files changed

+254
-250
lines changed

9 files changed

+254
-250
lines changed

@cli/seed-cli/src/tryAction.ts

+9-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
11
import { fail } from '@dumlj/feature-pretty'
22
import { yellOutdateds } from './utils/yellOutdateds'
33

4-
export function tryAction<A extends (...args: any[]) => Promise<any>>(handle: A) {
4+
const DEFAULT_CHECK_OUTDATED = process.argv.includes('--check-outdated') || false
5+
6+
export interface Options {
7+
checkOutdated?: boolean
8+
}
9+
10+
export function tryAction<A extends (...args: any[]) => Promise<any>>(handle: A, options?: Options) {
511
return async function execute(...args: Parameters<A>) {
6-
await yellOutdateds()
12+
const { checkOutdated = DEFAULT_CHECK_OUTDATED } = options || {}
13+
checkOutdated === true && (await yellOutdateds())
714

815
try {
916
return handle(...args)

@next-plugin/next-auto-dynamic/src/NextDynamicClientWebpackPlugin.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export const LOADER_PATH = require.resolve('./dynamicClientLoader')
1313
export interface NextDynamicClientWebpackPluginOptions extends SeedWebpackPluginOptions {
1414
/** 目标文件夹 */
1515
src: string | string[]
16-
/** 默认值为 .dynamic */
16+
/** 默认值为 .dynamic-client-virtual */
1717
suffix?: string
1818
/** 包含文件 */
1919
include?: string | string[]
@@ -67,7 +67,7 @@ export class NextDynamicClientWebpackPlugin extends SeedWebpackPlugin {
6767
}
6868

6969
protected applyVirtualModules(compiler: Compiler) {
70-
const fs = this.utilizeFSByCompiler(compiler)
70+
const fs = this.utilizeFSB(compiler)
7171

7272
const VM = new VirtualModulesPlugin()
7373
VM.apply(compiler)

@next-plugin/next-auto-dynamic/src/index.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ import { NextDynamicClientWebpackPlugin, type NextDynamicClientWebpackPluginOpti
44
export function nextDynamicClient(options?: NextDynamicClientWebpackPluginOptions) {
55
return function withDynamicClient(nextConfig: NextConfig) {
66
const noConflit = nextConfig.webpack
7-
nextConfig.webpack = (config, nextOptions) => {
7+
nextConfig.webpack = (config, context) => {
88
config.plugins.push(new NextDynamicClientWebpackPlugin(options))
99
if (typeof noConflit === 'function') {
10-
return noConflit(config, nextOptions)
10+
return noConflit(config, context)
1111
}
1212

1313
return config

@vite-plugin/vite-plugin-seed/src/index.ts

+11-2
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,17 @@ globalThis.hasChecked = false
1818
/** 名称 */
1919
export const PLUGIN_NAME = 'dumlj-vite-plugin'
2020

21+
const DEFAULT_CHECK_OUTDATED = !!process.env.DUMLJ_CHECK_OUTDATED || false
22+
23+
export interface Options {
24+
/** 是否检测过期 */
25+
checkOutdatd?: boolean
26+
}
27+
2128
/** 基础插件 */
2229
export const vitePlugin = connect(
23-
createVitePlugin(PLUGIN_NAME, () => ({ helper }) => {
30+
createVitePlugin(PLUGIN_NAME, (options?: Options) => ({ helper }) => {
31+
const { checkOutdatd = DEFAULT_CHECK_OUTDATED } = options || {}
2432
const { notifications } = helper
2533

2634
/** 检测版本过期 */
@@ -52,7 +60,8 @@ export const vitePlugin = connect(
5260

5361
return {
5462
async buildEnd() {
55-
await Promise.all([checkOutdated.call(this), applyNotify.call(this)])
63+
checkOutdatd && (await checkOutdated.call(this))
64+
await applyNotify.call(this)
5665
},
5766
}
5867
})

@webpack-plugin/seed-webpack-plugin/__tests__/plugins/SeedWebpackPlugin.spec.ts @webpack-plugin/seed-webpack-plugin/__tests__/SeedWebpackPlugin.spec.ts

+2-13
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
import { OutdatedWebpackPlugin } from '@/plugins/OutdatedWebpackPlugin'
2-
import { SeedWebpackPlugin } from '@/plugins/SeedWebpackPlugin'
1+
import { SeedWebpackPlugin } from '@/index'
32
import { mockWebpack } from '@dumlj/mock-lib'
43
import type { Compiler } from 'webpack'
54

6-
describe('test plugins/SeedWebpackPlugin', () => {
5+
describe('test SeedWebpackPlugin', () => {
76
const env = process.env
87
beforeEach(() => {
98
process.env.CI = undefined
@@ -27,16 +26,6 @@ describe('test plugins/SeedWebpackPlugin', () => {
2726
expect(instance).toHaveProperty('messages')
2827
})
2928

30-
it('is will push outdate plugin.', async () => {
31-
/**
32-
* process.env.CI must be equal undefined
33-
* OutdatedWebpackPlugin can not push to compiler.options.plugins in CI env.
34-
*/
35-
expect(process.env.CI).toBeUndefined()
36-
const { compiler } = await webpack({ plugins: [new SeedWebpackPlugin()] })
37-
expect(compiler.options.plugins.find((plugin) => plugin instanceof OutdatedWebpackPlugin)).not.toBeUndefined()
38-
})
39-
4029
it('can be inherited by custom plugin.', async () => {
4130
class CustomWebpackPlugin extends SeedWebpackPlugin {
4231
static PLUGIN_NAME = 'custom-webpack-plugin'
Original file line numberDiff line numberDiff line change
@@ -1 +1,228 @@
1-
export * from './plugins'
1+
import { findOutdateds } from '@dumlj/feature-updater'
2+
import { promisify } from 'util'
3+
import chalk from 'chalk'
4+
import type { Compiler } from 'webpack'
5+
import type { Class } from 'utility-types'
6+
7+
const DEFAULT_CHECK_OUTDATED = !!process.env.DUMLJ_CHECK_OUTDATED || false
8+
9+
/** 基础插件配置项 */
10+
export interface SeedWebpackPluginOptions {
11+
/** 打印详细信息 */
12+
verbose?: boolean
13+
/** 过滤所有打印信息 */
14+
silence?: boolean
15+
/** 是否检测过期 */
16+
checkOutdatd?: boolean
17+
}
18+
19+
/** 基础插件 */
20+
export class SeedWebpackPlugin<P extends SeedWebpackPluginOptions = SeedWebpackPluginOptions> {
21+
/**
22+
* 插件名称
23+
* @description
24+
* 继承时请保证名称不统一
25+
*/
26+
static PLUGIN_NAME = 'seed-webpack-plugin'
27+
28+
public options?: P
29+
public verbose: boolean
30+
public silence: boolean
31+
public checkOutdatd?: boolean
32+
33+
/** 通知信息 */
34+
protected messages: Array<{ type: 'info' | 'warn' | 'error'; message: string }>
35+
/** 记录 */
36+
protected logger!: ReturnType<Compiler['getInfrastructureLogger']>
37+
38+
/**
39+
* 获取当前插件名称
40+
* @description
41+
* 该方法可以获得继承后的插件名称,可通过`PLUGIN_NAME`静态属性设置
42+
*/
43+
public get pluginName() {
44+
const { PLUGIN_NAME = 'anonymous-plugin' } = Object.getPrototypeOf(this)?.constructor
45+
return PLUGIN_NAME
46+
}
47+
48+
constructor(options?: P) {
49+
this.options = options
50+
this.verbose = typeof options?.verbose === 'boolean' ? options.verbose : false
51+
this.silence = typeof options?.silence === 'boolean' ? options.silence : false
52+
this.checkOutdatd = typeof options?.checkOutdatd === 'boolean' ? options.checkOutdatd : DEFAULT_CHECK_OUTDATED
53+
this.messages = []
54+
}
55+
56+
public apply(compiler: Compiler) {
57+
this.applyNotify(compiler)
58+
59+
if (this.checkOutdatd === true) {
60+
this.applyOutdated(compiler)
61+
}
62+
}
63+
64+
/**
65+
* 注册 Logger
66+
* @description
67+
* 注册的 logger 与 notify 关联,
68+
* 结束的时候统一打印日志,这样可以比较好输出。
69+
*/
70+
public applyNotify(compiler: Compiler) {
71+
const name = this.pluginName.replace('-plugin', '')
72+
/** webpack 自带的 logger */
73+
this.logger = compiler.getInfrastructureLogger(name)
74+
75+
compiler.hooks.afterEmit.tap(this.pluginName, () => {
76+
if (Array.isArray(this.messages) && this.messages.length > 0) {
77+
this.messages.forEach(({ type, message }) => {
78+
if (this.logger && 'type' in this.logger && typeof this.logger[type] === 'function') {
79+
this.logger[type](message)
80+
}
81+
})
82+
}
83+
})
84+
}
85+
86+
/**
87+
* 注册"过期通知"能力
88+
* @description
89+
* 因为迭代原因,插件更新需要通知。
90+
* 这里整合 OutdatedWebpackPlugin 来完成更新提醒。
91+
*/
92+
public applyOutdated(compiler: Compiler) {
93+
const index = compiler.options.plugins.findIndex((plugin) => {
94+
if (plugin && 'pluginName' in plugin && typeof plugin.pluginName === 'string') {
95+
return plugin.pluginName === OutdatedWebpackPlugin.PLUGIN_NAME
96+
}
97+
})
98+
99+
if (-1 === index && process.env.NODE_ENV !== 'production' && !process.env.CI) {
100+
compiler.options.plugins.push(new OutdatedWebpackPlugin())
101+
}
102+
}
103+
104+
/**
105+
* 传入数据不完整跳过
106+
* @description
107+
* 辅助函数。
108+
* 用于部分传入配置不完整时跳过某些逻辑。
109+
*/
110+
protected isSkipIncomplete(title: string, variables: Record<string, string | undefined>) {
111+
const names = Object.keys(variables)
112+
const invalids = names.filter((name) => {
113+
const value = variables[name]
114+
return typeof value === 'string' && value.length > 0
115+
})
116+
117+
if (invalids.length > 0) {
118+
this.notify('warn', `${title}\nThe following options are missing: ${['', ...invalids].join('\n - ')}`)
119+
return true
120+
}
121+
122+
return false
123+
}
124+
125+
/**
126+
* 通知
127+
* @description
128+
* 因为 webpack 具有很多种时机,
129+
* 因此这里只会将信息预先保存起来,
130+
* 待到指定时机时可手动输出。
131+
*/
132+
protected notify(type: 'info' | 'warn' | 'error', message: string) {
133+
this.messages.push({ type, message })
134+
}
135+
136+
/** 工具化 fs */
137+
protected utilizeFSB(compiler: Compiler) {
138+
const { inputFileSystem: inputFS, outputFileSystem: outputFS } = compiler
139+
const pathExists = async (file: string) => {
140+
const promisifyStat = promisify(inputFS.stat)
141+
await promisifyStat(file).catch((error) => {
142+
if ('code' in error && error.code === 'ENOENT') {
143+
return false
144+
}
145+
146+
return Promise.reject(error)
147+
})
148+
149+
return true
150+
}
151+
152+
const promisifyReaddir = promisify(inputFS.readdir.bind(inputFS))
153+
const readdir = async (dir: string) => {
154+
const files = await promisifyReaddir(dir)
155+
if (!Array.isArray(files)) {
156+
return []
157+
}
158+
159+
return files.filter((file): file is string => typeof file === 'string')
160+
}
161+
162+
const readFile = promisify(inputFS.readFile.bind(inputFS))
163+
const writeFile = promisify(outputFS.writeFile.bind(outputFS))
164+
165+
return { pathExists, readdir, readFile, writeFile }
166+
}
167+
168+
/**
169+
* 插件能力使用
170+
* @param Plugin 必须为继承 `SeedWebpackPlugin` 的插件类
171+
* @description
172+
* 很多时候我们并不是简单继承某个插件的功能,
173+
* 而是通过合并不同的插件功能来达到新的效果。
174+
* 该方法主要帮助我们整合插件,暂时只是名称上整合;
175+
* 若每个功能都拥有自己的名字则无法很好找到对应插件的信息。
176+
*/
177+
protected use<T extends Class<Pick<SeedWebpackPlugin, 'apply'>> & { PLUGIN_NAME: string }>(Plugin: T) {
178+
const { pluginName } = this
179+
180+
const OverridePlugin = class OverridePlugin extends Plugin {
181+
static PLUGIN_NAME = pluginName
182+
}
183+
184+
return OverridePlugin
185+
}
186+
}
187+
188+
/**
189+
* 版本过期通知插件
190+
* @description
191+
* 与 SeedWebpackPlugin 相互引用
192+
* - OutdatedWebpackPlugin 继承 SeedWebpackPlugin
193+
* - SeedWebpackPlugin 在执行时才会调用 OutdatedWebpackPlugin
194+
*/
195+
export class OutdatedWebpackPlugin extends SeedWebpackPlugin {
196+
static PLUGIN_NAME = 'outdated-webpack-plugin'
197+
198+
/** 是否已经检测过 */
199+
protected hasChecked?: boolean
200+
201+
/** 版本过期警告 */
202+
public async yellOutdateds() {
203+
// 检查过就不在检查
204+
if (this.hasChecked) {
205+
return
206+
}
207+
208+
// 有缘提示无缘略过
209+
this.hasChecked = true
210+
211+
const outdates = await findOutdateds()
212+
outdates.forEach(({ name, updateType, version, latestVersion }) => {
213+
const message = [`${chalk.bold(name)}@${chalk.bold(version)} has a new ${chalk.bold(updateType)} version,`, `please update to ${chalk.bold(latestVersion)}.`]
214+
this.logger.warn(...message)
215+
})
216+
}
217+
218+
public async apply(compiler: Compiler) {
219+
super.applyNotify(compiler)
220+
221+
// 结束时提示
222+
compiler.hooks.afterDone.tap(this.pluginName, () => {
223+
this.yellOutdateds().catch((error) => {
224+
this.logger.error(error)
225+
})
226+
})
227+
}
228+
}

@webpack-plugin/seed-webpack-plugin/src/plugins/OutdatedWebpackPlugin.ts

-46
This file was deleted.

0 commit comments

Comments
 (0)