From 40581fc972319c4b7c4ce7c642b6439fcef4c38e Mon Sep 17 00:00:00 2001 From: Jinbao1001 Date: Fri, 6 Sep 2024 14:30:16 +0800 Subject: [PATCH 1/3] fix: livedemo disabled when config babel-plugin-import --- src/features/compile/index.ts | 8 ++---- src/features/compile/makoHooks.ts | 3 +++ src/features/compile/utils.ts | 17 +++++++++++++ src/loaders/markdown/index.ts | 42 ++++++++++++++++++------------- 4 files changed, 46 insertions(+), 24 deletions(-) create mode 100644 src/features/compile/utils.ts diff --git a/src/features/compile/index.ts b/src/features/compile/index.ts index 6d74793d6..568c64492 100644 --- a/src/features/compile/index.ts +++ b/src/features/compile/index.ts @@ -7,6 +7,7 @@ import fs from 'fs'; import path from 'path'; import { addAtomMeta, addExampleAssets } from '../assets'; import { getLoadHook } from './makoHooks'; +import { shouldDisabledLiveDemo } from './utils'; export const techStacks: IDumiTechStack[] = []; export default (api: IApi) => { api.describe({ key: 'dumi:compile' }); @@ -87,12 +88,6 @@ export default (api: IApi) => { const babelInUmi = memo.module.rule('src').use('babel-loader').entries(); if (!babelInUmi) return memo; const loaderPath = require.resolve('../../loaders/markdown'); - - // support require mjs packages(eg. element-plus/es) - // memo.resolve.byDependency.set('commonjs', { - // conditionNames: ['require', 'node', 'import'], - // }); - const loaderBaseOpts: Partial = { techStacks, cwd: api.cwd, @@ -103,6 +98,7 @@ export default (api: IApi) => { routes: api.appData.routes, locales: api.config.locales, pkg: api.pkg, + disableLiveDemo: shouldDisabledLiveDemo(api), }; memo.module .rule('watch-parent') diff --git a/src/features/compile/makoHooks.ts b/src/features/compile/makoHooks.ts index 3fe08a885..62251f4ac 100644 --- a/src/features/compile/makoHooks.ts +++ b/src/features/compile/makoHooks.ts @@ -6,6 +6,7 @@ import url from 'url'; import { techStacks } from '.'; import { RunLoaderOption, runLoaders } from '../../utils'; import { addAtomMeta, addExampleAssets } from '../assets'; +import { shouldDisabledLiveDemo } from './utils'; interface ICustomerRunLoaderInterface extends RunLoaderOption { type?: 'css' | 'js' | 'jsx'; @@ -31,6 +32,7 @@ const customRunLoaders = async (options: ICustomerRunLoaderInterface) => { const mdLoaderPath = require.resolve('../../loaders/markdown'); export const getLoadHook = (api: IApi) => { + const disableLiveDemo = shouldDisabledLiveDemo(api); return async (filePath: string) => { const loaderBaseOpts: Partial = { techStacks, @@ -42,6 +44,7 @@ export const getLoadHook = (api: IApi) => { routes: api.appData.routes, locales: api.config.locales || [], pkg: api.pkg, + disableLiveDemo, }; const requestUrl = url.parse(filePath); diff --git a/src/features/compile/utils.ts b/src/features/compile/utils.ts new file mode 100644 index 000000000..6f7d6b90e --- /dev/null +++ b/src/features/compile/utils.ts @@ -0,0 +1,17 @@ +import { isArray } from '@umijs/utils/compiled/lodash'; +import { IApi } from 'umi'; + +export const shouldDisabledLiveDemo = (api: IApi) => { + const extraBabelPlugins = api.userConfig.extraBabelPlugins; + const disableFlag = + isArray(extraBabelPlugins) && + extraBabelPlugins?.some((p: any) => + /^import$|babel-plugin-import/.test(p[0]), + ); + if (disableFlag) { + api.logger.warn( + "dumi don't suggest to use babel-plugin-import, liveDemo will be disabled because of this config.", + ); + } + return disableFlag; +}; diff --git a/src/loaders/markdown/index.ts b/src/loaders/markdown/index.ts index ec875ec48..0f793495a 100644 --- a/src/loaders/markdown/index.ts +++ b/src/loaders/markdown/index.ts @@ -21,6 +21,7 @@ interface IMdLoaderDefaultModeOptions atomId: string, meta: IMdTransformerResult['meta']['frontmatter'], ) => void; + disableLiveDemo: boolean; } interface IMdLoaderDemosModeOptions @@ -166,21 +167,21 @@ function emitDemo( } }); - const dedupedDemosDeps = Object.entries(demoDepsMap).reduce< - IDemoDependency[] - >((acc, [, deps]) => { - return acc.concat( - Object.entries(deps) - .map(([key, specifier]) => { - const existingIndex = acc.findIndex((obj) => obj.key === key); - if (existingIndex === -1) { - return { key, specifier }; - } - return undefined; - }) - .filter((item) => item !== undefined), - ); - }, []); + const dedupedDemosDeps = opts.disableLiveDemo + ? [] + : Object.entries(demoDepsMap).reduce((acc, [, deps]) => { + return acc.concat( + Object.entries(deps) + .map(([key, specifier]) => { + const existingIndex = acc.findIndex((obj) => obj.key === key); + if (existingIndex === -1) { + return { key, specifier }; + } + return undefined; + }) + .filter((item) => item !== undefined), + ); + }, []); return Mustache.render( `import React from 'react'; import '${winPath(this.getDependencies()[0])}?watch=parent'; @@ -231,8 +232,13 @@ export const demos = { renderContext: function renderContext( this: NonNullable[0], ) { - // do not render context for inline demo - if (!('resolveMap' in this) || !('asset' in this)) return 'undefined'; + // do not render context for inline demo && config babel-import-plugin project + if ( + !('resolveMap' in this) || + !('asset' in this) || + opts.disableLiveDemo + ) + return 'undefined'; const context = Object.entries(demoDepsMap[this.id]).reduce( (acc, [key, specifier]) => ({ ...acc, @@ -245,7 +251,7 @@ export const demos = { renderRenderOpts: function renderRenderOpts( this: NonNullable[0], ) { - if (!('renderOpts' in this)) { + if (!('renderOpts' in this) || opts.disableLiveDemo) { return 'undefined'; } const renderOpts = this.renderOpts; From d87c2072ca846b3fd47325c00db4d42c4a628bb8 Mon Sep 17 00:00:00 2001 From: Jinbao1001 Date: Mon, 9 Sep 2024 11:06:47 +0800 Subject: [PATCH 2/3] docs: guide to faq --- docs/guide/faq.md | 12 ++++++++++++ src/features/compile/utils.ts | 8 ++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/docs/guide/faq.md b/docs/guide/faq.md index 904012e80..2efbcd419 100644 --- a/docs/guide/faq.md +++ b/docs/guide/faq.md @@ -218,3 +218,15 @@ import './index.css'; ## 是否支持三级导航? 不支持。如果文档目录结构的复杂度超过 3 级,应该考虑优化文档整体结构而非使用三级导航。如果有特殊场景需要,可以自定义主题实现。 + +## 为什么 live demo 和 按需加载配置(babel-plugin-import) 不能同时开启? + +先说结论, `live demo` 特性开启时, 组件皆是全量导入的, 按需配置是无效的。 `babel-plugin-import` 唯一的作用就是自动引入组件 `css` 文件, 如果你的按需加载配置是 `style: false`, 请放心移除按需加载配置, 即可开启 `dumi` 最新的 `live demo` 特性。 + +原因在于 `live demo` 是需要使用 `sucrase` 在浏览器编译的, 我们会把依赖在编译时准备好注入到 `context` 中, 结合用户代码在源码在运行时变更时进行重新编译. 但此时会出现以下几点问题: + +1. 运行时重新编译并不会经过各类按需加载`插件loader`的流程处理, 和编译时行为不符。 +2. `babel-plugin-import` 会在编译时移除 `dumi` 注入的 `import * as antd from 'antd';` 等全量注入的`import`语句, 导致编译时变量缺失。 +3. 如果我们支持了按需的 `context` 依赖注入, 用户在运行时额外新增了 `input` 组件 -> `import { Button, Input } from 'antd';`, 也会发现 `Input` 组件未定义, 这不是预期内的结果。 + +所以 `dumi` 不建议在编写 `demo` 时配置按需加载, 考虑到部分用户使用按需加载是为了导入组件样式文件, 所以我们不会禁用 `babel-plugin-import` 等按需加载插件, 只是禁用 `dumi` 本身的 `live demo` 特性。 diff --git a/src/features/compile/utils.ts b/src/features/compile/utils.ts index 6f7d6b90e..43e575e34 100644 --- a/src/features/compile/utils.ts +++ b/src/features/compile/utils.ts @@ -1,16 +1,16 @@ +import { logger } from '@umijs/utils'; import { isArray } from '@umijs/utils/compiled/lodash'; import { IApi } from 'umi'; - export const shouldDisabledLiveDemo = (api: IApi) => { const extraBabelPlugins = api.userConfig.extraBabelPlugins; const disableFlag = isArray(extraBabelPlugins) && - extraBabelPlugins?.some((p: any) => + extraBabelPlugins!.some((p: any) => /^import$|babel-plugin-import/.test(p[0]), ); if (disableFlag) { - api.logger.warn( - "dumi don't suggest to use babel-plugin-import, liveDemo will be disabled because of this config.", + logger.warn( + 'live demo feature has been automatically disabled since babel-plugin-import be registered, if you want to enable live demo feature, checkout: https://d.umijs.org/guide/faq', ); } return disableFlag; From 351c2f5c374b84086229a8d57398d91cd52ca2ac Mon Sep 17 00:00:00 2001 From: Jinbao1001 Date: Mon, 9 Sep 2024 14:01:21 +0800 Subject: [PATCH 3/3] docs: simple faq --- docs/guide/faq.md | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/docs/guide/faq.md b/docs/guide/faq.md index 2efbcd419..1223e74da 100644 --- a/docs/guide/faq.md +++ b/docs/guide/faq.md @@ -219,14 +219,13 @@ import './index.css'; 不支持。如果文档目录结构的复杂度超过 3 级,应该考虑优化文档整体结构而非使用三级导航。如果有特殊场景需要,可以自定义主题实现。 -## 为什么 live demo 和 按需加载配置(babel-plugin-import) 不能同时开启? +## ## 为什么 live demo 和 babel-plugin-import 无法一起使用? -先说结论, `live demo` 特性开启时, 组件皆是全量导入的, 按需配置是无效的。 `babel-plugin-import` 唯一的作用就是自动引入组件 `css` 文件, 如果你的按需加载配置是 `style: false`, 请放心移除按需加载配置, 即可开启 `dumi` 最新的 `live demo` 特性。 +live demo 需要的正是全量引入,和 babel-plugin-import 的工作逻辑有冲突。 -原因在于 `live demo` 是需要使用 `sucrase` 在浏览器编译的, 我们会把依赖在编译时准备好注入到 `context` 中, 结合用户代码在源码在运行时变更时进行重新编译. 但此时会出现以下几点问题: +解决方案: -1. 运行时重新编译并不会经过各类按需加载`插件loader`的流程处理, 和编译时行为不符。 -2. `babel-plugin-import` 会在编译时移除 `dumi` 注入的 `import * as antd from 'antd';` 等全量注入的`import`语句, 导致编译时变量缺失。 -3. 如果我们支持了按需的 `context` 依赖注入, 用户在运行时额外新增了 `input` 组件 -> `import { Button, Input } from 'antd';`, 也会发现 `Input` 组件未定义, 这不是预期内的结果。 - -所以 `dumi` 不建议在编写 `demo` 时配置按需加载, 考虑到部分用户使用按需加载是为了导入组件样式文件, 所以我们不会禁用 `babel-plugin-import` 等按需加载插件, 只是禁用 `dumi` 本身的 `live demo` 特性。 +- 不需要 live demo:忽略警告即可 +- 希望开启 live demo: + - style: false 直接去掉插件即可 + - 否则借助 .dumi/global.css 加载组件样式,可以参考 [and ssr 静态样式导出](https://ant.design/docs/blog/extract-ssr-cn#static-extract-style)提取`css`文件。