├── .dumi
├── app.tsx
├── global.less
├── pages
│ └── __internal.tsx
└── theme
│ ├── builtins
│ ├── ThemeCases
│ │ ├── index.less
│ │ └── index.tsx
│ └── WhoAreUsing
│ │ ├── index.less
│ │ └── index.tsx
│ ├── internal
│ └── test
│ │ ├── index.tsx
│ │ └── normal.md
│ └── slots
│ └── HeaderExtra
│ ├── index.less
│ └── index.tsx
├── .dumirc.ts
├── .editorconfig
├── .eslintignore
├── .eslintrc.js
├── .fatherrc.ts
├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.yml
│ ├── config.yml
│ └── feature_request.yml
├── PULL_REQUEST_TEMPLATE.md
└── workflows
│ ├── ci.yml
│ ├── emoji-helper.yml
│ ├── issue-reply.yml
│ ├── pkg.pr.new.yml
│ ├── pr-welcome.yml
│ └── release-notify.yml
├── .gitignore
├── .husky
├── commit-msg
└── pre-commit
├── .npmrc
├── .prettierignore
├── .prettierrc.js
├── .stylelintrc
├── CONTRIBUTING.md
├── Cargo.lock
├── Cargo.toml
├── LICENSE
├── README.md
├── assets-types
├── package.json
└── typings
│ ├── atom
│ ├── index.d.ts
│ └── props
│ │ ├── index.d.ts
│ │ ├── jsdoc.d.ts
│ │ └── types.d.ts
│ ├── example.d.ts
│ └── index.d.ts
├── bin
├── dumi.js
└── forkedDev.js
├── compiled
├── @umijs
│ └── plugins
│ │ ├── index.js
│ │ └── package.json
├── _internal
│ └── searchWorker.min.js
└── loader-runner
│ ├── index.d.ts
│ └── index.js
├── crates
└── swc_plugin_react_demo
│ ├── Cargo.toml
│ ├── package.json
│ └── src
│ ├── lib.rs
│ └── utils.rs
├── docs
├── config
│ ├── demo.md
│ ├── env.md
│ ├── index.md
│ ├── markdown.md
│ └── runtime.md
├── guide
│ ├── auto-api-table.md
│ ├── commands.md
│ ├── conventional-routing.md
│ ├── demos
│ │ └── cols.tsx
│ ├── faq.md
│ ├── i18n.md
│ ├── index.md
│ ├── initialize.md
│ ├── markdown.md
│ ├── mobile-library.md
│ ├── page-config.md
│ ├── page-tab.$tab-example.md
│ ├── page-tab.md
│ ├── project-structure.md
│ ├── upgrading.md
│ ├── vue-api-table.md
│ ├── vue.md
│ └── write-demo.md
├── index.md
├── plugin
│ ├── api.md
│ ├── index.md
│ ├── market.md
│ ├── new.md
│ └── techstack.md
└── theme
│ ├── api.md
│ ├── default.md
│ ├── global-component.md
│ ├── index.md
│ ├── market.md
│ └── mobile.md
├── examples
├── mobile
│ ├── .dumirc.ts
│ ├── package.json
│ └── src
│ │ ├── .gitignore
│ │ ├── Foo
│ │ ├── index.md
│ │ └── index.tsx
│ │ ├── PCFoo
│ │ └── index.md
│ │ └── index.ts
├── normal
│ ├── .dumi
│ │ ├── pages
│ │ │ ├── loader-test.tsx
│ │ │ └── nothing.tsx
│ │ └── theme
│ │ │ └── plugin.ts
│ ├── .dumirc.ts
│ ├── .fatherrc.ts
│ ├── a.tsx
│ ├── docs
│ │ ├── a
│ │ │ └── b
│ │ │ │ ├── c.md
│ │ │ │ └── d.md
│ │ ├── demo.less
│ │ ├── demo.tsx
│ │ ├── hello
│ │ │ └── index.md
│ │ └── index.md
│ ├── logo.png
│ ├── mako.config.json
│ ├── package.json
│ ├── plugin.ts
│ └── src
│ │ ├── .gitignore
│ │ ├── Foo
│ │ ├── demo
│ │ │ └── work.tsx
│ │ ├── index.$tab-api.md
│ │ ├── index.en-US.md
│ │ ├── index.md
│ │ └── index.tsx
│ │ └── index.ts
├── temp
│ ├── b.md
│ └── docs
│ │ └── a.md
└── vue
│ ├── .dumirc.ts
│ ├── .eslintrc.js
│ ├── .fatherrc.ts
│ ├── README.md
│ ├── docs
│ ├── framework-test
│ │ ├── external
│ │ │ └── App.vue
│ │ └── index.md
│ └── index.md
│ ├── package.json
│ ├── src
│ ├── Button
│ │ ├── button.less
│ │ ├── demos
│ │ │ ├── Demo.tsx
│ │ │ └── demo.less
│ │ ├── index.md
│ │ └── index.tsx
│ ├── Foo
│ │ ├── demos
│ │ │ └── sfc-demo.vue
│ │ ├── index.md
│ │ └── index.tsx
│ ├── List.tsx
│ ├── functional.tsx
│ ├── index.ts
│ ├── my-badge.vue
│ └── test.svg
│ ├── tsconfig.json
│ └── tsconfig.vue.json
├── index.d.ts
├── mako.config.json
├── package.json
├── plugin-utils.d.ts
├── plugin-utils.js
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
├── rust-toolchain
├── rustfmt.toml
├── scripts
├── miscUtil.ts
├── pkg-pr-new.ts
├── pre-bundle-worker.js
├── prepare-examples.ts
├── release.ts
├── sync-from-umi.js
└── vercel-install.sh
├── src
├── assetParsers
│ ├── BaseParser.ts
│ ├── __tests__
│ │ ├── FakeParser.js
│ │ └── parser.fork.test.ts
│ ├── atom.ts
│ ├── block.ts
│ └── utils.ts
├── cli.ts
├── client
│ ├── misc
│ │ └── reactDemoCompiler.ts
│ ├── pages
│ │ ├── 404.ts
│ │ ├── Demo
│ │ │ ├── index.less
│ │ │ └── index.ts
│ │ └── Loading.ts
│ ├── theme-api
│ │ ├── AtomRenderer.tsx
│ │ ├── DumiDemo
│ │ │ ├── DemoErrorBoundary.tsx
│ │ │ └── index.tsx
│ │ ├── DumiDemoGrid.tsx
│ │ ├── DumiPage.tsx
│ │ ├── context.ts
│ │ ├── index.ts
│ │ ├── openCodeSandbox.ts
│ │ ├── openStackBlitz.ts
│ │ ├── types.ts
│ │ ├── useAtomAssets.ts
│ │ ├── useLiveDemo.ts
│ │ ├── useLocale.ts
│ │ ├── useNavData.ts
│ │ ├── usePrefersColor.ts
│ │ ├── useRenderer.ts
│ │ ├── useRouteMeta.ts
│ │ ├── useSidebarData.ts
│ │ ├── useSiteSearch
│ │ │ ├── index.ts
│ │ │ ├── searchWorker.ts
│ │ │ └── useSearchData.ts
│ │ ├── useTabMeta.ts
│ │ └── utils.ts
│ ├── theme-default
│ │ ├── builtins
│ │ │ ├── API
│ │ │ │ ├── index.less
│ │ │ │ └── index.tsx
│ │ │ ├── Badge
│ │ │ │ ├── index.less
│ │ │ │ └── index.tsx
│ │ │ ├── CodeGroup
│ │ │ │ └── index.tsx
│ │ │ ├── Container
│ │ │ │ ├── index.less
│ │ │ │ └── index.tsx
│ │ │ ├── Previewer
│ │ │ │ ├── index.less
│ │ │ │ └── index.tsx
│ │ │ ├── SourceCode
│ │ │ │ ├── index.less
│ │ │ │ └── index.tsx
│ │ │ ├── Table
│ │ │ │ ├── index.less
│ │ │ │ └── index.tsx
│ │ │ └── Tree
│ │ │ │ ├── index.less
│ │ │ │ └── index.tsx
│ │ ├── layouts
│ │ │ └── DocLayout
│ │ │ │ ├── index.less
│ │ │ │ └── index.tsx
│ │ ├── locales
│ │ │ ├── en-US.json
│ │ │ └── zh-CN.json
│ │ ├── slots
│ │ │ ├── ColorSwitch
│ │ │ │ ├── index.less
│ │ │ │ └── index.tsx
│ │ │ ├── Content
│ │ │ │ ├── index.less
│ │ │ │ └── index.tsx
│ │ │ ├── ContentFooter
│ │ │ │ ├── index.less
│ │ │ │ └── index.tsx
│ │ │ ├── ContentTabs
│ │ │ │ ├── index.less
│ │ │ │ └── index.tsx
│ │ │ ├── Features
│ │ │ │ ├── index.less
│ │ │ │ └── index.tsx
│ │ │ ├── Footer
│ │ │ │ ├── index.less
│ │ │ │ └── index.tsx
│ │ │ ├── Header
│ │ │ │ ├── index.less
│ │ │ │ └── index.tsx
│ │ │ ├── HeaderExtra
│ │ │ │ └── index.tsx
│ │ │ ├── Hero
│ │ │ │ ├── index.less
│ │ │ │ └── index.tsx
│ │ │ ├── HeroTitle
│ │ │ │ ├── index.less
│ │ │ │ └── index.tsx
│ │ │ ├── LangSwitch
│ │ │ │ ├── index.less
│ │ │ │ └── index.tsx
│ │ │ ├── Loading
│ │ │ │ ├── index.less
│ │ │ │ └── index.tsx
│ │ │ ├── Logo
│ │ │ │ ├── index.less
│ │ │ │ └── index.tsx
│ │ │ ├── Navbar
│ │ │ │ ├── index.less
│ │ │ │ └── index.tsx
│ │ │ ├── NavbarExtra
│ │ │ │ └── index.tsx
│ │ │ ├── NotFound
│ │ │ │ ├── index.less
│ │ │ │ └── index.tsx
│ │ │ ├── PreviewerActions
│ │ │ │ ├── index.less
│ │ │ │ └── index.tsx
│ │ │ ├── PreviewerActionsExtra
│ │ │ │ └── index.tsx
│ │ │ ├── RtlSwitch
│ │ │ │ ├── index.less
│ │ │ │ └── index.tsx
│ │ │ ├── SearchBar
│ │ │ │ ├── Input.tsx
│ │ │ │ ├── Mask.tsx
│ │ │ │ ├── index.less
│ │ │ │ └── index.tsx
│ │ │ ├── SearchResult
│ │ │ │ ├── index.less
│ │ │ │ └── index.tsx
│ │ │ ├── Sidebar
│ │ │ │ ├── index.less
│ │ │ │ └── index.tsx
│ │ │ ├── SocialIcon
│ │ │ │ ├── index.less
│ │ │ │ └── index.tsx
│ │ │ ├── SourceCodeEditor
│ │ │ │ ├── index.less
│ │ │ │ └── index.tsx
│ │ │ ├── Tabs
│ │ │ │ ├── index.less
│ │ │ │ └── index.tsx
│ │ │ └── Toc
│ │ │ │ ├── index.less
│ │ │ │ └── index.tsx
│ │ └── styles
│ │ │ ├── heti.less
│ │ │ ├── utils.less
│ │ │ └── variables.less
│ ├── tsconfig.json
│ └── typings.d.ts
├── constants.ts
├── features
│ ├── assets.ts
│ ├── autoAlias.ts
│ ├── compile
│ │ ├── babelLoaderCustomize.ts
│ │ ├── index.ts
│ │ ├── makoHooks.ts
│ │ └── utils.ts
│ ├── configPlugins
│ │ ├── index.ts
│ │ └── schema.ts
│ ├── derivative.ts
│ ├── exportStatic.ts
│ ├── exports.ts
│ ├── html2sketch.ts
│ ├── locales.ts
│ ├── meta.ts
│ ├── parser.ts
│ ├── routes.ts
│ ├── sideEffects
│ │ ├── docSideEffectsWebpackPlugin.ts
│ │ └── index.ts
│ ├── sitemap.ts
│ ├── tabs.ts
│ └── theme
│ │ ├── index.ts
│ │ └── loader.ts
├── index.ts
├── loaders
│ ├── demo
│ │ └── index.ts
│ ├── markdown
│ │ ├── index.ts
│ │ └── transformer
│ │ │ ├── fixtures
│ │ │ ├── code-group
│ │ │ │ ├── expect.ts
│ │ │ │ ├── index.md
│ │ │ │ └── with-hightlight
│ │ │ │ │ ├── expect.ts
│ │ │ │ │ └── index.md
│ │ │ ├── demo
│ │ │ │ ├── demo.jsx
│ │ │ │ ├── expect.ts
│ │ │ │ └── index.md
│ │ │ ├── embed
│ │ │ │ ├── embed.md
│ │ │ │ ├── expect.ts
│ │ │ │ └── index.md
│ │ │ ├── img-demo
│ │ │ │ ├── expect.ts
│ │ │ │ └── index.md
│ │ │ ├── normal
│ │ │ │ ├── expect.ts
│ │ │ │ ├── expect.ts.snap
│ │ │ │ └── index.md
│ │ │ ├── react-component
│ │ │ │ ├── expect.ts
│ │ │ │ └── index.md
│ │ │ └── skip-only
│ │ │ │ ├── case01
│ │ │ │ ├── expect.ts
│ │ │ │ └── index.md
│ │ │ │ ├── case02
│ │ │ │ ├── expect.ts
│ │ │ │ └── index.md
│ │ │ │ ├── case03
│ │ │ │ ├── expect.ts
│ │ │ │ └── index.md
│ │ │ │ ├── case04
│ │ │ │ ├── expect.ts
│ │ │ │ └── index.md
│ │ │ │ ├── case05
│ │ │ │ ├── expect.ts
│ │ │ │ └── index.md
│ │ │ │ └── demos
│ │ │ │ ├── bar.jsx
│ │ │ │ ├── baz.jsx
│ │ │ │ └── foo.jsx
│ │ │ ├── index.test.ts
│ │ │ ├── index.ts
│ │ │ ├── rehypeDemo.ts
│ │ │ ├── rehypeDesc.ts
│ │ │ ├── rehypeEnhancedTag.ts
│ │ │ ├── rehypeHighlightLine.ts
│ │ │ ├── rehypeImg.ts
│ │ │ ├── rehypeIsolation.ts
│ │ │ ├── rehypeJsxify.ts
│ │ │ ├── rehypeLink.ts
│ │ │ ├── rehypeRaw.ts
│ │ │ ├── rehypeSlug.ts
│ │ │ ├── rehypeStrip.ts
│ │ │ ├── rehypeText.ts
│ │ │ ├── remarkBreaks.ts
│ │ │ ├── remarkContainer.ts
│ │ │ ├── remarkEmbed.ts
│ │ │ └── remarkMeta.ts
│ ├── null
│ │ └── index.ts
│ ├── page
│ │ └── index.ts
│ ├── post-raw
│ │ └── index.ts
│ └── pre-raw
│ │ └── index.ts
├── preset.ts
├── registerMethods.ts
├── service
│ ├── cli.ts
│ ├── constants.ts
│ ├── dev.ts
│ ├── forkedDev.ts
│ ├── printHelp.ts
│ └── service.ts
├── techStacks
│ ├── react.ts
│ └── utils.ts
├── templates
│ ├── ContextWrapper.ts.tpl
│ └── meta
│ │ ├── exports.ts.tpl
│ │ ├── index.ts.tpl
│ │ └── runtime.ts.tpl
├── types.ts
└── utils.ts
├── suites
├── boilerplate
│ ├── .fatherrc.ts
│ ├── bin
│ │ └── create-dumi.js
│ ├── package.json
│ ├── src
│ │ ├── cli.ts
│ │ └── index.ts
│ ├── templates
│ │ ├── react
│ │ │ ├── .dumirc.ts.tpl
│ │ │ ├── .editorconfig
│ │ │ ├── .eslintrc.js
│ │ │ ├── .fatherrc.ts
│ │ │ ├── .gitignore.tpl
│ │ │ ├── .husky
│ │ │ │ ├── commit-msg
│ │ │ │ └── pre-commit
│ │ │ ├── .prettierignore
│ │ │ ├── .prettierrc.js
│ │ │ ├── .stylelintrc
│ │ │ ├── LICENSE.tpl
│ │ │ ├── README.md.tpl
│ │ │ ├── docs
│ │ │ │ ├── guide.md
│ │ │ │ └── index.md.tpl
│ │ │ ├── package.json.tpl
│ │ │ ├── src
│ │ │ │ ├── Foo
│ │ │ │ │ ├── index.md.tpl
│ │ │ │ │ └── index.tsx
│ │ │ │ └── index.ts
│ │ │ └── tsconfig.json.tpl
│ │ ├── site
│ │ │ ├── .dumirc.ts.tpl
│ │ │ ├── .editorconfig
│ │ │ ├── .gitignore.tpl
│ │ │ ├── .husky
│ │ │ │ ├── commit-msg
│ │ │ │ └── pre-commit
│ │ │ ├── .prettierignore
│ │ │ ├── .prettierrc.js
│ │ │ ├── LICENSE.tpl
│ │ │ ├── README.md.tpl
│ │ │ ├── docs
│ │ │ │ ├── guide.md
│ │ │ │ └── index.md.tpl
│ │ │ ├── package.json.tpl
│ │ │ └── tsconfig.json.tpl
│ │ ├── theme
│ │ │ ├── .editorconfig
│ │ │ ├── .eslintrc.js
│ │ │ ├── .fatherrc.ts
│ │ │ ├── .gitignore.tpl
│ │ │ ├── .husky
│ │ │ │ ├── commit-msg
│ │ │ │ └── pre-commit
│ │ │ ├── .prettierignore
│ │ │ ├── .prettierrc.js
│ │ │ ├── .stylelintrc
│ │ │ ├── LICENSE.tpl
│ │ │ ├── README.md.tpl
│ │ │ ├── example
│ │ │ │ ├── .dumirc.ts.tpl
│ │ │ │ └── docs
│ │ │ │ │ ├── index.md
│ │ │ │ │ └── test.md
│ │ │ ├── package.json.tpl
│ │ │ ├── src
│ │ │ │ ├── builtins
│ │ │ │ │ └── .gitkeep
│ │ │ │ ├── layouts
│ │ │ │ │ └── .gitkeep
│ │ │ │ ├── locales
│ │ │ │ │ └── .gitkeep
│ │ │ │ ├── plugin
│ │ │ │ │ └── index.ts.tpl
│ │ │ │ ├── slots
│ │ │ │ │ └── HeroTitle
│ │ │ │ │ │ └── index.tsx.tpl
│ │ │ │ └── types.ts
│ │ │ └── tsconfig.json.tpl
│ │ └── vue
│ │ │ ├── .dumirc.ts.tpl
│ │ │ ├── .editorconfig
│ │ │ ├── .eslintrc.js
│ │ │ ├── .gitignore.tpl
│ │ │ ├── .husky
│ │ │ ├── commit-msg
│ │ │ └── pre-commit
│ │ │ ├── .prettierignore
│ │ │ ├── .prettierrc.js
│ │ │ ├── .stylelintrc
│ │ │ ├── LICENSE.tpl
│ │ │ ├── README.md.tpl
│ │ │ ├── docs
│ │ │ ├── guide.md
│ │ │ └── index.md.tpl
│ │ │ ├── package.json.tpl
│ │ │ ├── src
│ │ │ ├── Bar
│ │ │ │ ├── Bar.test.ts.tpl
│ │ │ │ ├── RootBar.vue
│ │ │ │ ├── index.md.tpl
│ │ │ │ └── index.ts
│ │ │ ├── Foo
│ │ │ │ ├── Foo.less
│ │ │ │ ├── Foo.test.ts.tpl
│ │ │ │ ├── index.md.tpl
│ │ │ │ └── index.tsx
│ │ │ └── index.ts
│ │ │ ├── tsconfig.build.json.tpl
│ │ │ ├── tsconfig.json.tpl
│ │ │ ├── vite.config.mts
│ │ │ └── vitest.config.mts
│ └── tsconfig.json
├── dumi-vue-meta
│ ├── .fatherrc.ts
│ ├── .gitignore
│ ├── README.md
│ ├── package.json
│ ├── src
│ │ ├── checker
│ │ │ ├── Project.ts
│ │ │ ├── TypeCheckService.ts
│ │ │ ├── createVueLanguageService.ts
│ │ │ ├── helpers.ts
│ │ │ ├── index.ts
│ │ │ └── repo.ts
│ │ ├── dumiTransfomer.ts
│ │ ├── index.ts
│ │ ├── schemaResolver
│ │ │ ├── custom
│ │ │ │ ├── externalSymbol.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── vueOption.ts
│ │ │ │ └── vueTypes.ts
│ │ │ └── index.ts
│ │ ├── types.ts
│ │ └── utils.ts
│ ├── tests
│ │ ├── .eslintrc.js
│ │ ├── __snapshots__
│ │ │ ├── index.test.ts.snap
│ │ │ └── transformer.test.ts.snap
│ │ ├── fixtures
│ │ │ ├── externalProps.ts
│ │ │ ├── index.ts
│ │ │ ├── props.ts
│ │ │ ├── sfc-alias
│ │ │ │ ├── foo.vue
│ │ │ │ └── index.ts
│ │ │ ├── sfc
│ │ │ │ ├── foo.vue
│ │ │ │ └── index.ts
│ │ │ ├── tsconfig.json
│ │ │ └── tsx
│ │ │ │ ├── foo.tsx
│ │ │ │ ├── functional.tsx
│ │ │ │ ├── index.ts
│ │ │ │ ├── list.tsx
│ │ │ │ └── use.ts
│ │ ├── index.test.ts
│ │ ├── project.test.ts
│ │ ├── source.test.ts
│ │ ├── transformer.test.ts
│ │ └── utils.ts
│ └── tsconfig.json
├── father-plugin
│ ├── .fatherrc.ts
│ ├── LICENSE
│ ├── README.md
│ ├── example
│ │ ├── .fatherrc.ts
│ │ ├── package.json
│ │ └── src
│ │ │ ├── layouts
│ │ │ └── DocLayout.ts
│ │ │ ├── plugin.ts
│ │ │ └── slots
│ │ │ ├── 404.ts
│ │ │ ├── A
│ │ │ └── index.ts
│ │ │ └── B.ts
│ ├── package.json
│ ├── src
│ │ ├── babelPlugins
│ │ │ └── transformRelativeSlots.ts
│ │ └── index.ts
│ └── tsconfig.json
├── preset-vue
│ ├── .fatherrc.ts
│ ├── .gitignore
│ ├── README.md
│ ├── compiled
│ │ └── @vue
│ │ │ └── babel-plugin-jsx
│ │ │ ├── index.js
│ │ │ └── package.json
│ ├── lib
│ │ ├── compiler.mjs
│ │ ├── preflight.mjs
│ │ ├── renderer.mjs
│ │ └── runtimePlugin.mjs
│ ├── package.json
│ ├── src
│ │ ├── atomParser
│ │ │ └── index.ts
│ │ ├── common.ts
│ │ ├── compiler
│ │ │ ├── __tests__
│ │ │ │ ├── __snapshots__
│ │ │ │ │ └── compile.test.ts.snap
│ │ │ │ └── compile.test.ts
│ │ │ ├── browser.ts
│ │ │ ├── index.ts
│ │ │ └── node.ts
│ │ ├── index.ts
│ │ ├── requireHook.ts
│ │ ├── shared.ts
│ │ └── vue
│ │ │ ├── checkVersion.ts
│ │ │ ├── index.ts
│ │ │ ├── runtime
│ │ │ ├── getPreviewerData.ts
│ │ │ ├── preflight.ts
│ │ │ ├── renderer.ts
│ │ │ └── runtimePlugin.ts
│ │ │ ├── techStack
│ │ │ ├── index.ts
│ │ │ ├── jsx.ts
│ │ │ └── sfc.ts
│ │ │ └── webpack
│ │ │ ├── config.ts
│ │ │ └── index.ts
│ ├── tsconfig.json
│ ├── tsup.config.ts
│ └── typings.d.ts
└── theme-mobile
│ ├── .fatherrc.ts
│ ├── package.json
│ ├── src
│ ├── builtins
│ │ └── Previewer
│ │ │ ├── index.less
│ │ │ └── index.tsx
│ ├── layouts
│ │ ├── DemoLayout.less
│ │ └── DemoLayout.tsx
│ ├── slots
│ │ ├── Device
│ │ │ ├── index.less
│ │ │ └── index.tsx
│ │ └── PreviewerActions
│ │ │ ├── index.less
│ │ │ └── index.tsx
│ ├── styles
│ │ └── variables.less
│ └── types.ts
│ ├── tsconfig.json
│ └── typings.d.ts
├── tech-stack-utils.d.ts
├── tech-stack-utils.js
├── tests
└── build.test.ts
├── tsconfig.json
└── vitest.config.ts
/.dumi/app.tsx:
--------------------------------------------------------------------------------
1 | // @ts-ignore
2 | import { Navigate } from 'dumi';
3 | import React from 'react';
4 |
5 | export const patchClientRoutes = ({ routes }: any) => {
6 | /**
7 | * redirect all legacy zh-CN routes to root
8 | */
9 | routes[0].children.push({
10 | id: 'zh-cn-redirect',
11 | path: 'zh-CN/*',
12 | element: ,
13 | });
14 |
15 | return routes;
16 | };
17 |
--------------------------------------------------------------------------------
/.dumi/global.less:
--------------------------------------------------------------------------------
1 | @{dark-selector} body {
2 | color: #fff;
3 | }
4 |
5 | // nav / sidebar
6 | li,
7 | dd {
8 | &:has(a[href*='__internal']) {
9 | display: none;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/.dumi/theme/internal/test/index.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * Test importing markdown file
3 | * @see: https://github.com/umijs/dumi/pull/2281
4 | */
5 | import React from 'react';
6 | // @ts-ignore
7 | import NormalMarkdown from './normal.md';
8 |
9 | function InternalTest() {
10 | return (
11 |
12 |
Internal Test
13 |
14 |
15 | );
16 | }
17 |
18 | export default InternalTest;
19 |
--------------------------------------------------------------------------------
/.dumi/theme/internal/test/normal.md:
--------------------------------------------------------------------------------
1 | # Hello Dumi
2 |
3 | - hello
4 | - world
5 |
6 | ---
7 |
8 | - [ ] eat
9 | - [ ] sleep
10 | - [x] code
11 |
12 | see: [#2281](https://github.com/umijs/dumi/pull/2281)
13 |
14 | ## Contributor
15 |
16 | > Thanks for your contribution!
17 |
18 | - [@Wxh16144](https://github.com/Wxh16144)
19 | - Welcome to [contribute](https://github.com/umijs/dumi/blob/master/CONTRIBUTING.md)
20 |
--------------------------------------------------------------------------------
/.dumi/theme/slots/HeaderExtra/index.less:
--------------------------------------------------------------------------------
1 | @import (reference) '../../../../theme-default/styles/variables.less';
2 |
3 | .@{prefix}-lang-select.dumi-version-select {
4 | border-inline-start: 1px solid @c-border;
5 | margin-inline-start: 15px;
6 | padding-inline-start: 5px;
7 |
8 | @{dark-selector} & {
9 | border-inline-start-color: @c-border-dark;
10 | }
11 |
12 | > select {
13 | padding-top: 1px;
14 | padding-bottom: 1px;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/.dumi/theme/slots/HeaderExtra/index.tsx:
--------------------------------------------------------------------------------
1 | import { ReactComponent as IconDown } from '@ant-design/icons-svg/inline-svg/outlined/down.svg';
2 | import React, { type FC } from 'react';
3 | import './index.less';
4 |
5 | const HeaderExtra: FC = () => {
6 | return (
7 |
8 |
24 |
25 |
26 | );
27 | };
28 |
29 | export default HeaderExtra;
30 |
--------------------------------------------------------------------------------
/.dumirc.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from './dist';
2 | import { version } from './package.json';
3 |
4 | export default defineConfig({
5 | html2sketch: {},
6 | // mako: {},
7 | favicons: [
8 | 'https://gw.alipayobjects.com/zos/bmw-prod/d3e3eb39-1cd7-4aa5-827c-877deced6b7e/lalxt4g3_w256_h256.png',
9 | ],
10 | autoAlias: false,
11 | outputPath: 'docs-dist',
12 | define: {
13 | 'process.env.DUMI_VERSION': version,
14 | },
15 | themeConfig: {
16 | hd: { rules: [] },
17 | rtl: true,
18 | name: 'dumi',
19 | logo: 'https://gw.alipayobjects.com/zos/bmw-prod/d3e3eb39-1cd7-4aa5-827c-877deced6b7e/lalxt4g3_w256_h256.png',
20 | footer: `Open-source MIT Licensed | Copyright © 2019-present
21 |
22 | Powered by self`,
23 | prefersColor: { default: 'auto' },
24 | socialLinks: {
25 | github: 'https://github.com/umijs/dumi',
26 | },
27 | },
28 | ...(process.env.NODE_ENV === 'development'
29 | ? {}
30 | : { ssr: { builder: 'webpack' } }),
31 | analytics: {
32 | ga_v2: 'G-GX2S89BMXB',
33 | },
34 | sitemap: { hostname: 'https://d.umijs.org' },
35 | });
36 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 |
12 | [*.md]
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | /dist
2 | /compiled
3 | /theme-default
4 | /suites/*/compiled
5 | /suites/preset-vue/lib
6 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: require.resolve('@umijs/lint/dist/config/eslint'),
3 | rules: {
4 | '@typescript-eslint/no-unused-vars': [
5 | 'error',
6 | { ignoreRestSiblings: true },
7 | ],
8 | },
9 | };
10 |
--------------------------------------------------------------------------------
/.fatherrc.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'father';
2 |
3 | export default defineConfig({
4 | cjs: {
5 | output: 'dist',
6 | ignores: ['src/client/**'],
7 | },
8 | esm: {
9 | input: 'src/client',
10 | output: 'dist/client',
11 | ignores: [
12 | 'src/client/theme-default',
13 | 'src/client/theme-api/useSiteSearch/searchWorker.ts',
14 | ],
15 | overrides: {
16 | 'src/client/theme-default': {
17 | output: 'theme-default',
18 | },
19 | },
20 | },
21 | prebundle: {
22 | deps: {
23 | // pre-bundle analytics for reduce install size
24 | // because @umijs/plugins depends on a lot of 3rd-party deps
25 | '@umijs/plugins/dist/analytics': { dts: false },
26 | },
27 | },
28 | });
29 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
2 | contact_links:
3 | - name: Github Discussions
4 | url: https://github.com/umijs/dumi/discussions
5 | about: Ask or answer usage questions / 交流使用问题
6 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
5 |
6 | ### 🤔 这个变动的性质是?/ What is the nature of this change?
7 |
8 | - [ ] 新特性提交 / New feature
9 | - [ ] bug 修复 / Fix bug
10 | - [ ] 样式优化 / Style optimization
11 | - [ ] 代码风格优化 / Code style optimization
12 | - [ ] 性能优化 / Performance optimization
13 | - [ ] 构建优化 / Build optimization
14 | - [ ] 网站、文档、Demo 改进 / Website, documentation, demo improvements
15 | - [ ] 重构代码或样式 / Refactor code or style
16 | - [ ] 测试相关 / Test related
17 | - [ ] 其他 / Other
18 |
19 | ### 🔗 相关 Issue / Related Issue
20 |
21 |
25 |
26 | ### 💡 需求背景和解决方案 / Background or solution
27 |
28 |
32 |
33 | ### 📝 更新日志 / Changelog
34 |
35 |
39 |
40 | | Language | Changelog |
41 | | ---------- | --------- |
42 | | 🇺🇸 English | |
43 | | 🇨🇳 Chinese | |
44 |
--------------------------------------------------------------------------------
/.github/workflows/emoji-helper.yml:
--------------------------------------------------------------------------------
1 | name: Emoji Helper
2 |
3 | on:
4 | release:
5 | types: [published]
6 |
7 | jobs:
8 | emoji:
9 | runs-on: ubuntu-latest
10 | steps:
11 | - uses: actions-cool/emoji-helper@v1.0.0
12 | with:
13 | type: 'release'
14 | emoji: '+1, laugh, heart, hooray, rocket, eyes'
15 |
--------------------------------------------------------------------------------
/.github/workflows/pr-welcome.yml:
--------------------------------------------------------------------------------
1 | name: PR Welcome
2 |
3 | on:
4 | pull_request_target:
5 | types: [opened]
6 |
7 | jobs:
8 | welcome:
9 | runs-on: ubuntu-latest
10 | steps:
11 | - uses: actions-cool/pr-welcome@v1.1.2
12 | with:
13 | pr-emoji: '+1, heart'
14 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | /examples/*/node_modules
3 | /suites/**/node_modules
4 | /suites/**/dist
5 | /dist
6 | /docs-dist
7 | /theme-default
8 | .DS_Store
9 | .vscode
10 | .dumi/tmp
11 | .dumi/tmp-production
12 | /examples/*/dist
13 | /examples/*/server
14 | /examples/*/.dumi/tmp*
15 | /docs/.upstream
16 | .idea
17 | /target
18 | /compiled/crates
19 | .swc
20 | /server
21 |
--------------------------------------------------------------------------------
/.husky/commit-msg:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 | . "$(dirname -- "$0")/_/husky.sh"
3 |
4 | npx commitlint --edit "${1}"
5 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 | . "$(dirname -- "$0")/_/husky.sh"
3 |
4 | npx lint-staged
5 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | registry=https://registry.npmjs.org/
2 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | /dist
2 | /compiled
3 | /theme-default
4 | *.yaml
5 |
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | pluginSearchDirs: false,
3 | plugins: [
4 | require.resolve('prettier-plugin-organize-imports'),
5 | require.resolve('prettier-plugin-packagejson'),
6 | ],
7 | printWidth: 80,
8 | proseWrap: 'never',
9 | singleQuote: true,
10 | trailingComma: 'all',
11 | overrides: [
12 | {
13 | files: '*.md',
14 | options: {
15 | proseWrap: 'preserve',
16 | },
17 | },
18 | ],
19 | };
20 |
--------------------------------------------------------------------------------
/.stylelintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@umijs/lint/dist/config/stylelint",
3 | "ignoreFiles": ["**/heti.less"],
4 | "rules": {
5 | "function-no-unknown": null,
6 | "color-function-notation": "legacy"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # How to contribute
2 |
3 | ## Requirements
4 |
5 | [Node.js](https://nodejs.org/) >= 14, [PNPM](https://pnpm.io/) >= 7, [Rust](https://www.rust-lang.org/)
6 |
7 | After Rust is installed, add `wasm32-wasi`.
8 |
9 | ```shell
10 | rustup target add wasm32-wasi
11 | ```
12 |
13 | ## Setup the repository locally
14 |
15 | 1. Fork and clone the repository.
16 |
17 | ```shell
18 | git clone https://github.com//dumi.git
19 | cd dumi
20 | ```
21 |
22 | 2. Install all dependencies
23 |
24 | ```shell
25 | pnpm install
26 | ```
27 |
28 | > Due to the network download required by the scripts that need to be run during installation, you may need to set up a network proxy if an error occurs.
29 |
30 | Now you can start developing
31 |
32 | ```shell
33 | pnpm dev
34 | pnpm docs:dev
35 | ```
36 |
37 | ## Submitting the Pull Request
38 |
39 | Submit a pull request from your topic branch to the master branch on the umijs/dumi repository.
40 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [workspace]
2 | members = [
3 | "crates/swc_plugin_react_demo",
4 | ]
5 | resolver = "2"
6 |
7 | [profile.release]
8 | lto = true
9 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019-present UmiJS Team
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # dumi
2 |
3 | [](https://npmjs.org/package/dumi) [](https://npmjs.org/package/dumi) [](https://github.com/umijs/dumi)
4 |
5 |
6 |
7 | dumi is a static site generator for component library development.
8 |
9 | ## Usage & Guide
10 |
11 | To view more online examples and docs, please visit [dumi official site](https://d.umijs.org).
12 |
13 | ## Contributing
14 |
15 | See [CONTRIBUTING.md](CONTRIBUTING.md)
16 |
17 | ## Badge
18 |
19 | Using dumi? Add a README badge to show it off: [](https://github.com/umijs/dumi)
20 |
21 | ```
22 | [](https://github.com/umijs/dumi)
23 | ```
24 |
25 | ## LICENSE
26 |
27 | MIT
28 |
--------------------------------------------------------------------------------
/assets-types/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "dumi-assets-types",
3 | "version": "2.4.14",
4 | "description": "Standard assets type definitions for dumi library project",
5 | "homepage": "https://github.com/umijs/dumi/tree/master/assets-types#readme",
6 | "bugs": "https://github.com/umijs/dumi/issues",
7 | "repository": {
8 | "type": "git",
9 | "url": "https://github.com/umijs/dumi"
10 | },
11 | "license": "MIT",
12 | "types": "typings/index.d.ts",
13 | "files": [
14 | "typings"
15 | ],
16 | "publishConfig": {
17 | "access": "public"
18 | },
19 | "authors": [
20 | "PeachScript (https://github.com/PeachScript)"
21 | ]
22 | }
23 |
--------------------------------------------------------------------------------
/assets-types/typings/atom/props/jsdoc.d.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * supported jsdoc tags
3 | */
4 | export interface BuiltinTags {
5 | /**
6 | * prop category
7 | */
8 | category?: string;
9 | /**
10 | * prop title
11 | */
12 | title?: string;
13 | /**
14 | * prop description
15 | */
16 | description?: string;
17 | /**
18 | * prop default value
19 | */
20 | default?: any;
21 | /**
22 | * ignore in doc
23 | */
24 | ignore?: boolean;
25 | /**
26 | * customize prop panel renderer
27 | */
28 | renderType?: string;
29 | /**
30 | * customize options for prop panel renderer
31 | */
32 | renderOptions?: Record;
33 | }
34 |
--------------------------------------------------------------------------------
/assets-types/typings/atom/props/types.d.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * supported type mapping
3 | */
4 | export interface TypeMap {
5 | string: string;
6 | number: number;
7 | boolean: boolean;
8 | object: object;
9 | array: Array;
10 | element: EntityType<'element', ElementInstance>;
11 | function: EntityType<'function', FunctionInstance>;
12 | dom: EntityType<'dom', DomInstance>;
13 | any: any;
14 | }
15 |
16 | /**
17 | * entity type
18 | * @note such as ReactNode or Function
19 | */
20 | export type EntityType = {
21 | $$__type: TypeName;
22 | $$__body: Shape;
23 | };
24 |
25 | /**
26 | * ReactNode type
27 | */
28 | export type ElementInstance = {
29 | /**
30 | * export name of component
31 | */
32 | componentName: string;
33 | /**
34 | * the NPM package component belongs to
35 | * @note empty means current package
36 | */
37 | npmPackageName?: string;
38 | /**
39 | * props of component
40 | */
41 | props: Record;
42 | } & Record;
43 |
44 | /**
45 | * Function type
46 | */
47 | export type FunctionInstance = {
48 | /**
49 | * source code of function
50 | */
51 | sourceCode: string;
52 | };
53 |
54 | /**
55 | * DOM type
56 | */
57 | export type DomInstance = {
58 | id: string;
59 | };
60 |
--------------------------------------------------------------------------------
/bin/dumi.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | // disable for TypeError [ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING]: A dynamic import callback was not specified.
4 | // require('v8-compile-cache');
5 | require('../dist/cli');
6 |
--------------------------------------------------------------------------------
/bin/forkedDev.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | require('../dist/service/forkedDev');
4 |
--------------------------------------------------------------------------------
/compiled/@umijs/plugins/package.json:
--------------------------------------------------------------------------------
1 | {"name":"@umijs/plugins","version":"4.0.32","authors":["chencheng (https://github.com/sorrycc)"],"license":"MIT","_lastModified":"2022-11-20T09:58:00.334Z"}
--------------------------------------------------------------------------------
/crates/swc_plugin_react_demo/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "swc_plugin_react_demo"
3 | version = "0.0.0"
4 | edition = "2021"
5 |
6 | [lib]
7 | crate-type = ["cdylib"]
8 |
9 | [dependencies]
10 | swc_core = { version = "6.0.1", features = [
11 | "ecma_plugin_transform",
12 | "ecma_visit",
13 | "ecma_ast",
14 | "common"
15 | ]}
16 |
--------------------------------------------------------------------------------
/crates/swc_plugin_react_demo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "swc_plugin_react_demo",
3 | "version": "0.0.0",
4 | "private": true,
5 | "description": "swc plugin for transform dumi react demo",
6 | "license": "MIT",
7 | "author": "Peach "
8 | }
9 |
--------------------------------------------------------------------------------
/crates/swc_plugin_react_demo/src/utils.rs:
--------------------------------------------------------------------------------
1 | use swc_core::{
2 | common::{Span, DUMMY_SP}, ecma::ast::{Expr, IdentName, KeyValueProp, ObjectLit, Prop, PropName, PropOrSpread, ReturnStmt, Stmt}
3 | };
4 |
5 | /**
6 | * create a return statement with a default property
7 | */
8 | pub fn create_return_with_default(e: Expr, s: Span) -> Stmt {
9 | Stmt::Return(ReturnStmt {
10 | span: s,
11 | arg: Some(Box::new(Expr::Object(ObjectLit {
12 | span: DUMMY_SP,
13 | props: vec![PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
14 | key: PropName::Ident(IdentName::new("default".into(), DUMMY_SP)),
15 | value: Box::new(e),
16 | })))],
17 | }))),
18 | })
19 | }
20 |
--------------------------------------------------------------------------------
/docs/config/env.md:
--------------------------------------------------------------------------------
1 | ---
2 | toc: content
3 | group: 框架配置
4 | ---
5 |
6 | # 环境变量配置
7 |
8 |
9 |
10 | ### DUMI_CACHE
11 |
12 | 默认会将框架的高消耗执行结果(例如编译 Markdown 文件)持久化存储到本地,该结果会在二次执行时被选择性复用以提升框架运行速度,设置为 `none` 时可以禁用该行为,通常用于开发自定义 Markdown 插件的调试环节。
13 |
14 | ### DUMI_THEME
15 |
16 | 指定主题包路径,优先级高于项目 `package.json` 中声明的 `dumi-theme-xx` 主题包依赖;该路径下存在主题包 `package.json` 和 `dist` 产物,通常用于 dumi 插件强制指定主题包或基于 dumi 二次开发的场景,非前述场景不建议使用。
17 |
18 | ```bash
19 | $ DUMI_THEME=./path/to/theme1 dumi dev
20 | ```
21 |
--------------------------------------------------------------------------------
/docs/guide/auto-api-table.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: 自动 API 表格
3 | group: 进阶
4 | order: 2
5 | ---
6 |
7 | # 自动 API 表格实验性
8 |
9 | :::warning{title="功能限制"}
10 | 由于该特性仍在实验性阶段,存在如下限制,未来可能会有变动或改进:
11 |
12 | - API 表格仅支持展示解析出的顶层属性
13 | - `@description` 不支持多语言配置
14 | - Windows 操作系统上会工作不正常,目前不建议使用
15 | :::
16 |
17 | dumi 支持基于 JSDoc 及 TypeScript 类型定义自动为 React 组件生成 API 表格,降低 API 文档的维护成本。
18 |
19 | ## 使用方式
20 |
21 | 首先在 `.dumirc.ts` 中打开 `apiParser` 的配置项,并配置 `entryFile` 的路径:
22 |
23 | ```ts
24 | import { defineConfig } from 'dumi';
25 |
26 | export default defineConfig({
27 | apiParser: {},
28 | resolve: {
29 | // 配置入口文件路径,API 解析将从这里开始
30 | entryFile: './src/index.tsx',
31 | },
32 | });
33 | ```
34 |
35 | 然后在入口文件中导出组件模块,可以直接 export,也可以从其他模块 re-export,例如:
36 |
37 | ```tsx | pure
38 | // src/index.tsx
39 | import React, { type FC } from 'react';
40 |
41 | export const Foo: FC<{
42 | /**
43 | * @description 属性描述
44 | * @default "默认值"
45 | */
46 | title?: string;
47 | }> = ({ title }) => {title}
;
48 | ```
49 |
50 | 最后在 Markdown 文件中使用 API 组件即可:
51 |
52 | ```md
53 |
54 | ```
55 |
56 | 上述代码将会被渲染为:
57 |
58 | | 属性名 | 描述 | 类型 | 默认值 |
59 | | ------ | -------- | -------- | -------- |
60 | | title | 属性描述 | `string` | `默认值` |
61 |
62 | ## 注意事项
63 |
64 | 1. 默认值和必选是互斥的,所以如果属性是必选,`@default` 值将不会生效
65 | 2. 仅支持解析入口文件导出的 React 组件,不支持指定文件解析
66 | 3. `API` 组件必须使用双标签,对 Markdown 而言双标签才是合法的 HTML
67 |
--------------------------------------------------------------------------------
/docs/guide/commands.md:
--------------------------------------------------------------------------------
1 | ---
2 | toc: content
3 | group: 其他
4 | order: 1
5 | ---
6 |
7 | # 命令行
8 |
9 | ## `dumi dev`
10 |
11 | 启动本地开发服务器,进行项目的开发与调试。
12 |
13 | ## `dumi build`
14 |
15 | 构建 dumi 文档产物,适用于生产环境的部署。
16 |
17 | ## `dumi preview`
18 |
19 | 在本地启动一个静态 Web 服务器,将 docs-dist 文件夹运行在 http://127.0.0.1:4172, 用于预览构建后产物。
20 |
21 | 你可以通过 `--port` 参数来配置服务的运行端口。
22 |
23 | ```bash
24 | dumi preview --port 9527
25 | ```
26 |
27 | 现在 `preview` 命令会将服务器运行在 http://127.0.0.1:9527
28 |
29 | ## `dumi setup`
30 |
31 | 初始化项目,会做临时文件的生成等操作。通常在 package.json 的 `scripts.prepare` 里设置。
32 |
33 | ```json
34 | {
35 | "scripts": {
36 | "prepare": "dumi setup"
37 | }
38 | }
39 | ```
40 |
41 | ## `dumi version`
42 |
43 | 查看 dumi 版本,等同于 `dumi -v`。
44 |
--------------------------------------------------------------------------------
/docs/guide/demos/cols.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export default () => <>Demo 分栏演示>;
4 |
--------------------------------------------------------------------------------
/docs/guide/i18n.md:
--------------------------------------------------------------------------------
1 | ---
2 | group:
3 | title: 进阶
4 | order: 1
5 | order: 0
6 | ---
7 |
8 | # 多语言
9 |
10 | 当我们想为文档站点增加多语言支持时,只需要在 `.dumirc.ts` 通过 [locales](../config/index.md#locales) 增加多语言配置,例如:
11 |
12 | ```ts
13 | export default {
14 | locales: [
15 | { id: 'zh-CN', name: '中文' },
16 | { id: 'en-US', name: 'EN' },
17 | ],
18 | };
19 | ```
20 |
21 | 然后我们以约定式的形式创建多语言的 Markdown 文件即可实现多语言支持,例如:
22 |
23 | ```bash
24 | .
25 | └── docs
26 | ├── index.md # 已有的中文版首页
27 | └── index.en-US.md # 新创建的英文版首页
28 | ```
29 |
30 | 此时,假定我们的网站域名是 `www.example.com`,那么访问 `www.example.com` 时则会渲染 `index.md` 的内容,访问 `www.example.com/en-US` 时则会渲染 `index.en-US.md` 的内容;同时,导航栏的右侧也会出现多语言切换的按钮(两种语言时)或下拉菜单(超过两种语言时),可供用户自由切换站点语言。
31 |
32 | 更多关于多语言配置项的细节,可参考 [locales](../config/index.md#locales) 配置项。
33 |
--------------------------------------------------------------------------------
/docs/guide/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | nav:
3 | title: 指南
4 | order: -1
5 | group:
6 | title: 介绍
7 | order: -1
8 | ---
9 |
10 | # 什么是 dumi
11 |
12 | dumi,中文发音**嘟米**,是一款为组件开发场景而生的静态站点框架,与 [father](https://github.com/umijs/father) 一起为开发者提供一站式的组件开发体验,**father 负责组件源码构建,而 dumi 负责组件开发及组件文档生成**。
13 |
14 | ## 特性
15 |
16 | 全新的 dumi 2.0 主要具备以下特性:
17 |
18 | - 🚀 **更好的编译性能**:通过结合使用 Umi 4 MFSU、esbuild、SWC、持久缓存等方案,带来比 dumi 1.x 更快的编译速度
19 | - 🔍 **内置全文搜索**:不需要接入任何三方服务,标题、正文、demo 等内容均可被搜索,支持多关键词搜索,且不会带来产物体积的增加
20 | - 🎨 **全新主题系统**:为主题包增加插件、国际化等能力的支持,且参考 Docusaurus 为主题用户提供局部覆盖能力,更强更易用
21 | - 🚥 **约定式路由增强**:通过拆分路由概念、简化路由配置等方式,让路由生成一改 dumi 1.x 的怪异、繁琐,更加符合直觉
22 | - 💡 **资产元数据 2.0**:在 1.x 及 JSON Schema 的基础上对资产属性定义结构进行全新设计,为资产的流通提供更多可能
23 | - 💎 **继续为组件研发而生**:提供与全新的 NPM 包研发工具 father 4 集成的脚手架,为开发者提供一站式的研发体验
24 |
25 | ## 问题反馈
26 |
27 | 如果在使用过程中发现任何问题、或者有改善建议,欢迎在 GitHub Issues 进行反馈:https://github.com/umijs/dumi/issues
28 |
29 | 或加入讨论群:
30 |
31 |
32 |

33 |
34 |
--------------------------------------------------------------------------------
/docs/guide/initialize.md:
--------------------------------------------------------------------------------
1 | ---
2 | nav: 指南
3 | group:
4 | title: 基础
5 | order: 0
6 | order: 0
7 | ---
8 |
9 | # 初始化
10 |
11 | ## 环境准备
12 |
13 | 确保正确安装 [Node.js](https://nodejs.org/en/) 且版本为 14+ 即可。
14 |
15 | ```bash
16 | $ node -v
17 | v14.19.1
18 | ```
19 |
20 | ## 脚手架
21 |
22 | ```bash
23 | # 先找个地方建个空目录。
24 | $ mkdir myapp && cd myapp
25 |
26 | # 通过官方工具创建项目,选择你需要的模板
27 | $ npx create-dumi
28 |
29 | # 选择一个模板
30 | $ ? Pick template type › - Use arrow-keys. Return to submit.
31 | $ ❯ Static Site # 用于构建网站
32 | $ React Library # 用于构建组件库,有组件例子
33 | $ Theme Package # 主题包开发脚手架,用于开发主题包
34 |
35 | # 安装依赖后启动项目
36 | $ npm start
37 | ```
38 |
--------------------------------------------------------------------------------
/docs/guide/mobile-library.md:
--------------------------------------------------------------------------------
1 | ---
2 | group: 进阶
3 | order: 2
4 | ---
5 |
6 | # 移动端组件研发
7 |
8 | ## 启用方式
9 |
10 | 与 dumi 1 一样,只需要安装移动端组件研发主题即可切换到移动端组件研发模式:
11 |
12 | ```bash
13 | $ npm i dumi-theme-mobile@^2.0.0 -D
14 | ```
15 |
16 | 然后可以通过 `themeConfig` 配置该主题包的行为:
17 |
18 | ```ts
19 | export default {
20 | themeConfig: {
21 | // 配置高清方案,默认为 750 高清方案
22 | hd: {
23 | rules: [...],
24 | },
25 | // 配置 demo 预览器的设备宽度,默认为 375px
26 | deviceWidth: 375,
27 | },
28 | }
29 | ```
30 |
31 | 关于该主题包的更多介绍及配置项使用方式,可以访问 [移动端组件研发主题](/theme/mobile) 了解更多。
32 |
33 | ## 效果预览
34 |
35 | ```tsx
36 | /**
37 | * title: demo 预览器示例
38 | * description: |
39 | * 和默认主题一样可以为 demo 配置介绍文案。
40 | *
41 | * 且相较 dumi 1 新增了 `compact`(配置为 `true` 时没有内边距)和 `background`(修改背景色)配置项的支持。
42 | */
43 | import React from 'react';
44 |
45 | export default () => (
46 |
57 | );
58 | ```
59 |
--------------------------------------------------------------------------------
/docs/guide/page-tab.$tab-example.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Tab 示例
3 | ---
4 |
5 | 这里是页面 Tab 的示例,内容在独立的 Markdown 中维护。
6 |
--------------------------------------------------------------------------------
/docs/guide/page-tab.md:
--------------------------------------------------------------------------------
1 | ---
2 | group: 进阶
3 | order: 1
4 | ---
5 |
6 | # 页面 Tab
7 |
8 | 为了便于开发者更优雅地组织复杂文档,比如将 API、示例、设计规范等内容组合呈现,dumi 提供了开箱即用的约定式页面 Tab 特性,以该篇文档为例:
9 |
10 | ```bash
11 | .
12 | └── docs
13 | └── guide
14 | ├── page-tab.md
15 | └── page-tab.$tab-example.md # Tab 示例内容
16 | ```
17 |
18 | dumi 约定同名 Markdown 文件的 `$tab-{key}` 修饰符该文档的 Tab 内容,如上所示,`page-tab.$tab-example.md` 会作为 `page-tab.md` 的 Tab 呈现。此处 `example` 作为 Tab 的 key 值,如果需要配置 Tab 标题,可以使用 FrontMatter 来定义,用法[同普通文档](../config/markdown.md):
19 |
20 | ```md
21 | ---
22 | title: Tab 示例
23 | ---
24 | ```
25 |
26 | 如此一来,我们就能得到和当前页面一样的 Tab 效果。
27 |
28 | 如果你是 dumi 的主题包或插件开发者,还可以通过插件 API 来为指定路由添加 Tab,具体用法请参考 [插件 API - addContentTab](../plugin/api.md#addcontenttab)。
29 |
--------------------------------------------------------------------------------
/docs/plugin/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | nav:
3 | title: 插件
4 | order: 1
5 | group: 介绍
6 | ---
7 |
8 | # 如何工作
9 |
10 | dumi 是基于 Umi 打造的静态站点框架,所以同时也继承了 Umi 强大的插件机制,并在之上做了扩展,以满足更多的场景需求。
11 |
12 |
13 |
--------------------------------------------------------------------------------
/docs/plugin/market.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: 插件市场
3 | group: 介绍
4 | order: 4
5 | toc: null
6 | ---
7 |
8 |
9 |
10 | 阅读更多关于 [如何使用插件](./index.md#配置),或 [如何编写属于你自己的插件](./new.md) 并与社区分享!
11 |
12 | ## 官方插件
13 |
14 | - [**preset-vue**](https://github.com/umijs/dumi/tree/master/suites/preset-vue): Vue 插件,支持 Vue Demo 渲染及编辑,API Table 等
15 |
16 | ## 社区插件
17 |
18 | - [**color-chunk**](https://github.com/Wxh16144/dumi-plugin-color-chunk#readme): 美化内联代码颜色块儿 \`#f00\`。
19 | - TBD: [提交我的插件](https://github.com/umijs/dumi/edit/master/docs/plugin/market.md)
20 |
21 | ## 更多插件
22 |
23 | 查找 [NPM 上所有可用的插件](https://www.npmjs.com/search?q=keywords%3Adumi-plugin)。
24 |
--------------------------------------------------------------------------------
/docs/plugin/new.md:
--------------------------------------------------------------------------------
1 | ---
2 | group: 开发
3 | ---
4 |
5 | # 创建插件
6 |
7 | 如果你希望在 dumi 里使用插件,有 3 种方式:
8 |
9 | 1. 本地插件:创建 `.dumi/theme/plugin.ts` 即可,适用于对本地项目做定制,且不需要与其他项目共享的场景
10 | 2. 主题插件:在主题包内创建 `src/plugin.ts` 或 `src/plugin/index.ts`,适用于插件和主题包结合使用的场景
11 | 3. 独立插件(集):发布到 NPM 并在 dumi 项目中通过 `plugins`/`presets` 配置启用,适用于插件(集)独立运行且希望与其他项目共享的场景
12 |
13 | 如果是创建独立插件,请在给 NPM 包命名时遵循以下原则:
14 |
15 | 1. 如果是 Umi/dumi 通用型插件,建议以 `umi-plugin-` 或 `@org/umi-plugin-` 开头命名
16 | 2. 如果是 dumi 专用插件,例如用到了 [插件 API - 重点方法](./api.md#重点方法),或者其他 dumi 特有能力,建议以 `dumi-plugin-` 或 `@org/dumi-plugin-` 开头命名
17 |
18 | 如果是创建独立插件集,将上述命名规则中的 `plugin` 换成 `preset` 即可,例如 `dumi-preset-bar`。
19 |
20 | ## 快速上手
21 |
22 | ### 本地插件
23 |
24 | 创建 `.dumi/theme/plugin.ts` 后根据需要编写逻辑即可,不需要做任何配置,该插件文件会被 dumi 自动挂载。
25 |
26 | ### 主题插件
27 |
28 | 使用主题包脚手架初始化的项目中已经包含 `src/plugin/index.ts` 文件,根据需要编写逻辑即可,该插件文件会跟随主题包自动被加载。更多主题包开发相关内容可参考 [主题开发](../theme/index.md) 文档。
29 |
30 | ### 独立插件(集)
31 |
32 | 推荐使用 father 脚手架初始化插件项目:
33 |
34 | ```bash
35 | $ npx create-father
36 | ```
37 |
38 | 上述命令会询问项目的目标运行平台,如果是纯 Node.js 插件,请选择『Node.js』,如果插件还包含一些浏览器端的模块,例如 React 组件,可以选择『Both』,初始化完成后根据需要编写逻辑即可。
39 |
40 | 可参考 [father - 开发](https://github.com/umijs/father/blob/master/docs/guide/dev.md) 文档了解如何实时编译插件源码并 link 到项目中做调试,link 完成后请将插件配置到测试项目的 `.dumirc.ts` 中做验证:
41 |
42 | ```ts
43 | // .dumirc.ts
44 | export default {
45 | plugins: ['dumi-plugin-bar'],
46 | };
47 | ```
48 |
--------------------------------------------------------------------------------
/examples/mobile/.dumirc.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | themeConfig: { deviceWidth: 414 },
3 | mfsu: false,
4 | };
5 |
--------------------------------------------------------------------------------
/examples/mobile/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@examples/mobile",
3 | "private": true,
4 | "scripts": {
5 | "build": "node ../../bin/dumi.js build",
6 | "dev": "node ../../bin/dumi.js dev",
7 | "list": "node ../../bin/dumi.js plugin list",
8 | "preview": "node ../../bin/dumi.js preview",
9 | "setup": "node ../../bin/dumi.js setup",
10 | "start": "npm run dev"
11 | },
12 | "dependencies": {
13 | "dumi-theme-mobile": "workspace:*",
14 | "react": "^18.2.0"
15 | },
16 | "devDependencies": {
17 | "typescript": "~4.7.4"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/examples/mobile/src/.gitignore:
--------------------------------------------------------------------------------
1 | .dumi/tmp
2 | .dumi/tmp-production
3 |
--------------------------------------------------------------------------------
/examples/mobile/src/Foo/index.md:
--------------------------------------------------------------------------------
1 | ```jsx
2 | /**
3 | * title: 有标题
4 | * description: 有简介,且有背景色 + 紧凑模式
5 | * compact: true
6 | * background: '#f7f9fb'
7 | */
8 | export default () => 'Hello Foo!';
9 | ```
10 |
11 | ```jsx
12 | /**
13 | * title: 只有标题
14 | */
15 | export default () => 'Hello Foo12313121!';
16 | ```
17 |
18 | ```jsx
19 | /**
20 | * title: 调试 demo
21 | * debug: true
22 | */
23 | export default () => 'Hello Foo12313121!';
24 | ```
25 |
26 | ```jsx
27 | export default () => 'Hello Foo12313121!';
28 | ```
29 |
--------------------------------------------------------------------------------
/examples/mobile/src/Foo/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { type FC } from 'react';
2 |
3 | const Foo: FC<{
4 | /**
5 | * @description 标题
6 | */
7 | title: string;
8 | /**
9 | * @description 顺序
10 | */
11 | order?: number;
12 | }> = (props) => {
13 | return <>{props.title}>;
14 | };
15 |
16 | export default Foo;
17 |
--------------------------------------------------------------------------------
/examples/mobile/src/PCFoo/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | mobile: false
3 | ---
4 |
5 | 指定 PC 模式渲染
6 |
7 | ```jsx
8 | export default () => 'Hello Foo!';
9 | ```
10 |
--------------------------------------------------------------------------------
/examples/mobile/src/index.ts:
--------------------------------------------------------------------------------
1 | export { default as Foo } from './Foo';
2 |
--------------------------------------------------------------------------------
/examples/normal/.dumi/pages/loader-test.tsx:
--------------------------------------------------------------------------------
1 | // Customize Page for dumi test
2 | export default () => 'Customize Dumi Test Page';
3 |
--------------------------------------------------------------------------------
/examples/normal/.dumi/pages/nothing.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * title: Nothing
3 | */
4 |
5 | export default () => '自定义 React 页面测试';
6 |
--------------------------------------------------------------------------------
/examples/normal/.dumi/theme/plugin.ts:
--------------------------------------------------------------------------------
1 | export default (api) => {
2 | api.describe({ key: 'example:normal:test' });
3 | };
4 |
--------------------------------------------------------------------------------
/examples/normal/.dumirc.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | locales: [
3 | { id: 'zh-CN', name: '中文' },
4 | { id: 'en-US', name: 'EN' },
5 | ],
6 | mako: {},
7 | ssr: { builder: 'mako' },
8 |
9 | themeConfig: { name: '示例' },
10 | mfsu: false,
11 | apiParser: {},
12 | resolve: { entryFile: './src/index.ts' },
13 | };
14 |
--------------------------------------------------------------------------------
/examples/normal/.fatherrc.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | esm: {},
3 | };
4 |
--------------------------------------------------------------------------------
/examples/normal/a.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export default () => <>Hi, hello>;
4 |
--------------------------------------------------------------------------------
/examples/normal/docs/a/b/c.md:
--------------------------------------------------------------------------------
1 | ---
2 | nav:
3 | title: 'nav title'
4 | second:
5 | title: 'second title'
6 | ---
7 |
8 | doc/a/b/c.md
9 |
--------------------------------------------------------------------------------
/examples/normal/docs/a/b/d.md:
--------------------------------------------------------------------------------
1 | ---
2 | nav:
3 | title: 'nav title'
4 | second:
5 | title: 'second title2'
6 | ---
7 |
8 | doc/a/b/d.md
9 |
--------------------------------------------------------------------------------
/examples/normal/docs/demo.less:
--------------------------------------------------------------------------------
1 | body {
2 | /* 外部文件测试 */
3 | }
4 |
--------------------------------------------------------------------------------
/examples/normal/docs/demo.tsx:
--------------------------------------------------------------------------------
1 | import './demo.less';
2 |
3 | export default () => 'Hello first external demo!';
4 |
--------------------------------------------------------------------------------
/examples/normal/docs/hello/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | nav: Hello
3 | group: 测试分组
4 | ---
5 |
6 | ## This is hello/index.md
7 |
8 | 约定式导航、分组测试
9 |
--------------------------------------------------------------------------------
/examples/normal/docs/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | hero:
3 | title: Umi
4 | description: 企业级前端开发框架
5 | actions:
6 | - text: 快速上手
7 | link: /hello
8 | - text: GitHub
9 | link: /hello
10 | features:
11 | - title: 非常快
12 | emoji: 🚀
13 | description: 考究的默认配置和约定式的目录结构,帮助开发者零成本上手,让所有注意力都能放在文档编写和组件开发上
14 | - title: 非常快
15 | emoji: 🚀
16 | description: 考究的默认配置和约定式的目录结构,帮助开发者零成本上手,让所有注意力都能放在文档编写和组件开发上
17 | - title: 非常快
18 | emoji: 🚀
19 | description: 考究的默认配置和约定式的目录结构,帮助开发者零成本上手,让所有注意力都能放在文档编写和组件开发上
20 | - title: 非常快
21 | emoji: 🚀
22 | description: 考究的默认配置和约定式的目录结构,帮助开发者零成本上手,让所有注意力都能放在文档编写和组件开发上
23 | - title: 非常快
24 | emoji: 🚀
25 | description: 考究的默认配置和约定式的目录结构,帮助开发者零成本上手,让所有注意力都能放在文档编写和组件开发上
26 | ---
27 |
28 | ## hello
29 |
30 | 代码块 demo
31 |
32 | ```jsx
33 | import React from 'react';
34 |
35 | export default () => <>Hello first code block demo!>;
36 | ```
37 |
38 | ## world
39 |
40 | 外部 demo
41 |
42 |
43 |
44 | 嵌入文档
45 |
46 |
47 |
48 | ---
49 |
50 | inline demo
51 |
52 | ```jsx | inline
53 | export default () => ;
54 | ```
55 |
56 | ---
57 |
58 | ## img
59 |
60 | 引入图片的相对路径
61 |
62 |
--------------------------------------------------------------------------------
/examples/normal/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umijs/dumi/9c73ff231ccaa1a73e75797c80e62c3c5fc63181/examples/normal/logo.png
--------------------------------------------------------------------------------
/examples/normal/mako.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "optimization": {
3 | "skipModules": false,
4 | "concatenateModules": false
5 | },
6 | "moduleIdStrategy": "named"
7 | }
8 |
--------------------------------------------------------------------------------
/examples/normal/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@examples/normal",
3 | "private": true,
4 | "scripts": {
5 | "build": "node ../../bin/dumi.js build",
6 | "dev": "node ../../bin/dumi.js dev",
7 | "preview": "node ../../bin/dumi.js preview",
8 | "setup": "node ../../bin/dumi.js setup",
9 | "start": "npm run dev"
10 | },
11 | "dependencies": {
12 | "antd": "^5.0.0",
13 | "react": "^18.3.1",
14 | "react-dom": "^18.3.1"
15 | },
16 | "devDependencies": {
17 | "typescript": "~4.7.4"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/examples/normal/plugin.ts:
--------------------------------------------------------------------------------
1 | import { IApi } from '../..';
2 |
3 | export default (api: IApi) => {
4 | api.addContentTab(() => ({
5 | key: 'test',
6 | component: require.resolve('./a.tsx'),
7 | }));
8 | };
9 |
--------------------------------------------------------------------------------
/examples/normal/src/.gitignore:
--------------------------------------------------------------------------------
1 | .dumi/tmp
2 | .dumi/tmp-production
3 |
--------------------------------------------------------------------------------
/examples/normal/src/Foo/demo/work.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * title: 我是标题
3 | * description: 我是简介,我可以用 `Markdown` 来编写
4 | */
5 | import { Button, Space } from 'antd';
6 | import React from 'react';
7 |
8 | export default () => (
9 |
10 | External Demo Block
11 |
12 |
13 | );
14 |
--------------------------------------------------------------------------------
/examples/normal/src/Foo/index.$tab-api.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: API
3 | ---
4 |
5 | 约定式 Tab 测试 + 自动 API 测试
6 |
7 |
8 |
--------------------------------------------------------------------------------
/examples/normal/src/Foo/index.en-US.md:
--------------------------------------------------------------------------------
1 | English component route
2 |
--------------------------------------------------------------------------------
/examples/normal/src/Foo/index.md:
--------------------------------------------------------------------------------
1 | 组件路由测试
2 |
3 | ```jsx
4 | import React from 'react';
5 |
6 | export default () => Hello Foo!
;
7 | ```
8 |
9 | 你好,Foo!
10 |
11 | 示例框
12 |
--------------------------------------------------------------------------------
/examples/normal/src/Foo/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { type FC } from 'react';
2 |
3 | interface A {
4 | a: string;
5 | }
6 |
7 | const Foo: FC<{
8 | /**
9 | * @description 标题
10 | * @default "标题"
11 | */
12 | title?: string;
13 | /**
14 | * @description 顺序
15 | */
16 | order?: number;
17 | a: A[];
18 | b: { c?: string };
19 | c: '1' | '2' | '3';
20 | d: 1 | 2 | 3;
21 | e: A | 1;
22 | f: { g?: string }[];
23 | onClick: (e?: MouseEvent) => void;
24 | children: React.ReactNode;
25 | onConfirm: (output: { children: any[] }) => void;
26 | dom: HTMLElement;
27 | // eslint-disable-next-line @typescript-eslint/ban-types
28 | func: Function;
29 | }> = (props) => {
30 | return <>{props.title}>;
31 | };
32 |
33 | export default Foo;
34 |
--------------------------------------------------------------------------------
/examples/normal/src/index.ts:
--------------------------------------------------------------------------------
1 | export { default as Foo } from './Foo';
2 |
--------------------------------------------------------------------------------
/examples/temp/b.md:
--------------------------------------------------------------------------------
1 | abc
2 | 123
3 | 456
4 |
5 | ---
6 |
7 | # []()
8 |
9 | 11111
10 |
--------------------------------------------------------------------------------
/examples/temp/docs/a.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/examples/vue/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | parser: 'vue-eslint-parser',
4 | parserOptions: {
5 | parser: '@typescript-eslint/parser',
6 | sourceType: 'module',
7 | ecmaVersion: 2020,
8 | ecmaFeatures: {
9 | jsx: true,
10 | },
11 | },
12 | extends: ['plugin:vue/vue3-recommended'],
13 | rules: {},
14 | };
15 |
--------------------------------------------------------------------------------
/examples/vue/.fatherrc.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'father';
2 |
3 | export default defineConfig({
4 | esm: {
5 | transformer: 'babel',
6 | },
7 | });
8 |
--------------------------------------------------------------------------------
/examples/vue/README.md:
--------------------------------------------------------------------------------
1 | # @examples/vue
2 |
--------------------------------------------------------------------------------
/examples/vue/docs/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | hero:
3 | title: Vue.js Support
4 | description: A demo about Vue SFC and JSX
5 | actions:
6 | - text: SFC
7 | link: /components/foo
8 | - text: JSX
9 | link: /components/button
10 | features:
11 | - title: SFC
12 | emoji: 🤟
13 | description: Put hello description here
14 | - title: JSX
15 | emoji: ⚛️
16 | description: Put world description here
17 | - title: TSX
18 | emoji: ⚛️
19 | description: Put ! description here
20 | ---
21 |
--------------------------------------------------------------------------------
/examples/vue/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@examples/vue",
3 | "private": true,
4 | "description": "A Vue3 component library",
5 | "repository": {
6 | "type": "git",
7 | "url": "https://github.com/umijs/dumi",
8 | "directory": "examples/vue"
9 | },
10 | "license": "MIT",
11 | "scripts": {
12 | "build": "node ../../bin/dumi.js build",
13 | "dev": "node ../../bin/dumi.js dev",
14 | "preview": "node ../../bin/dumi.js preview",
15 | "setup": "node ../../bin/dumi.js setup",
16 | "start": "npm run dev"
17 | },
18 | "dependencies": {
19 | "dayjs": "^1.11.10",
20 | "element-plus": "^2.3.14",
21 | "pinia": "^2.1.7",
22 | "react": "^18.2.0",
23 | "vue": "3.4.28"
24 | },
25 | "devDependencies": {
26 | "@dumijs/preset-vue": "workspace:*",
27 | "@typescript-eslint/parser": "^6.7.2",
28 | "eslint-plugin-vue": "^9.17.0",
29 | "typescript": "~4.7.4",
30 | "unplugin-auto-import": "^0.16.6",
31 | "unplugin-element-plus": "^0.8.0",
32 | "unplugin-vue-components": "^0.25.2"
33 | },
34 | "authors": []
35 | }
36 |
--------------------------------------------------------------------------------
/examples/vue/src/Button/button.less:
--------------------------------------------------------------------------------
1 | .btn {
2 | --btn-color: rgb(93, 93, 245);
3 |
4 | padding: 10px;
5 | min-width: 100px;
6 | height: 50px;
7 | border: 1px solid var(--btn-color);
8 | border-radius: 50px;
9 | font-size: 16px;
10 | background: var(--btn-color);
11 | color: #fff;
12 | }
13 |
--------------------------------------------------------------------------------
/examples/vue/src/Button/demos/Demo.tsx:
--------------------------------------------------------------------------------
1 | import { Button } from '@examples/vue';
2 | import { defineComponent, ref } from 'vue';
3 | import './demo.less';
4 |
5 | export default defineComponent({
6 | setup() {
7 | const count = ref(0);
8 | const handleClick = (e: Event) => {
9 | count.value++;
10 | };
11 | return () => (
12 |
13 |
16 |
17 | );
18 | },
19 | });
20 |
--------------------------------------------------------------------------------
/examples/vue/src/Button/demos/demo.less:
--------------------------------------------------------------------------------
1 | .demo {
2 | padding: 100px;
3 | background: aliceblue;
4 | }
5 |
--------------------------------------------------------------------------------
/examples/vue/src/Foo/demos/sfc-demo.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
15 |
16 |
22 |
--------------------------------------------------------------------------------
/examples/vue/src/Foo/index.tsx:
--------------------------------------------------------------------------------
1 | import { defineComponent } from 'vue';
2 |
3 | export default defineComponent({
4 | inheritAttrs: false,
5 | props: {
6 | /**
7 | * @description 标题
8 | */
9 | title: {
10 | type: String,
11 | default: '',
12 | },
13 | },
14 | setup({ title }) {
15 | return () => {title}
;
16 | },
17 | });
18 |
--------------------------------------------------------------------------------
/examples/vue/src/functional.tsx:
--------------------------------------------------------------------------------
1 | import { FunctionalComponent as FC } from 'vue';
2 | export interface ArticleProps {
3 | /**
4 | * 文章标题
5 | * @beta
6 | * @default "Functional Component Demo"
7 | */
8 | title?: string;
9 | /**
10 | * 文章描述
11 | * @since 0.0.1
12 | * @default "No Desc here"
13 | */
14 | desc?: string;
15 |
16 | /**
17 | * 点击事件
18 | * @since 0.0.1
19 | */
20 | onClick?: (e: Event) => void;
21 | }
22 |
23 | export interface ArticleSlots {
24 | default?: any;
25 | }
26 |
27 | const Article: FC = function (
28 | props,
29 | { slots },
30 | ) {
31 | return (
32 |
33 | {props.title}
34 | {props.desc}
35 | {slots.default && {slots.default()}
}
36 |
37 | );
38 | };
39 |
40 | Article.props = {
41 | title: {
42 | type: String,
43 | required: false,
44 | default: 'Functional Component Demo',
45 | },
46 | desc: {
47 | type: String,
48 | required: false,
49 | default: 'No Desc here',
50 | },
51 | };
52 |
53 | export default Article;
54 |
--------------------------------------------------------------------------------
/examples/vue/src/index.ts:
--------------------------------------------------------------------------------
1 | import { h } from 'vue';
2 |
3 | export { default as Button } from './Button';
4 | export { default as Foo } from './Foo';
5 | export * from './List';
6 | export { default as Article } from './functional';
7 | export { default as Badge } from './my-badge.vue';
8 |
9 | export function useVNode() {
10 | return h('div');
11 | }
12 |
--------------------------------------------------------------------------------
/examples/vue/src/my-badge.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 | {{ icon }}
7 |
8 |
9 |
33 |
--------------------------------------------------------------------------------
/examples/vue/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "baseUrl": "./",
5 | "paths": {
6 | "@examples/vue": ["./src"]
7 | },
8 | "allowJs": true,
9 | "jsx": "preserve",
10 | "jsxImportSource": "vue",
11 | "strictNullChecks": false
12 | },
13 | "include": [".dumirc.ts", "src/**/*", "docs/**/*"]
14 | }
15 |
--------------------------------------------------------------------------------
/examples/vue/tsconfig.vue.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "allowJs": true,
5 | "jsx": "preserve",
6 | "jsxImportSource": "vue",
7 | "strictNullChecks": false
8 | },
9 | "include": ["src/**/*", "docs/**/*"]
10 | }
11 |
--------------------------------------------------------------------------------
/index.d.ts:
--------------------------------------------------------------------------------
1 | export * from '@@/dumi/exports';
2 | export { Root as HastRoot } from 'hast';
3 | export * from 'umi';
4 | export {
5 | Plugin as UnifiedPlugin,
6 | Transformer as UnifiedTransformer,
7 | } from 'unified';
8 | export * from './dist';
9 | // override umi exported defineConfig
10 | export { defineConfig } from './dist';
11 | export { IApi } from './dist/types';
12 |
--------------------------------------------------------------------------------
/mako.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "watch": {
3 | "ignorePaths": ["examples", "suites"]
4 | },
5 | "moduleIdStrategy": "named",
6 | "optimization": {
7 | "skipModules": false,
8 | "concatenateModules": false
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/plugin-utils.d.ts:
--------------------------------------------------------------------------------
1 | export * from 'umi/plugin-utils';
2 |
--------------------------------------------------------------------------------
/plugin-utils.js:
--------------------------------------------------------------------------------
1 | module.exports = require('umi/plugin-utils');
2 |
--------------------------------------------------------------------------------
/pnpm-workspace.yaml:
--------------------------------------------------------------------------------
1 | packages:
2 | - '.'
3 | - 'assets-types'
4 | - 'suites/*'
5 | - 'examples/*'
6 |
--------------------------------------------------------------------------------
/rust-toolchain:
--------------------------------------------------------------------------------
1 | nightly
2 |
--------------------------------------------------------------------------------
/rustfmt.toml:
--------------------------------------------------------------------------------
1 | tab_spaces = 2
2 |
--------------------------------------------------------------------------------
/scripts/miscUtil.ts:
--------------------------------------------------------------------------------
1 | import { execa } from '@umijs/utils';
2 | import path from 'path';
3 |
4 | interface IPackage {
5 | name: string;
6 | version: string;
7 | private: boolean;
8 | path: string;
9 | dependencies?: Record;
10 | devDependencies?: Record;
11 | [key: string]: any;
12 | }
13 |
14 | export const workspaces: IPackage[] = (function () {
15 | try {
16 | const { stdout } = execa.execaSync(
17 | 'pnpm',
18 | ['recursive', 'list', '--json'],
19 | {
20 | cwd: path.join(__dirname, '..'),
21 | },
22 | );
23 | return JSON.parse(stdout);
24 | } catch (error) {
25 | return [];
26 | }
27 | })();
28 |
29 | export const getExamples = () => {
30 | const __examplesPath = path.join(__dirname, '../examples');
31 | return workspaces.filter((pkg) => {
32 | return (pkg?.path || '').startsWith(__examplesPath);
33 | });
34 | };
35 |
36 | export function getPublishPackages() {
37 | return workspaces.filter((pkg) => {
38 | return [
39 | !pkg.private, // ignore private packages
40 | !['create-dumi'].includes(pkg.name), // ignore by name
41 | ].every(Boolean);
42 | });
43 | }
44 |
--------------------------------------------------------------------------------
/scripts/pkg-pr-new.ts:
--------------------------------------------------------------------------------
1 | import { execa } from '@umijs/utils';
2 | import path from 'path';
3 | import { getExamples, getPublishPackages } from './miscUtil';
4 |
5 | const rootPath = path.join(__dirname, '..');
6 |
7 | function main() {
8 | // https://github.com/stackblitz-labs/pkg.pr.new
9 | const params = [
10 | // packages
11 | ...getPublishPackages().map((pkg) =>
12 | path.join(`.${path.sep}`, path.relative(rootPath, pkg.path)),
13 | ),
14 | // template
15 | ...getExamples().map(
16 | (example) => `--template=${path.relative(rootPath, example.path)}`,
17 | ),
18 | // more options
19 | '--pnpm',
20 | ];
21 |
22 | console.log('🚀 pkg-pr-new', params);
23 |
24 | execa.execaSync('npx', ['pkg-pr-new', 'publish', ...params], {
25 | stdio: 'inherit',
26 | cwd: rootPath,
27 | });
28 | }
29 |
30 | // \\\\\\\\\\\
31 | // \\\ main \\
32 | // \\\\\\\\\\\
33 | main();
34 |
--------------------------------------------------------------------------------
/scripts/pre-bundle-worker.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const { build } = require('@umijs/bundler-utils/compiled/esbuild');
3 |
4 | // use esbuild to pre-bundle worker
5 | // why not ncc?
6 | // because it only works for Node.js package
7 | build({
8 | entryPoints: ['src/client/theme-api/useSiteSearch/searchWorker'],
9 | absWorkingDir: path.dirname(__dirname),
10 | outfile: 'compiled/_internal/searchWorker.min.js',
11 | bundle: true,
12 | minify: true,
13 | logLevel: 'silent',
14 | target: 'es6',
15 | format: 'iife',
16 | });
17 |
--------------------------------------------------------------------------------
/scripts/vercel-install.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y -t wasm32-wasip1 --default-toolchain nightly
3 | source "$HOME/.cargo/env"
4 | pnpm i
5 |
--------------------------------------------------------------------------------
/src/assetParsers/__tests__/FakeParser.js:
--------------------------------------------------------------------------------
1 | const { createApiParser } = require('../../../tech-stack-utils');
2 |
3 | module.exports.FakeParser = createApiParser({
4 | filename: __filename,
5 | worker: class {
6 | patch() {}
7 | parse() {
8 | return new Promise((resolve) => {
9 | setTimeout(() => {
10 | resolve({
11 | components: {},
12 | functions: {},
13 | });
14 | }, 1000);
15 | });
16 | }
17 | async destroy() {}
18 | },
19 | // If the worker class has no parameters
20 | // entryFile and resolveDir must be passed in manually.
21 | parseOptions: {
22 | entryFile: __filename,
23 | resolveDir: __dirname,
24 | },
25 | });
26 |
--------------------------------------------------------------------------------
/src/assetParsers/__tests__/parser.fork.test.ts:
--------------------------------------------------------------------------------
1 | import { expect, test } from 'vitest';
2 | // @ts-ignore
3 | import { FakeParser } from './FakeParser';
4 |
5 | test('AtomAssetsParser: create worker mode', async () => {
6 | const parser = FakeParser();
7 | const now = performance.now();
8 | parser.parse().then((result: any) => {
9 | expect(result).toStrictEqual({
10 | components: {},
11 | functions: {},
12 | });
13 | });
14 | await parser.destroyWorker();
15 | expect(performance.now() - now).toBeGreaterThan(1000);
16 | });
17 |
--------------------------------------------------------------------------------
/src/cli.ts:
--------------------------------------------------------------------------------
1 | import { chalk, logger } from 'umi/plugin-utils';
2 | import { run } from './service/cli';
3 |
4 | (async () => {
5 | try {
6 | logger.info(chalk.cyan.bold(`dumi v${require('../package').version}`));
7 | await run({
8 | presets: [require.resolve('./preset')],
9 | });
10 | } catch (e) {
11 | console.error(e);
12 | process.exit(1);
13 | }
14 | })();
15 |
--------------------------------------------------------------------------------
/src/client/misc/reactDemoCompiler.ts:
--------------------------------------------------------------------------------
1 | import { transform } from 'sucrase';
2 | import type { IDemoCompileFn } from '../theme-api/types';
3 |
4 | const compile: IDemoCompileFn = async (code) => {
5 | return transform(code, {
6 | transforms: ['typescript', 'jsx', 'imports'],
7 | }).code;
8 | };
9 |
10 | export default compile;
11 |
--------------------------------------------------------------------------------
/src/client/pages/404.ts:
--------------------------------------------------------------------------------
1 | // allow customize 404 via theme slots
2 | export { default } from 'dumi/theme/slots/NotFound';
3 |
--------------------------------------------------------------------------------
/src/client/pages/Demo/index.less:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | }
5 |
--------------------------------------------------------------------------------
/src/client/pages/Loading.ts:
--------------------------------------------------------------------------------
1 | // allow customize loading via theme slots or ./dumi/loading.tsx
2 | export { default } from 'dumi/theme/slots/Loading';
3 |
--------------------------------------------------------------------------------
/src/client/theme-api/DumiDemo/DemoErrorBoundary.tsx:
--------------------------------------------------------------------------------
1 | import Container from 'dumi/theme/builtins/Container';
2 | import React, { type FC, type ReactNode } from 'react';
3 | import { ErrorBoundary } from 'react-error-boundary';
4 |
5 | const DemoErrorBoundary: FC<{ children: ReactNode }> = (props) => (
6 | (
8 |
9 |
10 | {error.message || 'This demo has been crashed.'}
11 |
12 | {error.stack && (
13 |
14 |
15 | Error stack
16 | {error.stack}
17 |
18 |
19 | )}
20 |
21 | )}
22 | >
23 | {props.children}
24 |
25 | );
26 |
27 | export default DemoErrorBoundary;
28 |
--------------------------------------------------------------------------------
/src/client/theme-api/DumiPage.tsx:
--------------------------------------------------------------------------------
1 | import { useRouteMeta } from 'dumi';
2 | import ContentTabs from 'dumi/theme/slots/ContentTabs';
3 | import React, { useState, type FC, type ReactNode } from 'react';
4 | import { useTabQueryState } from './useTabMeta';
5 |
6 | export const DumiPage: FC<{ children: ReactNode }> = (props) => {
7 | const { tabs } = useRouteMeta();
8 | const [tabKey, setTabKey] = useTabQueryState();
9 | const [tab, setTab] = useState[0] | undefined>(() =>
10 | tabs?.find(({ key }) => key === tabKey),
11 | );
12 |
13 | return (
14 | <>
15 | {
19 | setTab(val);
20 | setTabKey(val?.key);
21 | }}
22 | />
23 | {tab ? React.createElement(tab.components.default) : props.children}
24 | >
25 | );
26 | };
27 |
--------------------------------------------------------------------------------
/src/client/theme-api/context.ts:
--------------------------------------------------------------------------------
1 | import type { PICKED_PKG_FIELDS } from '@/constants';
2 | import type { AtomComponentAsset } from 'dumi-assets-types';
3 | import { createContext, useContext } from 'react';
4 | import type { IDemoData, ILocalesConfig, IThemeConfig } from './types';
5 |
6 | export interface ISiteContext {
7 | pkg: Partial>;
8 | historyType: 'browser' | 'hash' | 'memory';
9 | entryExports: Record;
10 | demos: Record;
11 | components: Record;
12 | locales: ILocalesConfig;
13 | themeConfig: IThemeConfig;
14 | hostname?: string;
15 | loading: boolean;
16 | setLoading: (status: boolean) => void;
17 | /**
18 | * private field, do not use it in your code
19 | */
20 | _2_level_nav_available: boolean;
21 | }
22 |
23 | export const SiteContext = createContext({
24 | pkg: {},
25 | historyType: 'browser',
26 | entryExports: {},
27 | demos: {},
28 | components: {},
29 | locales: [],
30 | themeConfig: {} as IThemeConfig,
31 | loading: false,
32 | setLoading: () => {},
33 | _2_level_nav_available: true,
34 | });
35 |
36 | export const useSiteData = () => {
37 | return useContext(SiteContext);
38 | };
39 |
--------------------------------------------------------------------------------
/src/client/theme-api/index.ts:
--------------------------------------------------------------------------------
1 | export {
2 | FormattedDate,
3 | FormattedDateParts,
4 | FormattedDisplayName,
5 | FormattedList,
6 | FormattedMessage,
7 | FormattedNumber,
8 | FormattedNumberParts,
9 | FormattedPlural,
10 | FormattedRelativeTime,
11 | FormattedTime,
12 | FormattedTimeParts,
13 | IntlContext,
14 | IntlProvider,
15 | RawIntlProvider,
16 | createIntlCache,
17 | defineMessages,
18 | injectIntl,
19 | useIntl,
20 | } from 'react-intl';
21 | export { AtomRenderer } from './AtomRenderer';
22 | export { DumiDemo } from './DumiDemo';
23 | export { DumiDemoGrid } from './DumiDemoGrid';
24 | export { DumiPage } from './DumiPage';
25 | export { useSiteData } from './context';
26 | export { openCodeSandbox } from './openCodeSandbox';
27 | export { openStackBlitz } from './openStackBlitz';
28 | export type { IDemoCancelableFn, IPreviewerProps } from './types';
29 | export { useAtomAssets } from './useAtomAssets';
30 | export { useLiveDemo } from './useLiveDemo';
31 | export { useLocale } from './useLocale';
32 | export { useNavData } from './useNavData';
33 | export { usePrefersColor } from './usePrefersColor';
34 | export { useMatchedRoute, useRouteMeta } from './useRouteMeta';
35 | export { useFullSidebarData, useSidebarData } from './useSidebarData';
36 | export { useSiteSearch } from './useSiteSearch';
37 | export { useTabMeta } from './useTabMeta';
38 |
--------------------------------------------------------------------------------
/src/client/theme-api/useAtomAssets.ts:
--------------------------------------------------------------------------------
1 | import { useSiteData } from 'dumi';
2 | import { AtomComponentAsset } from 'dumi-assets-types';
3 |
4 | export const useAtomAssets = (): {
5 | components: Record;
6 | } => {
7 | const { components } = useSiteData();
8 |
9 | return { components };
10 | };
11 |
--------------------------------------------------------------------------------
/src/client/theme-api/useLocale.ts:
--------------------------------------------------------------------------------
1 | import { useIntl, useSiteData } from 'dumi';
2 | import { useState } from 'react';
3 | import type { ILocale } from './types';
4 |
5 | export const useLocale = (): ILocale => {
6 | const intl = useIntl();
7 | const { locales } = useSiteData();
8 | const [locale] = useState(
9 | () => locales.find(({ id }) => id === intl.locale)!,
10 | );
11 | return locale;
12 | };
13 |
--------------------------------------------------------------------------------
/src/client/theme-api/useTabMeta.ts:
--------------------------------------------------------------------------------
1 | import { history, useLocation, useRouteMeta, useSearchParams } from 'dumi';
2 | import { useCallback } from 'react';
3 | import type { IRouteMeta } from './types';
4 |
5 | export const TAB_QUERY_KEY = 'tab';
6 |
7 | export const useTabQueryState = (): [string | null, (val?: string) => void] => {
8 | const { pathname } = useLocation();
9 | const [params] = useSearchParams();
10 | const setTabQueryState = useCallback(
11 | (val?: string) => {
12 | if (val) params.set(TAB_QUERY_KEY, val);
13 | else params.delete(TAB_QUERY_KEY);
14 |
15 | history.push({
16 | pathname,
17 | search: `?${params.toString()}`,
18 | });
19 | },
20 | [params, pathname],
21 | );
22 |
23 | return [params.get(TAB_QUERY_KEY), setTabQueryState];
24 | };
25 |
26 | export const useTabMeta = ():
27 | | NonNullable[0]['meta']
28 | | undefined => {
29 | const { tabs } = useRouteMeta();
30 | const [tabKey] = useTabQueryState();
31 |
32 | return tabs?.find(({ key }) => tabKey === key)?.meta;
33 | };
34 |
--------------------------------------------------------------------------------
/src/client/theme-default/builtins/API/index.less:
--------------------------------------------------------------------------------
1 | @import (reference) '../../styles/variables.less';
2 |
3 | .@{prefix}-api {
4 | &-type {
5 | [data-token='|'],
6 | [data-token='=>'] {
7 | padding: 0 0.5em;
8 | }
9 |
10 | [data-token=';'],
11 | [data-token=':'],
12 | [data-token=','],
13 | [data-token='async'] {
14 | padding-right: 0.5em;
15 | }
16 |
17 | [data-token='{'] {
18 | padding-right: 0.5em;
19 | }
20 |
21 | [data-token='}'] {
22 | padding-left: 0.5em;
23 | }
24 | }
25 |
26 | &-release {
27 | [data-release] + [data-release] {
28 | margin-left: 0.5em;
29 | }
30 | .@{prefix}-badge {
31 | transform: scale(0.9);
32 | }
33 | }
34 |
35 | &-release-name {
36 | &[data-release='deprecated'] {
37 | text-decoration: line-through;
38 | }
39 | }
40 |
41 | &-release-modifer {
42 | &[data-release='deprecated'] {
43 | cursor: help;
44 | border-bottom: 1px dotted @c-text-secondary;
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/client/theme-default/builtins/Badge/index.less:
--------------------------------------------------------------------------------
1 | @import (reference) '../../styles/variables.less';
2 |
3 | .@{prefix}-badge {
4 | display: inline-block;
5 | margin-inline-start: 2px;
6 | padding: 1px 8px;
7 | font-size: 12px;
8 | font-weight: 400;
9 | line-height: 20px;
10 | border-radius: 4px;
11 | vertical-align: top;
12 |
13 | &:not([type]),
14 | &[type='info'] {
15 | color: @c-primary;
16 | background: lighten(@c-primary, 40%);
17 |
18 | @{dark-selector} & {
19 | color: @c-primary;
20 | background: darken(@c-primary-dark, 20%);
21 | }
22 | }
23 |
24 | &[type='warning'] {
25 | color: @c-warning;
26 | background: lighten(@c-warning, 48%);
27 |
28 | @{dark-selector} & {
29 | color: @c-warning;
30 | background: darken(@c-warning-dark, 20%);
31 | }
32 | }
33 |
34 | &[type='success'] {
35 | color: @c-success;
36 | background: lighten(@c-success, 54%);
37 |
38 | @{dark-selector} & {
39 | color: @c-success;
40 | background: darken(@c-success-dark, 9%);
41 | }
42 | }
43 |
44 | &[type='error'] {
45 | color: @c-error;
46 | background: lighten(@c-error, 47%);
47 |
48 | @{dark-selector} & {
49 | color: @c-error;
50 | background: darken(@c-error-dark, 18%);
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/client/theme-default/builtins/Badge/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { type FC, type ReactNode } from 'react';
2 | import './index.less';
3 |
4 | const Badge: FC<{
5 | children: ReactNode;
6 | type: 'info' | 'warning' | 'error' | 'success';
7 | }> = (props) => ;
8 |
9 | export default Badge;
10 |
--------------------------------------------------------------------------------
/src/client/theme-default/builtins/CodeGroup/index.tsx:
--------------------------------------------------------------------------------
1 | import Tabs, { ITabsProps } from '@/client/theme-default/slots/Tabs';
2 | import SourceCode, { ISourceCodeProps } from 'dumi/theme/builtins/SourceCode';
3 | import toArray from 'rc-util/lib/Children/toArray';
4 | import React from 'react';
5 |
6 | type Unpacked = T extends (infer U)[] ? U : T;
7 | type Item = Unpacked['items']>;
8 |
9 | function CodeGroup(props: React.PropsWithChildren) {
10 | const { children } = props;
11 |
12 | const usefulChildren = toArray(children).filter(
13 | (child) =>
14 | typeof child === 'object' &&
15 | typeof child.type === 'function' &&
16 | child.type?.name === SourceCode.name,
17 | ) as React.ReactElement[];
18 |
19 | const items = usefulChildren.map- ((child, idx) => {
20 | const { lang, title } = child.props ?? {};
21 |
22 | return {
23 | key: String(child.key ?? idx),
24 | label: title || lang || 'txt', // fallback to txt if no lang and title
25 | children: child,
26 | };
27 | });
28 |
29 | return ;
30 | }
31 |
32 | export default CodeGroup;
33 |
--------------------------------------------------------------------------------
/src/client/theme-default/builtins/Container/index.tsx:
--------------------------------------------------------------------------------
1 | import { ReactComponent as IconSuccess } from '@ant-design/icons-svg/inline-svg/outlined/check-circle.svg';
2 | import { ReactComponent as IconError } from '@ant-design/icons-svg/inline-svg/outlined/close-circle.svg';
3 | import { ReactComponent as IconInfo } from '@ant-design/icons-svg/inline-svg/outlined/info-circle.svg';
4 | import { ReactComponent as IconWarning } from '@ant-design/icons-svg/inline-svg/outlined/warning.svg';
5 | import React, { useState, type FC, type ReactNode } from 'react';
6 | import './index.less';
7 |
8 | const ICONS = {
9 | info: IconInfo,
10 | warning: IconWarning,
11 | success: IconSuccess,
12 | error: IconError,
13 | };
14 |
15 | const Container: FC<{ type: string; title?: string; children: ReactNode }> = (
16 | props,
17 | ) => {
18 | const [Icon] = useState(() => ICONS[props.type as keyof typeof ICONS]);
19 |
20 | return (
21 |
22 |
23 |
{props.title || props.type.toUpperCase()}
24 |
25 |
26 | );
27 | };
28 |
29 | export default Container;
30 |
--------------------------------------------------------------------------------
/src/client/theme-default/builtins/Table/index.less:
--------------------------------------------------------------------------------
1 | @import (reference) '../../styles/variables.less';
2 |
3 | .@{prefix}-table {
4 | margin: 24px 0 32px;
5 | transform: translate(0, 0);
6 |
7 | &-content {
8 | overflow: auto;
9 |
10 | &::before,
11 | &::after {
12 | content: '';
13 | display: block;
14 | position: fixed;
15 | z-index: 1;
16 | top: 0;
17 | bottom: 0;
18 | width: 6px;
19 | pointer-events: none;
20 | }
21 |
22 | &[data-left-folded]::before {
23 | left: 0;
24 | background-image: linear-gradient(
25 | to right,
26 | rgba(0, 0, 0, 10%),
27 | rgba(0, 0, 0, 0%)
28 | );
29 |
30 | @{dark-selector} & {
31 | background-image: linear-gradient(
32 | to right,
33 | rgba(0, 0, 0, 50%),
34 | rgba(0, 0, 0, 0%)
35 | );
36 | }
37 | }
38 |
39 | &[data-right-folded]::after {
40 | right: 0;
41 | background-image: linear-gradient(
42 | to left,
43 | rgba(0, 0, 0, 10%),
44 | rgba(0, 0, 0, 0%)
45 | );
46 |
47 | @{dark-selector} & {
48 | background-image: linear-gradient(
49 | to left,
50 | rgba(0, 0, 0, 50%),
51 | rgba(0, 0, 0, 0%)
52 | );
53 | }
54 | }
55 |
56 | > table > thead > tr > th,
57 | > table > tr > th {
58 | white-space: nowrap;
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/client/theme-default/slots/ColorSwitch/index.less:
--------------------------------------------------------------------------------
1 | @import (reference) '../../styles/variables.less';
2 |
3 | .@{prefix}-color-switch {
4 | position: relative;
5 | font-size: 0;
6 | line-height: 0;
7 |
8 | @media screen and (max-width: 1430px) {
9 | &::before {
10 | left: auto;
11 | right: auto;
12 | inset-inline-end: -15px;
13 | transform: none;
14 |
15 | [class*='-switch'] + &,
16 | [class*='-select'] + & {
17 | inset-inline-end: 0;
18 | }
19 | }
20 | }
21 |
22 | [class*='-switch'] + &,
23 | [class*='-select'] + & {
24 | margin-inline-start: 15px;
25 | margin-inline-end: -15px;
26 | padding-inline: 15px;
27 | border-inline-start: 1px solid @c-border;
28 |
29 | @{dark-selector} & {
30 | border-inline-start-color: @c-border-dark;
31 | }
32 | }
33 |
34 | svg {
35 | width: 16px;
36 | fill: @c-text-secondary;
37 |
38 | @{dark-selector} & {
39 | fill: @c-text-secondary-dark;
40 | }
41 | }
42 |
43 | &:hover svg {
44 | fill: @c-primary;
45 |
46 | @{dark-selector} & {
47 | fill: @c-primary-dark;
48 | }
49 | }
50 |
51 | select {
52 | position: absolute;
53 | inset: 0 15%;
54 | opacity: 0;
55 | width: 100%;
56 | min-width: 16px;
57 | max-width: 70%;
58 | height: 16px;
59 | cursor: pointer;
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/client/theme-default/slots/Content/index.tsx:
--------------------------------------------------------------------------------
1 | import { useRouteMeta, useSidebarData, useSiteData } from 'dumi';
2 | import React, { type FC, type ReactNode } from 'react';
3 | import '../../styles/heti.less';
4 | import './index.less';
5 |
6 | const Content: FC<{ children: ReactNode }> = (props) => {
7 | const sidebar = useSidebarData();
8 | const { themeConfig } = useSiteData();
9 | const { frontmatter } = useRouteMeta();
10 |
11 | return (
12 |
17 | {props.children}
18 |
19 | );
20 | };
21 |
22 | export default Content;
23 |
--------------------------------------------------------------------------------
/src/client/theme-default/slots/ContentTabs/index.tsx:
--------------------------------------------------------------------------------
1 | import { useIntl, useRouteMeta } from 'dumi';
2 | import React, { type FC } from 'react';
3 | import './index.less';
4 |
5 | type IContentTabs = ReturnType['tabs'];
6 |
7 | export interface IContentTabsProps {
8 | tabs: IContentTabs;
9 | tabKey: string | null;
10 | onChange: (tab?: NonNullable[0]) => void;
11 | }
12 |
13 | const ContentTabs: FC = ({
14 | tabs,
15 | tabKey: key,
16 | onChange,
17 | }) => {
18 | const intl = useIntl();
19 |
20 | // TODO: tab.Extra & tab.Action render
21 | return Boolean(tabs?.length) ? (
22 |
23 | - onChange()} data-active={!key || undefined}>
24 |
27 |
28 | {tabs!.map((tab) => (
29 | - onChange(tab)}
32 | data-active={key === tab.key || undefined}
33 | >
34 |
39 |
40 | ))}
41 |
42 | ) : null;
43 | };
44 |
45 | export default ContentTabs;
46 |
--------------------------------------------------------------------------------
/src/client/theme-default/slots/Features/index.tsx:
--------------------------------------------------------------------------------
1 | import { Link, useRouteMeta } from 'dumi';
2 | import React, { type FC } from 'react';
3 | import './index.less';
4 |
5 | const Features: FC = () => {
6 | const { frontmatter } = useRouteMeta();
7 |
8 | return Boolean(frontmatter.features?.length) ? (
9 | frontmatter.features!.length % n === 0) || 3
14 | }
15 | >
16 | {frontmatter.features!.map(({ title, description, emoji, link }) => {
17 | let titleWithLink: React.ReactNode;
18 | if (link) {
19 | titleWithLink = /^(\w+:)\/\/|^(mailto|tel):/.test(link) ? (
20 |
21 | {title}
22 |
23 | ) : (
24 |
{title}
25 | );
26 | }
27 | return (
28 |
29 | {emoji &&
{emoji}}
30 | {title &&
{titleWithLink || title}
}
31 | {description && (
32 |
33 | )}
34 |
35 | );
36 | })}
37 |
38 | ) : null;
39 | };
40 |
41 | export default Features;
42 |
--------------------------------------------------------------------------------
/src/client/theme-default/slots/Footer/index.less:
--------------------------------------------------------------------------------
1 | @import (reference) '../../styles/variables.less';
2 |
3 | .@{prefix}-footer {
4 | margin-top: @s-content-padding;
5 | border-top: 1px solid @c-border-light;
6 | color: @c-text-note;
7 | font-size: 15px;
8 | line-height: 26px;
9 | text-align: center;
10 | padding: @s-content-padding * 0.6 0;
11 |
12 | @{dark-selector} & {
13 | border-top-color: @c-border-less-dark;
14 | color: @c-text-note-dark;
15 | }
16 |
17 | @media @mobile {
18 | padding: @s-content-padding * 0.3 0;
19 | font-size: 13px;
20 | }
21 |
22 | a {
23 | color: @c-primary;
24 |
25 | @{dark-selector} & {
26 | color: @c-primary-dark;
27 | }
28 |
29 | &:not(:hover) {
30 | text-decoration: none;
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/client/theme-default/slots/Footer/index.tsx:
--------------------------------------------------------------------------------
1 | import { useSiteData } from 'dumi';
2 | import React, { type FC } from 'react';
3 | import './index.less';
4 |
5 | const Footer: FC = () => {
6 | const { themeConfig } = useSiteData();
7 |
8 | if (!themeConfig.footer) return null;
9 |
10 | return (
11 |
15 | );
16 | };
17 |
18 | export default Footer;
19 |
--------------------------------------------------------------------------------
/src/client/theme-default/slots/HeaderExtra/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { type FC } from 'react';
2 |
3 | const NavbarExtra: FC = () => <>>;
4 |
5 | export default NavbarExtra;
6 |
--------------------------------------------------------------------------------
/src/client/theme-default/slots/Hero/index.tsx:
--------------------------------------------------------------------------------
1 | import { Link, useRouteMeta } from 'dumi';
2 | import HeroTitle from 'dumi/theme/slots/HeroTitle';
3 | import React, { type FC } from 'react';
4 | import './index.less';
5 |
6 | const Hero: FC = () => {
7 | const { frontmatter } = useRouteMeta();
8 |
9 | if (!('hero' in frontmatter)) return null;
10 |
11 | return (
12 |
13 | {frontmatter.hero!.title && (
14 |
{frontmatter.hero!.title}
15 | )}
16 | {frontmatter.hero!.description && (
17 |
20 | )}
21 | {Boolean(frontmatter.hero!.actions?.length) && (
22 |
23 | {frontmatter.hero!.actions!.map(({ text, link }) =>
24 | /^(\w+:)\/\/|^(mailto|tel):/.test(link) ? (
25 |
26 | {text}
27 |
28 | ) : (
29 |
30 | {text}
31 |
32 | ),
33 | )}
34 |
35 | )}
36 |
37 | );
38 | };
39 |
40 | export default Hero;
41 |
--------------------------------------------------------------------------------
/src/client/theme-default/slots/HeroTitle/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { type FC, type ReactNode } from 'react';
2 | import './index.less';
3 |
4 | const HeroTitle: FC<{ children: ReactNode }> = (props) => (
5 |
6 | {props.children}
7 |
8 | );
9 |
10 | export default HeroTitle;
11 |
--------------------------------------------------------------------------------
/src/client/theme-default/slots/LangSwitch/index.less:
--------------------------------------------------------------------------------
1 | @import (reference) '../../styles/variables.less';
2 |
3 | .@{prefix}-lang-switch {
4 | color: @c-text-secondary;
5 | font-size: 14px;
6 | line-height: 16px;
7 | text-decoration: none;
8 | transition: all 0.3s;
9 | cursor: pointer;
10 |
11 | @{dark-selector} & {
12 | color: @c-text-secondary-dark;
13 | }
14 |
15 | &:hover {
16 | color: @c-primary;
17 |
18 | @{dark-selector} & {
19 | color: @c-primary-dark;
20 | }
21 | }
22 | }
23 |
24 | .@{prefix}-lang-select {
25 | display: inline-flex;
26 | align-items: center;
27 |
28 | > select {
29 | appearance: none;
30 | padding: 6px 0;
31 | padding-inline-start: 10px;
32 | padding-inline-end: 18px;
33 | color: @c-text-secondary;
34 | text-align: right;
35 | font-size: 14px;
36 | line-height: 1;
37 | border: 0;
38 | background-color: transparent;
39 | cursor: pointer;
40 |
41 | @{dark-selector} & {
42 | color: @c-text-secondary-dark;
43 | }
44 | }
45 |
46 | > svg {
47 | margin-inline-start: -16px;
48 | width: 12px;
49 | fill: darken(@c-border, 10%);
50 | pointer-events: none;
51 |
52 | @{dark-selector} & {
53 | fill: lighten(@c-border-dark, 10%);
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/client/theme-default/slots/Loading/index.less:
--------------------------------------------------------------------------------
1 | @import (reference) '../../styles/variables.less';
2 |
3 | @skeleton-cls: react-loading-skeleton;
4 |
5 | .@{prefix}-loading-skeleton {
6 | .@{skeleton-cls} {
7 | margin-block-end: 0.38em;
8 |
9 | &.first-line {
10 | width: calc(100% - 2em);
11 | margin-inline-start: 2em;
12 | }
13 |
14 | // ======== dark mode ========
15 | @{dark-selector} & {
16 | --highlight-color: fade(@c-text-dark, @dark-light-amount);
17 | --base-color: @c-text;
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/client/theme-default/slots/Loading/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Skeleton from 'react-loading-skeleton';
3 | import 'react-loading-skeleton/dist/skeleton.css';
4 | import './index.less';
5 |
6 | const Loading: React.FC = () => (
7 |
8 |
9 |
10 |
11 |
12 | );
13 |
14 | export default Loading;
15 |
--------------------------------------------------------------------------------
/src/client/theme-default/slots/Logo/index.less:
--------------------------------------------------------------------------------
1 | @import (reference) '../../styles/variables.less';
2 |
3 | .@{prefix}-logo {
4 | display: inline-flex;
5 | align-items: center;
6 | color: @c-text;
7 | font-size: 22px;
8 | line-height: 1;
9 | font-weight: bold;
10 | text-decoration: none;
11 |
12 | @{dark-selector} & {
13 | color: @c-text-dark;
14 | }
15 |
16 | @media @mobile {
17 | font-size: 18px;
18 |
19 | img {
20 | height: 32px;
21 | }
22 | }
23 |
24 | img {
25 | margin-inline-end: 10px;
26 | height: 40px;
27 |
28 | @media @mobile {
29 | height: 32px;
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/client/theme-default/slots/NavbarExtra/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { type FC } from 'react';
2 |
3 | const NavbarExtra: FC = () => <>>;
4 |
5 | export default NavbarExtra;
6 |
--------------------------------------------------------------------------------
/src/client/theme-default/slots/NotFound/index.less:
--------------------------------------------------------------------------------
1 | @import (reference) '../../styles/variables.less';
2 |
3 | .@{prefix}-not-found {
4 | display: flex;
5 | flex-direction: column;
6 | height: 65vh;
7 | align-items: center;
8 | justify-content: center;
9 |
10 | > h1 {
11 | position: relative;
12 | margin: 24px 0;
13 | color: @c-border-light;
14 | font-size: 78px;
15 | font-weight: 700;
16 | text-shadow: -1px -1px 0 @c-border;
17 |
18 | @{dark-selector} & {
19 | color: @c-border-less-dark;
20 | text-shadow: -1px -1px 0 @c-border-dark;
21 | }
22 | }
23 |
24 | > a {
25 | color: @c-primary;
26 |
27 | @{dark-selector} & {
28 | color: @c-primary-dark;
29 | }
30 |
31 | &:not(:hover) {
32 | text-decoration: none;
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/client/theme-default/slots/NotFound/index.tsx:
--------------------------------------------------------------------------------
1 | import { Link, useIntl, useLocale } from 'dumi';
2 | import React, { type FC } from 'react';
3 | import './index.less';
4 |
5 | const Page404: FC = () => {
6 | const intl = useIntl();
7 | const locale = useLocale();
8 |
9 | return (
10 |
11 |
{intl.formatMessage({ id: '404.title' })}
12 |
13 | {intl.formatMessage({ id: '404.back' })} →
14 |
15 |
16 | );
17 | };
18 |
19 | export default Page404;
20 |
--------------------------------------------------------------------------------
/src/client/theme-default/slots/PreviewerActionsExtra/index.tsx:
--------------------------------------------------------------------------------
1 | import type { IPreviewerProps } from 'dumi';
2 | import React, { type FC } from 'react';
3 |
4 | const PreviewerActionsExtra: FC = () => <>>;
5 | export default PreviewerActionsExtra;
6 |
--------------------------------------------------------------------------------
/src/client/theme-default/slots/RtlSwitch/index.less:
--------------------------------------------------------------------------------
1 | @import '../LangSwitch/index.less';
2 |
3 | .@{prefix}-rtl-switch {
4 | height: 16px;
5 | padding: 0;
6 | appearance: none;
7 | border: 0;
8 | background-color: transparent;
9 | cursor: pointer;
10 |
11 | [class*='-switch'] + &,
12 | [class*='-select'] + & {
13 | margin-inline-start: 15px;
14 | margin-inline-end: -15px;
15 | padding-inline: 15px;
16 | border-inline-start: 1px solid @c-border-light;
17 |
18 | @{dark-selector} & {
19 | border-inline-start-color: @c-border-less-dark;
20 | }
21 | }
22 |
23 | > svg {
24 | height: 16px;
25 | fill: @c-text-secondary;
26 |
27 | @{dark-selector} & {
28 | fill: @c-text-secondary-dark;
29 | }
30 | }
31 |
32 | &:hover svg {
33 | fill: @c-primary;
34 |
35 | @{dark-selector} & {
36 | fill: @c-primary-dark;
37 | }
38 | }
39 | }
40 |
41 | html[data-direction='rtl'] {
42 | direction: rtl;
43 | }
44 |
--------------------------------------------------------------------------------
/src/client/theme-default/slots/SearchBar/Mask.tsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, type FC, type ReactNode } from 'react';
2 |
3 | type MaskProps = {
4 | visible: boolean;
5 | children: ReactNode;
6 | onMaskClick?: () => void;
7 | onClose?: () => void;
8 | };
9 |
10 | export const Mask: FC = (props) => {
11 | useEffect(() => {
12 | if (props.visible) {
13 | document.body.style.overflow = 'hidden';
14 | } else if (document.body.style.overflow) {
15 | document.body.style.overflow = '';
16 | props.onClose?.();
17 | }
18 | }, [props.visible]);
19 |
20 | return props.visible ? (
21 |
22 |
26 |
{props.children}
27 |
28 | ) : null;
29 | };
30 |
--------------------------------------------------------------------------------
/src/client/theme-default/slots/Sidebar/index.tsx:
--------------------------------------------------------------------------------
1 | import { NavLink, useLocation, useRouteMeta, useSidebarData } from 'dumi';
2 | import Toc from 'dumi/theme/slots/Toc';
3 | import React, { type FC } from 'react';
4 | import './index.less';
5 |
6 | const Sidebar: FC = () => {
7 | const { pathname } = useLocation();
8 | const meta = useRouteMeta();
9 | const sidebar = useSidebarData();
10 |
11 | if (!sidebar) return null;
12 |
13 | return (
14 |
15 | {sidebar.map((item, i) => (
16 |
17 | {item.title && - {item.title}
}
18 | {item.children.map((child) => (
19 | -
20 |
21 | {child.title}
22 |
23 | {child.link === pathname && meta.frontmatter.toc === 'menu' && (
24 |
25 | )}
26 |
27 | ))}
28 |
29 | ))}
30 |
31 | );
32 | };
33 |
34 | export default Sidebar;
35 |
--------------------------------------------------------------------------------
/src/client/theme-default/slots/SocialIcon/index.less:
--------------------------------------------------------------------------------
1 | @import (reference) '../../styles/variables.less';
2 |
3 | .@{prefix}-icon {
4 | font-size: 0;
5 | line-height: 0;
6 |
7 | [class*='-switch'] + &,
8 | [class*='-select'] + & {
9 | margin-inline-start: 15px;
10 | margin-inline-end: -15px;
11 | padding-inline: 15px;
12 | border-inline-start: 1px solid @c-border;
13 |
14 | @{dark-selector} & {
15 | border-inline-start-color: @c-border-dark;
16 | }
17 | }
18 |
19 | & + & {
20 | margin-inline-start: 18px;
21 | }
22 |
23 | > svg {
24 | height: 16px;
25 | fill: @c-text-secondary;
26 |
27 | @{dark-selector} & {
28 | fill: @c-text-secondary-dark;
29 | }
30 | }
31 |
32 | &:hover svg {
33 | fill: @c-primary;
34 |
35 | @{dark-selector} & {
36 | fill: @c-primary-dark;
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/client/theme-default/slots/Tabs/index.tsx:
--------------------------------------------------------------------------------
1 | import InternalTabs, { type TabsProps as InternalTabsProps } from 'rc-tabs';
2 | import React, { type FC } from 'react';
3 | import './index.less';
4 |
5 | export type ITabsProps = Omit;
6 |
7 | const Tabs: FC = (props) => (
8 |
9 | );
10 |
11 | export default Tabs;
12 |
--------------------------------------------------------------------------------
/src/client/theme-default/slots/Toc/index.less:
--------------------------------------------------------------------------------
1 | @import (reference) '../../styles/variables.less';
2 |
3 | .@{prefix}-toc {
4 | list-style: none;
5 | margin: 12px 0 0;
6 | padding: 4px 0;
7 | border-inline-start: 1px solid @c-border;
8 |
9 | @{dark-selector} & {
10 | border-inline-start-color: @c-border-dark;
11 | }
12 |
13 | &:empty {
14 | display: none;
15 | }
16 |
17 | > li {
18 | > a {
19 | display: block;
20 | margin: 6px 0;
21 | padding: 3px 12px;
22 | color: @c-text-secondary;
23 | font-size: 13px;
24 | line-height: 1;
25 | text-decoration: none;
26 | white-space: nowrap;
27 | overflow: hidden;
28 | text-overflow: ellipsis;
29 |
30 | @{dark-selector} & {
31 | color: @c-text-secondary-dark;
32 | }
33 |
34 | &:hover {
35 | color: @c-text;
36 |
37 | @{dark-selector} & {
38 | color: @c-text-dark;
39 | }
40 | }
41 |
42 | &.active {
43 | margin-inline-start: -1px;
44 | color: @c-text;
45 | border-inline-start: 1px solid @c-primary;
46 |
47 | @{dark-selector} & {
48 | color: @c-text-dark;
49 | border-inline-start-color: @c-primary-dark;
50 | }
51 | }
52 | }
53 |
54 | &[data-depth='3'] > a {
55 | padding-inline-start: 20px;
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/client/theme-default/styles/variables.less:
--------------------------------------------------------------------------------
1 | @prefix: dumi-default;
2 |
3 | @s-content-width: 1392px;
4 | @s-content-padding: 48px;
5 | @s-sidebar-width: 184px;
6 | @s-header-height: 76px;
7 | @s-header-height-m: 52px;
8 |
9 | // default theme colors
10 | @c-primary: #1677ff;
11 | @c-warning: #d59200;
12 | @c-success: #208a41;
13 | @c-error: #ce1f31;
14 | @c-text: #30363f;
15 | @c-text-secondary: #4f5866;
16 | @c-text-note: #8a9099;
17 | @c-border: #d0d5d8;
18 | @c-border-light: #e4e9ec;
19 | @c-site-bg: #f7f9fb;
20 |
21 | // dark theme colors
22 | // @dark-selector be injected by less-loader in feature/theme/index.ts
23 | @dark-solid-amount: 15%;
24 | @dark-light-amount: 22%;
25 | @dark-border-amount: 71%;
26 | @c-primary-dark: darken(@c-primary, @dark-solid-amount);
27 | @c-warning-dark: darken(@c-warning, @dark-solid-amount);
28 | @c-success-dark: darken(@c-success, @dark-solid-amount);
29 | @c-error-dark: darken(@c-error, @dark-solid-amount);
30 | @c-text-dark: lighten(@c-text-note, @dark-light-amount);
31 | @c-text-secondary-dark: lighten(@c-text-secondary, @dark-light-amount);
32 | @c-text-note-dark: lighten(@c-text, @dark-light-amount);
33 | @c-border-dark: darken(@c-border, @dark-border-amount);
34 | @c-border-less-dark: darken(@c-border-light, @dark-border-amount);
35 | @c-site-bg-dark: darken(@c-site-bg, 95%);
36 |
37 | @mobile: ~'only screen and (max-width: 767px)';
38 | @tablet: ~'only screen and (min-width: 768px) and (max-width: 1024px)';
39 | @desktop: ~'only screen and (min-width: 1025px)';
40 |
--------------------------------------------------------------------------------
/src/client/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "moduleResolution": "node",
5 | "target": "esnext",
6 | "module": "esnext",
7 | "paths": {
8 | "@/*": ["src/*"],
9 | "@@/*": [".dumi/tmp/*"],
10 | "dumi": ["."],
11 | "dumi/dist/*": ["src/*"],
12 | "dumi/theme/*": ["src/client/theme-default/*"]
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/client/typings.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.svg' {
2 | import * as React from 'react';
3 | export const ReactComponent: React.FunctionComponent<
4 | React.SVGProps & { title?: string }
5 | >;
6 |
7 | const src: string;
8 | export default src;
9 | }
10 |
--------------------------------------------------------------------------------
/src/constants.ts:
--------------------------------------------------------------------------------
1 | export const LOCAL_DUMI_DIR = '.dumi';
2 |
3 | export const LOCAL_THEME_DIR = `${LOCAL_DUMI_DIR}/theme`;
4 |
5 | export const LOCAL_PAGES_DIR = `${LOCAL_DUMI_DIR}/pages`;
6 |
7 | export const THEME_PREFIX = 'dumi-theme-';
8 |
9 | export const SP_ROUTE_PREFIX = '~';
10 |
11 | export const PREFERS_COLOR_ATTR = 'data-prefers-color';
12 |
13 | export const PREFERS_COLOR_LS_KEY = 'dumi:prefers-color';
14 |
15 | export const PICKED_PKG_FIELDS = {
16 | name: '',
17 | description: '',
18 | version: '',
19 | license: '',
20 | repository: '',
21 | author: '',
22 | authors: '',
23 | };
24 |
25 | export const USELESS_TMP_FILES = ['tsconfig.json', 'typings.d.ts'];
26 |
27 | export const VERSION_2_LEVEL_NAV = '^2.2.0';
28 |
29 | export const VERSION_2_DEPRECATE_SOFT_BREAKS = '^2.2.0';
30 |
31 | export const DEFAULT_DEMO_MODULE_EXTENSIONS = ['.js', '.jsx', '.ts', '.tsx'];
32 |
33 | export const DEFAULT_DEMO_PLAIN_TEXT_EXTENSIONS = [
34 | '.css',
35 | '.less',
36 | '.sass',
37 | '.scss',
38 | '.styl',
39 | '.json',
40 | ];
41 |
42 | export const FS_CACHE_DIR = 'node_modules/.cache/dumi';
43 |
44 | export const SHOULD_SKIP_LIVEDEMO_ERROR = [
45 | 'Unable to find node on an unmounted component',
46 | '#188',
47 | 'Portals are not currently supported by the server renderer',
48 | '#257',
49 | ];
50 |
--------------------------------------------------------------------------------
/src/features/compile/babelLoaderCustomize.ts:
--------------------------------------------------------------------------------
1 | import type { PartialConfig } from '@umijs/bundler-utils/compiled/@babel/core';
2 |
3 | export default function babelLoaderCustomize() {
4 | return {
5 | config(config: PartialConfig) {
6 | const context = this as any;
7 |
8 | // make meta filename is different with source file, to avoid collect wrong deps in MFSU
9 | if (
10 | config.options.filename &&
11 | ['type=demo', 'type=frontmatter', 'type=demo-index', 'type=text'].some(
12 | (v) => context.resourceQuery.includes(v),
13 | )
14 | ) {
15 | config.options.filename += context.resourceQuery;
16 | }
17 |
18 | return config.options;
19 | },
20 | };
21 | }
22 |
--------------------------------------------------------------------------------
/src/features/compile/utils.ts:
--------------------------------------------------------------------------------
1 | import { logger } from '@umijs/utils';
2 | import { isArray } from '@umijs/utils/compiled/lodash';
3 | import { IApi } from 'umi';
4 | export const shouldDisabledLiveDemo = (api: IApi) => {
5 | const extraBabelPlugins = api.userConfig.extraBabelPlugins;
6 | const disableFlag =
7 | isArray(extraBabelPlugins) &&
8 | extraBabelPlugins!.some((p: any) =>
9 | /^import$|babel-plugin-import/.test(p[0]),
10 | );
11 | if (disableFlag) {
12 | logger.warn(
13 | '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',
14 | );
15 | }
16 | return disableFlag;
17 | };
18 |
--------------------------------------------------------------------------------
/src/features/configPlugins/index.ts:
--------------------------------------------------------------------------------
1 | import type { IApi } from '@/types';
2 | import { getSchemas } from './schema';
3 |
4 | export default (api: IApi) => {
5 | const configDefaults: Record = {
6 | resolve: {
7 | docDirs: ['docs'],
8 | atomDirs: [{ type: 'component', dir: 'src' }],
9 | codeBlockMode: 'active',
10 | forceKebabCaseRouting: true,
11 | },
12 | themeConfig: {
13 | footer: `Copyright © ${new Date().getFullYear()} | Powered by dumi`,
14 | prefersColor: { default: 'light', switch: true },
15 | nprogress: true,
16 | lastUpdated: true,
17 | },
18 | };
19 |
20 | const schemas = getSchemas();
21 | for (const key of Object.keys(schemas)) {
22 | const config: Record = {
23 | schema: schemas[key] || ((joi: any) => joi.any()),
24 | };
25 | if (key in configDefaults) {
26 | config.default = configDefaults[key];
27 | }
28 | api.registerPlugins([
29 | {
30 | id: `virtual: config-${key}`,
31 | key: key,
32 | config,
33 | },
34 | ]);
35 | }
36 | };
37 |
--------------------------------------------------------------------------------
/src/features/exports.ts:
--------------------------------------------------------------------------------
1 | import type { IApi } from '@/types';
2 | import { winPath } from 'umi/plugin-utils';
3 |
4 | export default (api: IApi) => {
5 | api.describe({ key: 'dumi:exports' });
6 |
7 | // allow import from dumi
8 | api.modifyConfig((memo) => {
9 | memo.alias['dumi$'] = '@@/dumi/exports';
10 |
11 | return memo;
12 | });
13 |
14 | // exports all theme api from dumi
15 | api.onGenerateFiles(() => {
16 | api.writeTmpFile({
17 | noPluginDir: true,
18 | path: 'dumi/exports.ts',
19 | content: `export * from '../exports';
20 | export * from '${winPath(require.resolve('../client/theme-api'))}';
21 | export * from './meta/exports';`,
22 | });
23 | });
24 | };
25 |
--------------------------------------------------------------------------------
/src/features/sideEffects/index.ts:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 | import type { IApi } from 'umi';
3 | import { winPath } from 'umi/plugin-utils';
4 | import docSideEffectsWebpackPlugin from './docSideEffectsWebpackPlugin';
5 |
6 | /**
7 | * plugin for register the doc side-effects webpack plugin
8 | * to avoid tree-shaking for .umi & .dumi/theme directory if package.json has sideEffects: false
9 | */
10 | export default (api: IApi) => {
11 | api.describe({ key: 'dumi:sideEffects' });
12 |
13 | api.chainWebpack((memo) => {
14 | memo.plugin('docSideEffects').use(docSideEffectsWebpackPlugin, [
15 | {
16 | sideEffects: [
17 | // such as .dumi/tmp-production/**
18 | winPath(
19 | path.relative(api.cwd, path.join(api.paths.absTmpPath, '**')),
20 | ),
21 | // .dumi local theme
22 | '.dumi/theme/**',
23 | ],
24 | pkgPath: path.join(api.cwd, 'package.json'),
25 | },
26 | ]);
27 |
28 | return memo;
29 | });
30 | };
31 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import type {
2 | IDumiTechStack,
3 | IDumiTechStackRuntimeOpts,
4 | IDumiUserConfig,
5 | } from '@/types';
6 | let unistUtilVisit: typeof import('unist-util-visit');
7 |
8 | // workaround to export pure esm package in cjs
9 | (async () => {
10 | unistUtilVisit = await import('unist-util-visit');
11 | })();
12 |
13 | export * from 'umi';
14 | export { getProjectRoot } from './utils';
15 | export { unistUtilVisit, IDumiTechStack, IDumiTechStackRuntimeOpts };
16 | export const defineConfig = (config: IDumiUserConfig) => config;
17 |
--------------------------------------------------------------------------------
/src/loaders/demo/index.ts:
--------------------------------------------------------------------------------
1 | import type { IDumiTechStack } from '@/types';
2 | import { winPath } from '@umijs/utils';
3 | export interface IDemoLoaderOptions {
4 | techStacks: IDumiTechStack[];
5 | cwd: string;
6 | }
7 |
8 | export default function demoLoader(this: any, raw: string) {
9 | const opts: IDemoLoaderOptions = this.getOptions();
10 | const techStackName = new URLSearchParams(this.resourceQuery).get(
11 | 'techStack',
12 | );
13 | const techStack = opts.techStacks.find((t) => t.name === techStackName)!;
14 |
15 | let code = techStack.transformCode(raw, {
16 | type: 'external',
17 | fileAbsPath: this.resourcePath,
18 | });
19 | code = `import '${winPath(this.resourcePath)}?watch=parent';${code}`;
20 | return code;
21 | }
22 |
--------------------------------------------------------------------------------
/src/loaders/markdown/transformer/fixtures/code-group/expect.ts:
--------------------------------------------------------------------------------
1 | import { IMdTransformerResult } from '../..';
2 |
3 | export default (ret: IMdTransformerResult) => {
4 | expect(ret.content).toEqual(
5 | '<>{$$contentTexts[0].value}{$$contentTexts[1].value}{$$contentTexts[2].value}>',
6 | );
7 | };
8 |
--------------------------------------------------------------------------------
/src/loaders/markdown/transformer/fixtures/code-group/index.md:
--------------------------------------------------------------------------------
1 | ### CodeGroup
2 |
3 | :::code-group
4 |
5 | ```bash [npm]
6 | npm install -D dumi
7 | ```
8 |
9 | ```bash [yarn]
10 | yarn add -D dumi
11 | ```
12 |
13 | ```bash [pnpm]
14 | pnpm add -D dumi
15 | ```
16 |
17 | :::
18 |
--------------------------------------------------------------------------------
/src/loaders/markdown/transformer/fixtures/code-group/with-hightlight/expect.ts:
--------------------------------------------------------------------------------
1 | import { IMdTransformerResult } from '../..';
2 |
3 | export default (ret: IMdTransformerResult) => {
4 | expect(ret.content).toEqual(
5 | '<>{"CodeGroup"}
{$$contentTexts[0].value}
{$$contentTexts[1].value}>',
6 | );
7 | };
8 |
--------------------------------------------------------------------------------
/src/loaders/markdown/transformer/fixtures/code-group/with-hightlight/index.md:
--------------------------------------------------------------------------------
1 | ### CodeGroup
2 |
3 | > 支持高亮行
4 |
5 | :::code-group
6 |
7 | ```ts [.dumirc.ts] {1,3-5}
8 | import { defineConfig } from 'dumi';
9 |
10 | export default defineConfig({
11 | // ...
12 | });
13 | ```
14 |
15 | :::
16 |
--------------------------------------------------------------------------------
/src/loaders/markdown/transformer/fixtures/demo/demo.jsx:
--------------------------------------------------------------------------------
1 | export default () => 'demo';
2 |
--------------------------------------------------------------------------------
/src/loaders/markdown/transformer/fixtures/demo/expect.ts:
--------------------------------------------------------------------------------
1 | import type { IMdTransformerResult } from '../..';
2 |
3 | export default (ret: IMdTransformerResult) => {
4 | // replace to global DumiDemo component
5 | expect(ret.content).toEqual(`<>>`);
18 |
19 | // code block demo to inline component
20 | expect(ret.meta.demos![0].id).toEqual('demo-0');
21 | expect(ret.meta.demos![0].component).toContain('Fake');
22 |
23 | // external demo to lazy import component
24 | expect(ret.meta.demos![1].id).toEqual('demo-demo');
25 | expect(ret.meta.demos![1].component).toContain('React.lazy');
26 | };
27 |
--------------------------------------------------------------------------------
/src/loaders/markdown/transformer/fixtures/demo/index.md:
--------------------------------------------------------------------------------
1 | ```jsx
2 | export default () => <>Hello>;
3 | ```
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/loaders/markdown/transformer/fixtures/embed/embed.md:
--------------------------------------------------------------------------------
1 | ### This is embed.md
2 |
3 | second line
4 |
5 | third line
6 |
7 | type:null
8 |
9 | :::success
10 | 这是一条成功信息
11 | :::
12 |
--------------------------------------------------------------------------------
/src/loaders/markdown/transformer/fixtures/embed/index.md:
--------------------------------------------------------------------------------
1 | ### This is index.md
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/loaders/markdown/transformer/fixtures/img-demo/expect.ts:
--------------------------------------------------------------------------------
1 | import type { IMdTransformerResult } from '../..';
2 |
3 | export default (ret: IMdTransformerResult) => {
4 | expect(ret.content).toEqual(
5 | '<>>',
6 | );
7 | };
8 |
--------------------------------------------------------------------------------
/src/loaders/markdown/transformer/fixtures/img-demo/index.md:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/loaders/markdown/transformer/fixtures/normal/expect.ts:
--------------------------------------------------------------------------------
1 | import type { IMdTransformerResult } from '../..';
2 | // @ts-ignore
3 | import fs from 'fs';
4 | import * as prettier from 'prettier';
5 |
6 | export default (ret: IMdTransformerResult) => {
7 | const content = prettier.format(ret.content, {
8 | parser: 'babel',
9 | endOfLine: 'lf', // 强制使用 LF 作为行尾符(Windows 平台下也是 LF)
10 | });
11 |
12 | const filePath = `${__filename}.snap`;
13 | if (
14 | (!fs.existsSync(filePath) || process.env.UPDATE_SNAPSHOT) &&
15 | !process.env.CI
16 | ) {
17 | fs.writeFileSync(filePath, content);
18 | return;
19 | }
20 |
21 | const snap = fs.readFileSync(filePath, 'utf-8').replace(/\r\n/g, '\n'); // 统一换行符
22 |
23 | expect(content).toEqual(snap);
24 | };
25 |
--------------------------------------------------------------------------------
/src/loaders/markdown/transformer/fixtures/normal/index.md:
--------------------------------------------------------------------------------
1 | # first-level title
2 |
3 | ## second-level title
4 |
5 | ### third-level title
6 |
7 | #### fourth-level title
8 |
9 | ##### fifth-level title
10 |
11 | ###### sixth-level title
12 |
13 | hello
14 |
15 | world
16 |
17 | ---
18 |
19 | **strong**_italic_~~strike through~~`inline code`
20 |
21 | > quote
22 | >
23 | > > nested quote
24 |
25 | - unordered list
26 | - nested item
27 | - another item
28 |
29 | 1. ordered list
30 | 1. nested item
31 |
32 | - another item
33 |
34 | ```css
35 | /* code block */
36 | ```
37 |
38 | ```ts {1}
39 | console.log('line highlighting');
40 | ```
41 |
42 | ```jsx {1,2,3-5} | pure
43 | console.log('line highlighting');
44 | ```
45 |
46 |
47 | manual pre tag
48 |
49 |
50 | | header-left | header-right |
51 | | ----------- | -----------: |
52 | | cell-left | cell-right |
53 |
54 | [link](https://d.umijs.org)
55 |
56 | Auto-link: https://d.umijs.org
57 |
58 | 
59 |
--------------------------------------------------------------------------------
/src/loaders/markdown/transformer/fixtures/react-component/expect.ts:
--------------------------------------------------------------------------------
1 | import type { IMdTransformerResult } from '../..';
2 |
3 | export default (ret: IMdTransformerResult) => {
4 | expect(ret.content).toContain('');
8 | expect(ret.content).toContain('');
9 |
10 | // don't transform tagName which is part of attr value
11 | expect(ret.content).toContain('value="Array"');
12 | expect(ret.content).toContain('html=""');
13 | expect(ret.content).toContain('value=""');
14 | expect(ret.content).toContain('html=""');
15 | };
16 |
--------------------------------------------------------------------------------
/src/loaders/markdown/transformer/fixtures/react-component/index.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | dumi
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/loaders/markdown/transformer/fixtures/skip-only/case01/expect.ts:
--------------------------------------------------------------------------------
1 | import type { IMdTransformerResult } from '../..';
2 |
3 | export default (ret: IMdTransformerResult) => {
4 | expect(ret.content).toEqual(`<>>`);
13 | };
14 |
--------------------------------------------------------------------------------
/src/loaders/markdown/transformer/fixtures/skip-only/case01/index.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/loaders/markdown/transformer/fixtures/skip-only/case02/expect.ts:
--------------------------------------------------------------------------------
1 | import type { IMdTransformerResult } from '../..';
2 |
3 | export default (ret: IMdTransformerResult) => {
4 | expect(ret.content).toEqual(`<>>`);
19 | };
20 |
--------------------------------------------------------------------------------
/src/loaders/markdown/transformer/fixtures/skip-only/case02/index.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/loaders/markdown/transformer/fixtures/skip-only/case03/expect.ts:
--------------------------------------------------------------------------------
1 | import type { IMdTransformerResult } from '../..';
2 |
3 | export default (ret: IMdTransformerResult) => {
4 | expect(ret.content).toEqual(`<>>`);
13 | };
14 |
--------------------------------------------------------------------------------
/src/loaders/markdown/transformer/fixtures/skip-only/case03/index.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/loaders/markdown/transformer/fixtures/skip-only/case04/expect.ts:
--------------------------------------------------------------------------------
1 | import type { IMdTransformerResult } from '../..';
2 |
3 | export default (ret: IMdTransformerResult) => {
4 | expect(ret.content).toEqual(`<>>`);
13 | };
14 |
--------------------------------------------------------------------------------
/src/loaders/markdown/transformer/fixtures/skip-only/case04/index.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/loaders/markdown/transformer/fixtures/skip-only/case05/expect.ts:
--------------------------------------------------------------------------------
1 | import type { IMdTransformerResult } from '../..';
2 |
3 | export default (ret: IMdTransformerResult) => {
4 | expect(ret.content).toEqual(`<>>`);
13 | };
14 |
--------------------------------------------------------------------------------
/src/loaders/markdown/transformer/fixtures/skip-only/case05/index.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/loaders/markdown/transformer/fixtures/skip-only/demos/bar.jsx:
--------------------------------------------------------------------------------
1 | export default () => 'Bar';
2 |
--------------------------------------------------------------------------------
/src/loaders/markdown/transformer/fixtures/skip-only/demos/baz.jsx:
--------------------------------------------------------------------------------
1 | export default () => 'Baz';
2 |
--------------------------------------------------------------------------------
/src/loaders/markdown/transformer/fixtures/skip-only/demos/foo.jsx:
--------------------------------------------------------------------------------
1 | export default () => 'Foo';
2 |
--------------------------------------------------------------------------------
/src/loaders/markdown/transformer/rehypeDesc.ts:
--------------------------------------------------------------------------------
1 | import type { Root } from 'hast';
2 | import type { Transformer } from 'unified';
3 |
4 | let visit: typeof import('unist-util-visit').visit;
5 | let EXIT: typeof import('unist-util-visit').EXIT;
6 | let toString: typeof import('hast-util-to-string').toString;
7 |
8 | (async () => {
9 | ({ visit, EXIT } = await import('unist-util-visit'));
10 | ({ toString } = await import('hast-util-to-string'));
11 | })();
12 |
13 | /**
14 | * rehype plugin for extract fallback description from markdown content
15 | */
16 | export default function rehypeDesc(): Transformer {
17 | return async (tree, vFile) => {
18 | // skip if user has defined description
19 | if (!vFile.data.frontmatter!.description) {
20 | visit(tree, 'element', (node) => {
21 | if (node.tagName === 'p') {
22 | const text = toString(node).trim();
23 |
24 | if (text) {
25 | vFile.data.frontmatter!.description = text;
26 | return EXIT;
27 | }
28 | }
29 | });
30 | }
31 | };
32 | }
33 |
--------------------------------------------------------------------------------
/src/loaders/markdown/transformer/rehypeHighlightLine.ts:
--------------------------------------------------------------------------------
1 | import type { Root } from 'hast';
2 | import type { Transformer } from 'unified';
3 |
4 | let visit: typeof import('unist-util-visit').visit;
5 | let isElement: typeof import('hast-util-is-element').isElement;
6 |
7 | const RE = /{((?:\d+(?:-\d+)?,?)+)}/;
8 |
9 | // workaround to import pure esm module
10 | (async () => {
11 | ({ visit } = await import('unist-util-visit'));
12 | ({ isElement } = await import('hast-util-is-element'));
13 | })();
14 |
15 | const attrsToLines = (attrs: string) => {
16 | const result: number[] = [];
17 | attrs
18 | .split(',')
19 | .map((v) => v.split('-').map((v) => parseInt(v, 10)))
20 | .forEach(([start, end = start]) => {
21 | for (let i = start; i <= end; i++) {
22 | result.push(i);
23 | }
24 | });
25 | return result;
26 | };
27 |
28 | function rehypeHighlightLine(): Transformer {
29 | return async (tree) => {
30 | visit(tree, 'element', (node) => {
31 | if (isElement(node, 'code') && typeof node.data?.meta === 'string') {
32 | const lines = node.data.meta.match(RE)?.[1];
33 |
34 | if (lines) {
35 | // ensure the next plugin get the correct lang
36 | node.data.meta = node.data.meta.replace(lines, '').trim();
37 |
38 | node.data.highlightLines = attrsToLines(lines);
39 | }
40 | }
41 | });
42 | };
43 | }
44 |
45 | export default rehypeHighlightLine;
46 |
--------------------------------------------------------------------------------
/src/loaders/markdown/transformer/rehypeImg.ts:
--------------------------------------------------------------------------------
1 | import type { Element, Root } from 'hast';
2 | import path from 'path';
3 | import type { Transformer } from 'unified';
4 |
5 | let visit: typeof import('unist-util-visit').visit;
6 |
7 | // workaround to import pure esm module
8 | (async () => {
9 | ({ visit } = await import('unist-util-visit'));
10 | })();
11 |
12 | function isRelativeUrl(url: string) {
13 | return (
14 | !url.startsWith('data:image') &&
15 | !/^((blob:)?\w+:)?\/\//.test(url) &&
16 | !path.isAbsolute(url)
17 | );
18 | }
19 |
20 | /**
21 | * rehype plugin to handle img source from local
22 | */
23 | export default function rehypeImg(): Transformer {
24 | return (tree) => {
25 | visit(tree, 'element', (node: Element) => {
26 | if (node.tagName === 'img' && typeof node.properties?.src === 'string') {
27 | const src = node.properties.src.trim();
28 |
29 | if (src && isRelativeUrl(src)) {
30 | delete node.properties.src;
31 | node.JSXAttributes = [
32 | {
33 | type: 'JSXAttribute',
34 | name: 'src',
35 | value: `require('${decodeURI(src)}')`,
36 | },
37 | ];
38 | }
39 | }
40 | });
41 | };
42 | }
43 |
--------------------------------------------------------------------------------
/src/loaders/markdown/transformer/rehypeStrip.ts:
--------------------------------------------------------------------------------
1 | import type { Root } from 'hast';
2 | import type { Transformer } from 'unified';
3 |
4 | let visit: typeof import('unist-util-visit').visit;
5 | let SKIP: typeof import('unist-util-visit').SKIP;
6 |
7 | // workaround to import pure esm module
8 | (async () => {
9 | ({ visit, SKIP } = await import('unist-util-visit'));
10 | })();
11 |
12 | export default function rehypeStrip(): Transformer {
13 | return (tree) => {
14 | visit(tree, 'text', (node, index, parent) => {
15 | // strip all useless break line node, it is means nothing for HTML
16 | if (/^[\n\r]+$/.test(node.value)) {
17 | parent?.children.splice(index!, 1);
18 |
19 | // skip and re-visit current index
20 | return [SKIP, index];
21 | }
22 | });
23 | };
24 | }
25 |
--------------------------------------------------------------------------------
/src/loaders/null/index.ts:
--------------------------------------------------------------------------------
1 | export default function loader() {
2 | return '';
3 | }
4 |
5 | export function pitch() {
6 | return '';
7 | }
8 |
--------------------------------------------------------------------------------
/src/loaders/page/index.ts:
--------------------------------------------------------------------------------
1 | import { parseCodeFrontmatter } from '@/utils';
2 | import path from 'path';
3 | import { lodash, winPath } from 'umi/plugin-utils';
4 |
5 | export default function pageMetaLoader(this: any, raw: string) {
6 | const pathWithoutIndex = winPath(this.resourcePath).replace(
7 | /(\/index([^/]+)?)?\.(j|t)sx?$/,
8 | '',
9 | );
10 | let { frontmatter } = parseCodeFrontmatter(raw);
11 |
12 | frontmatter ||= {};
13 | // fallback use filename as page title
14 | frontmatter.title ??= lodash.startCase(path.basename(pathWithoutIndex));
15 |
16 | return `export const frontmatter = ${JSON.stringify(frontmatter)};
17 | export const toc = [];`;
18 | }
19 |
--------------------------------------------------------------------------------
/src/loaders/post-raw/index.ts:
--------------------------------------------------------------------------------
1 | import { winPath } from '@umijs/utils';
2 | /**
3 | * loader for mako dumi-raw watch-parent
4 | */
5 | export default function postRawLoader(this: any, raw: string) {
6 | return `
7 | import '${winPath(this.resourcePath)}?watch=parent';
8 | ${raw};
9 | `;
10 | }
11 |
--------------------------------------------------------------------------------
/src/loaders/pre-raw/index.ts:
--------------------------------------------------------------------------------
1 | import { parseCodeFrontmatter } from '@/utils';
2 |
3 | /**
4 | * loader for discard frontmatter from code file content
5 | */
6 | export default function preRawLoader(this: any, raw: string) {
7 | if (/\.(j|t)sx?$/.test(this.resourcePath)) {
8 | return parseCodeFrontmatter(raw).code;
9 | }
10 |
11 | return raw;
12 | }
13 |
--------------------------------------------------------------------------------
/src/preset.ts:
--------------------------------------------------------------------------------
1 | import type { IApi } from '@/types';
2 |
3 | export default (api: IApi) => {
4 | api.describe({ key: 'dumi-preset' });
5 |
6 | return {
7 | plugins: [
8 | require.resolve('./registerMethods'),
9 | require.resolve('./features/configPlugins'),
10 | require.resolve('./features/autoAlias'),
11 | require.resolve('./features/derivative'),
12 | require.resolve('./features/sideEffects'),
13 | require.resolve('./features/exports'),
14 | require.resolve('./features/compile'),
15 | require.resolve('./features/routes'),
16 | require.resolve('./features/meta'),
17 | require.resolve('./features/tabs'),
18 | require.resolve('./features/theme'),
19 | require.resolve('./features/locales'),
20 | require.resolve('./features/parser'),
21 | require.resolve('./features/assets'),
22 | require.resolve('./features/exportStatic'),
23 | require.resolve('./features/sitemap'),
24 | require.resolve('./features/html2sketch'),
25 | ],
26 | };
27 | };
28 |
--------------------------------------------------------------------------------
/src/registerMethods.ts:
--------------------------------------------------------------------------------
1 | import type { IApi } from './types';
2 |
3 | export default (api: IApi) => {
4 | api.describe({ key: 'dumi:registerMethods' });
5 |
6 | [
7 | 'registerTechStack',
8 | 'addContentTab',
9 | 'modifyAssetsMetadata',
10 | 'modifyTheme',
11 | ].forEach((name) => {
12 | api.registerMethod({ name });
13 | });
14 | };
15 |
--------------------------------------------------------------------------------
/src/service/constants.ts:
--------------------------------------------------------------------------------
1 | export const MIN_NODE_VERSION = 14;
2 | export const DEV_COMMAND = 'dev';
3 | export const DEFAULT_CONFIG_FILES = ['.dumirc.ts', '.dumirc.js'];
4 | export const FRAMEWORK_NAME = 'dumi';
5 |
--------------------------------------------------------------------------------
/src/service/dev.ts:
--------------------------------------------------------------------------------
1 | import fork from 'umi/dist/cli/fork';
2 |
3 | export function dev() {
4 | const child = fork({
5 | scriptPath: require.resolve('../../bin/forkedDev'),
6 | });
7 | // ref:
8 | // http://nodejs.cn/api/process/signal_events.html
9 | // https://lisk.io/blog/development/why-we-stopped-using-npm-start-child-processes
10 | process.on('SIGINT', () => {
11 | child.kill('SIGINT');
12 | // ref:
13 | // https://github.com/umijs/umi/issues/6009
14 | process.exit(0);
15 | });
16 | process.on('SIGTERM', () => {
17 | child.kill('SIGTERM');
18 | process.exit(1);
19 | });
20 | }
21 |
--------------------------------------------------------------------------------
/src/service/forkedDev.ts:
--------------------------------------------------------------------------------
1 | import { setNodeTitle } from '@umijs/utils/dist/node';
2 | import { logger, printHelp, setNoDeprecation, yParser } from 'umi/plugin-utils';
3 | import { DEV_COMMAND, FRAMEWORK_NAME } from './constants';
4 | import { DumiService } from './service';
5 |
6 | setNodeTitle(`${FRAMEWORK_NAME}-dev`);
7 | setNoDeprecation();
8 |
9 | (async () => {
10 | try {
11 | const args = yParser(process.argv.slice(2));
12 | const service = new DumiService();
13 | await service.run2({
14 | name: DEV_COMMAND,
15 | args,
16 | });
17 |
18 | let closed = false;
19 |
20 | // @ts-ignore
21 | function onSignal(signal: string) {
22 | if (closed) return;
23 | closed = true;
24 | // 退出时触发插件中的 onExit 事件
25 | service.applyPlugins({
26 | key: 'onExit',
27 | args: {
28 | signal,
29 | },
30 | });
31 | process.exit(0);
32 | }
33 |
34 | // kill(2) Ctrl-C
35 | process.once('SIGINT', () => onSignal('SIGINT'));
36 | // kill(3) Ctrl-\
37 | process.once('SIGQUIT', () => onSignal('SIGQUIT'));
38 | // kill(15) default
39 | process.once('SIGTERM', () => onSignal('SIGTERM'));
40 | } catch (e: any) {
41 | logger.fatal(e);
42 | printHelp.exit();
43 | process.exit(1);
44 | }
45 | })();
46 |
--------------------------------------------------------------------------------
/src/service/printHelp.ts:
--------------------------------------------------------------------------------
1 | import { logger } from 'umi/plugin-utils';
2 |
3 | export function printHelp() {
4 | const loggerPath = logger.getLatestLogFilePath();
5 | if (loggerPath) {
6 | logger.fatal('A complete log of this run can be found in:');
7 | logger.fatal(loggerPath);
8 | }
9 | logger.fatal(
10 | 'Consider reporting a GitHub issue on https://github.com/umijs/dumi/issues',
11 | );
12 | // logger.fatal(FEEDBACK_MESSAGE);
13 | }
14 |
--------------------------------------------------------------------------------
/src/service/service.ts:
--------------------------------------------------------------------------------
1 | import { Env } from '@umijs/core';
2 | import { join } from 'path';
3 | import { Service } from 'umi';
4 | import { winPath } from 'umi/plugin-utils';
5 | import { DEFAULT_CONFIG_FILES, FRAMEWORK_NAME } from './constants';
6 |
7 | function winJoin(...args: string[]) {
8 | return winPath(join(...args));
9 | }
10 |
11 | export class DumiService extends Service {
12 | constructor() {
13 | super({
14 | defaultConfigFiles: DEFAULT_CONFIG_FILES,
15 | frameworkName: FRAMEWORK_NAME,
16 | });
17 | }
18 |
19 | async getPaths() {
20 | const { cwd } = this;
21 | const tmp = this.env === Env.development ? `tmp` : `tmp-${this.env}`;
22 | const absFWPath = winJoin(cwd, `.${FRAMEWORK_NAME}`);
23 |
24 | // use .dumi as src dir for move all conventional files to .dumi
25 | // such as app.ts, global.ts, loading.tsx & etc.
26 | const absSrcPath = absFWPath;
27 | const absPagesPath = winJoin(absSrcPath, 'pages');
28 | const absApiRoutesPath = winJoin(absSrcPath, 'api');
29 | const absTmpPath = winJoin(absSrcPath, tmp);
30 | const absNodeModulesPath = winJoin(cwd, 'node_modules');
31 | const absOutputPath = winJoin(cwd, 'dist');
32 |
33 | return {
34 | cwd,
35 | absSrcPath,
36 | absPagesPath,
37 | absApiRoutesPath,
38 | absTmpPath,
39 | absNodeModulesPath,
40 | absOutputPath,
41 | };
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/techStacks/react.ts:
--------------------------------------------------------------------------------
1 | import type { IDumiTechStack } from '@/types';
2 | import { wrapDemoWithFn } from './utils';
3 |
4 | export default class ReactTechStack implements IDumiTechStack {
5 | name = 'react';
6 |
7 | runtimeOpts?: IDumiTechStack['runtimeOpts'] = {
8 | compilePath: require.resolve('../client/misc/reactDemoCompiler'),
9 | };
10 |
11 | isSupported(...[, lang]: Parameters) {
12 | return ['jsx', 'tsx'].includes(lang);
13 | }
14 |
15 | transformCode(...[raw, opts]: Parameters) {
16 | if (opts.type === 'code-block') {
17 | const isTSX = opts.fileAbsPath.endsWith('.tsx');
18 | const code = wrapDemoWithFn(raw, {
19 | filename: opts.fileAbsPath,
20 | parserConfig: {
21 | syntax: isTSX ? 'typescript' : 'ecmascript',
22 | [isTSX ? 'tsx' : 'jsx']: true,
23 | },
24 | });
25 | return `React.memo(React.lazy(${code}))`;
26 | }
27 | return raw;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/templates/meta/index.ts.tpl:
--------------------------------------------------------------------------------
1 | {{#metaFiles}}
2 | import { frontmatter as fm{{{index}}}, toc as t{{{index}}} } from '{{{file}}}?type=frontmatter';
3 | {{#isMarkdown}}
4 | import { demoIndex as dmi{{{index}}} } from '{{{file}}}?type=demo-index';
5 | {{/isMarkdown}}
6 | {{/metaFiles}}
7 |
8 | export const filesMeta = {
9 | {{#metaFiles}}
10 | '{{{id}}}': {
11 | frontmatter: fm{{{index}}},
12 | toc: t{{{index}}},
13 | {{#isMarkdown}}
14 | demoIndex: dmi{{{index}}},
15 | {{/isMarkdown}}
16 | {{#tabs}}
17 | tabs: {{{tabs}}},
18 | {{/tabs}}
19 | {{#isMarkdown}}
20 | textGetter: () => import({{{chunkName}}}'{{{file}}}?type=text'),
21 | {{/isMarkdown}}
22 | },
23 | {{/metaFiles}}
24 | }
25 |
26 | export { tabs as tabsMeta } from './tabs';
27 |
--------------------------------------------------------------------------------
/src/templates/meta/runtime.ts.tpl:
--------------------------------------------------------------------------------
1 | import { warning } from '{{{rc_util}}}';
2 | import deepmerge from '{{{deepmerge}}}';
3 | import { getRouteMetaById } from './exports';
4 |
5 | // Proxy do not warning since `Object.keys` will get nothing to loop
6 | function wrapEmpty(meta, fieldName, defaultValue) {
7 | Object.defineProperty(meta, fieldName, {
8 | get: () => {
9 | warning(false, `'${fieldName}' return empty in latest version, please use \`useRouteMeta\` instead.`);
10 | return defaultValue;
11 | },
12 | });
13 | }
14 |
15 | export const patchRoutes = ({ routes }) => {
16 | Object.values(routes).forEach((route) => {
17 | const routeMeta = getRouteMetaById(route.id, { syncOnly: true });
18 |
19 | if (routeMeta) {
20 | if (process.env.NODE_ENV === 'production' && (route.meta?.frontmatter?.debug || routeMeta.frontmatter?.debug)) {
21 | // hide route in production which set hide frontmatter
22 | delete routes[route.id];
23 | } else {
24 | // merge meta to route object
25 | route.meta = deepmerge(route.meta, routeMeta);
26 |
27 | wrapEmpty(route.meta, 'toc', []);
28 | wrapEmpty(route.meta, 'texts', []);
29 |
30 | route.meta.tabs?.forEach((tab) => {
31 | wrapEmpty(tab, 'toc', []);
32 | wrapEmpty(tab, 'texts', []);
33 | });
34 | }
35 | }
36 | });
37 | }
38 |
--------------------------------------------------------------------------------
/suites/boilerplate/.fatherrc.ts:
--------------------------------------------------------------------------------
1 | import { version } from '../../package.json';
2 |
3 | export default {
4 | cjs: { output: 'dist' },
5 | define: {
6 | // replace process.env.DUMI_VERSION to current version
7 | 'process.env.DUMI_VERSION': JSON.stringify(`^${version}`),
8 | },
9 | };
10 |
--------------------------------------------------------------------------------
/suites/boilerplate/bin/create-dumi.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | process.env.FS_LOGGER = 'none';
4 | require('../dist/cli');
5 |
--------------------------------------------------------------------------------
/suites/boilerplate/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "create-dumi",
3 | "version": "2.4.14",
4 | "description": "Creator for dumi boilerplate",
5 | "homepage": "https://github.com/umijs/dumi/tree/master/suites/boilerplate#readme",
6 | "bugs": "https://github.com/umijs/dumi/issues",
7 | "repository": {
8 | "type": "git",
9 | "url": "https://github.com/umijs/dumi"
10 | },
11 | "license": "MIT",
12 | "main": "dist/index.js",
13 | "types": "dist/index.d.ts",
14 | "bin": {
15 | "create-dumi": "bin/create-dumi.js"
16 | },
17 | "files": [
18 | "dist",
19 | "templates"
20 | ],
21 | "scripts": {
22 | "build": "father build",
23 | "dev": "father dev"
24 | },
25 | "dependencies": {
26 | "@umijs/utils": "^4.0.84"
27 | },
28 | "publishConfig": {
29 | "access": "public"
30 | },
31 | "authors": [
32 | "PeachScript (https://github.com/PeachScript)"
33 | ]
34 | }
35 |
--------------------------------------------------------------------------------
/suites/boilerplate/src/cli.ts:
--------------------------------------------------------------------------------
1 | import { chalk, isLocalDev, yParser } from '@umijs/utils';
2 |
3 | const args = yParser(process.argv.slice(2), {
4 | alias: {
5 | version: ['v'],
6 | help: ['h'],
7 | },
8 | boolean: ['version'],
9 | });
10 |
11 | if (args.version && !args._[0]) {
12 | args._[0] = 'version';
13 | const local = isLocalDev() ? chalk.cyan('@local') : '';
14 | const { name, version } = require('../package.json');
15 | console.log(`${name}@${version}${local}`);
16 | } else {
17 | require('./')
18 | .default({
19 | cwd: process.cwd(),
20 | args,
21 | })
22 | .catch((err: Error) => {
23 | console.error(`Create failed, ${err.message}`);
24 | console.error(err);
25 | });
26 | }
27 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/react/.dumirc.ts.tpl:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'dumi';
2 |
3 | export default defineConfig({
4 | outputPath: 'docs-dist',
5 | themeConfig: {
6 | name: '{{{ name }}}',
7 | },
8 | });
9 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/react/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 |
12 | [*.md]
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/react/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: require.resolve('@umijs/lint/dist/config/eslint'),
3 | };
4 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/react/.fatherrc.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'father';
2 |
3 | export default defineConfig({
4 | // more father config: https://github.com/umijs/father/blob/master/docs/config.md
5 | esm: { output: 'dist' },
6 | });
7 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/react/.gitignore.tpl:
--------------------------------------------------------------------------------
1 | node_modules
2 | /dist
3 | .dumi/tmp
4 | .dumi/tmp-test
5 | .dumi/tmp-production
6 | .DS_Store
7 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/react/.husky/commit-msg:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 | . "$(dirname -- "$0")/_/husky.sh"
3 |
4 | npx commitlint --edit "${1}"
5 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/react/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 | . "$(dirname -- "$0")/_/husky.sh"
3 |
4 | npx lint-staged
5 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/react/.prettierignore:
--------------------------------------------------------------------------------
1 | /dist
2 | *.yaml
3 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/react/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | pluginSearchDirs: false,
3 | plugins: [
4 | require.resolve('prettier-plugin-organize-imports'),
5 | require.resolve('prettier-plugin-packagejson'),
6 | ],
7 | printWidth: 80,
8 | proseWrap: 'never',
9 | singleQuote: true,
10 | trailingComma: 'all',
11 | overrides: [
12 | {
13 | files: '*.md',
14 | options: {
15 | proseWrap: 'preserve',
16 | },
17 | },
18 | ],
19 | };
20 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/react/.stylelintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@umijs/lint/dist/config/stylelint"
3 | }
4 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/react/LICENSE.tpl:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) {{{ author }}}
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/react/README.md.tpl:
--------------------------------------------------------------------------------
1 | # {{{ name }}}
2 |
3 | [](https://npmjs.org/package/{{{ name }}})
4 | [](https://npmjs.org/package/{{{ name }}})
5 |
6 | {{{ description }}}
7 |
8 | ## Usage
9 |
10 | TODO
11 |
12 | ## Options
13 |
14 | TODO
15 |
16 | ## Development
17 |
18 | ```bash
19 | # install dependencies
20 | $ {{ npmClient }} install
21 |
22 | # develop library by docs demo
23 | $ {{ npmClient }} start
24 |
25 | # build library source code
26 | $ {{ npmClient }} run build
27 |
28 | # build library source code in watch mode
29 | $ {{ npmClient }} run build:watch
30 |
31 | # build docs
32 | $ {{ npmClient }} run docs:build
33 |
34 | # Locally preview the production build.
35 | $ {{ npmClient }} run docs:preview
36 |
37 | # check your project for potential problems
38 | $ {{ npmClient }} run doctor
39 | ```
40 |
41 | ## LICENSE
42 |
43 | MIT
44 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/react/docs/guide.md:
--------------------------------------------------------------------------------
1 | This is a guide example.
2 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/react/docs/index.md.tpl:
--------------------------------------------------------------------------------
1 | ---
2 | hero:
3 | title: library
4 | description: {{{ description }}}
5 | actions:
6 | - text: Hello
7 | link: /
8 | - text: World
9 | link: /
10 | features:
11 | - title: Hello
12 | emoji: 💎
13 | description: Put hello description here
14 | - title: World
15 | emoji: 🌈
16 | description: Put world description here
17 | - title: '!'
18 | emoji: 🚀
19 | description: Put ! description here
20 | ---
21 |
22 | {{{ name }}}
23 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/react/src/Foo/index.md.tpl:
--------------------------------------------------------------------------------
1 | # Foo
2 |
3 | This is an example component.
4 |
5 | ```jsx
6 | import { Foo } from '{{{ name }}}';
7 |
8 | export default () =>
9 | ```
10 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/react/src/Foo/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { type FC } from 'react';
2 |
3 | const Foo: FC<{ title: string }> = (props) => {props.title}
;
4 |
5 | export default Foo;
6 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/react/src/index.ts:
--------------------------------------------------------------------------------
1 | export { default as Foo } from './Foo';
2 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/react/tsconfig.json.tpl:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "strict": true,
4 | "declaration": true,
5 | "skipLibCheck": true,
6 | "esModuleInterop": true,
7 | "jsx": "react",
8 | "baseUrl": "./",
9 | "paths": {
10 | "@@/*": [".dumi/tmp/*"],
11 | "{{{ name }}}": ["src"],
12 | "{{{ name }}}/*": ["src/*", "*"]
13 | }
14 | },
15 | "include": [".dumirc.ts", "src/**/*"]
16 | }
17 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/site/.dumirc.ts.tpl:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'dumi';
2 |
3 | export default defineConfig({
4 | themeConfig: {
5 | name: '{{{ name }}}',
6 | },
7 | });
8 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/site/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 |
12 | [*.md]
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/site/.gitignore.tpl:
--------------------------------------------------------------------------------
1 | node_modules
2 | /dist
3 | .dumi/tmp
4 | .dumi/tmp-production
5 | .DS_Store
6 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/site/.husky/commit-msg:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 | . "$(dirname -- "$0")/_/husky.sh"
3 |
4 | npx commitlint --edit "${1}"
5 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/site/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 | . "$(dirname -- "$0")/_/husky.sh"
3 |
4 | npx lint-staged
5 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/site/.prettierignore:
--------------------------------------------------------------------------------
1 | .dumi/tmp
2 | .dumi/tmp-production
3 | *.yaml
4 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/site/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | printWidth: 80,
3 | proseWrap: 'never',
4 | singleQuote: true,
5 | trailingComma: 'all',
6 | overrides: [
7 | {
8 | files: '*.md',
9 | options: {
10 | proseWrap: 'preserve',
11 | },
12 | },
13 | ],
14 | };
15 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/site/LICENSE.tpl:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) {{{ author }}}
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/site/README.md.tpl:
--------------------------------------------------------------------------------
1 | # {{{ name }}}
2 |
3 | A static site base on [dumi](https://d.umijs.org).
4 |
5 | ## Development
6 |
7 | ```bash
8 | # install dependencies
9 | $ {{ npmClient }} install
10 |
11 | # start dev server
12 | $ {{ npmClient }} start
13 |
14 | # build docs
15 | $ {{ npmClient }} run build
16 |
17 | # Locally preview the production build
18 | $ {{ npmClient }} run preview
19 | ```
20 |
21 | ## LICENSE
22 |
23 | MIT
24 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/site/docs/guide.md:
--------------------------------------------------------------------------------
1 | This is a guide example.
2 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/site/docs/index.md.tpl:
--------------------------------------------------------------------------------
1 | ---
2 | title: A static site based on dumi
3 | hero:
4 | title: Site
5 | description: {{{ description }}}
6 | actions:
7 | - text: Hello
8 | link: /
9 | - text: World
10 | link: /
11 | features:
12 | - title: Hello
13 | emoji: 💎
14 | description: Put hello description here
15 | - title: World
16 | emoji: 🌈
17 | description: Put world description here
18 | - title: '!'
19 | emoji: 🚀
20 | description: Put ! description here
21 | ---
22 |
23 | {{{ name }}}
24 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/site/package.json.tpl:
--------------------------------------------------------------------------------
1 | {
2 | "name": "{{{ name }}}",
3 | "version": "0.0.1",
4 | "description": "{{{ description }}}",
5 | "scripts": {
6 | "start": "npm run dev",
7 | "dev": "dumi dev",
8 | "build": "dumi build",
9 | "preview": "dumi preview",
10 | "prepare": "husky install && dumi setup"
11 | },
12 | "authors": [{{#author}}
13 | "{{{ author }}}"
14 | {{/author}}],
15 | "license": "MIT",
16 | "commitlint": {
17 | "extends": [
18 | "@commitlint/config-conventional"
19 | ]
20 | },
21 | "lint-staged": {
22 | "*.{md,json}": [
23 | "prettier --write --no-error-on-unmatched-pattern"
24 | ]
25 | },
26 | "devDependencies": {
27 | "@commitlint/cli": "^17.1.2",
28 | "@commitlint/config-conventional": "^17.1.0",
29 | "dumi": "{{{version}}}",
30 | "husky": "^8.0.1",
31 | "lint-staged": "^13.0.3",
32 | "prettier": "^2.7.1"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/site/tsconfig.json.tpl:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "strict": true,
4 | "skipLibCheck": true,
5 | "esModuleInterop": true,
6 | "baseUrl": "./",
7 | "paths": {
8 | "@@/*": [".dumi/tmp/*"]
9 | }
10 | },
11 | "include": [".dumirc.ts"]
12 | }
13 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/theme/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 |
12 | [*.md]
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/theme/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: require.resolve('@umijs/lint/dist/config/eslint'),
3 | };
4 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/theme/.fatherrc.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'father';
2 |
3 | export default defineConfig({
4 | plugins: ['father-plugin-dumi-theme'],
5 | });
6 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/theme/.gitignore.tpl:
--------------------------------------------------------------------------------
1 | /node_modules
2 | /dist
3 | /example/node_modules
4 | /example/dist
5 | /example/.dumi/tmp
6 | /example/.dumi/tmp-production
7 | /example/.dumi/theme
8 | .DS_Store
9 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/theme/.husky/commit-msg:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 | . "$(dirname -- "$0")/_/husky.sh"
3 |
4 | npx commitlint --edit "${1}"
5 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/theme/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 | . "$(dirname -- "$0")/_/husky.sh"
3 |
4 | npx lint-staged
5 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/theme/.prettierignore:
--------------------------------------------------------------------------------
1 | /dist
2 | *.yaml
3 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/theme/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | printWidth: 80,
3 | proseWrap: 'never',
4 | singleQuote: true,
5 | trailingComma: 'all',
6 | overrides: [
7 | {
8 | files: '*.md',
9 | options: {
10 | proseWrap: 'preserve',
11 | },
12 | },
13 | ],
14 | };
15 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/theme/.stylelintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@umijs/lint/dist/config/stylelint"
3 | }
4 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/theme/LICENSE.tpl:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) {{{ author }}}
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/theme/example/.dumirc.ts.tpl:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'dumi';
2 |
3 | export default defineConfig({
4 | // disable mfsu for HMR
5 | mfsu: false,
6 | // pass theme config
7 | themeConfig: {
8 | hello: 'world',
9 | },
10 | });
11 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/theme/example/docs/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | hero:
3 | title: Example
4 | ---
5 |
6 | Homepage test
7 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/theme/example/docs/test.md:
--------------------------------------------------------------------------------
1 | Other test page
2 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/theme/src/builtins/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umijs/dumi/9c73ff231ccaa1a73e75797c80e62c3c5fc63181/suites/boilerplate/templates/theme/src/builtins/.gitkeep
--------------------------------------------------------------------------------
/suites/boilerplate/templates/theme/src/layouts/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umijs/dumi/9c73ff231ccaa1a73e75797c80e62c3c5fc63181/suites/boilerplate/templates/theme/src/layouts/.gitkeep
--------------------------------------------------------------------------------
/suites/boilerplate/templates/theme/src/locales/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umijs/dumi/9c73ff231ccaa1a73e75797c80e62c3c5fc63181/suites/boilerplate/templates/theme/src/locales/.gitkeep
--------------------------------------------------------------------------------
/suites/boilerplate/templates/theme/src/plugin/index.ts.tpl:
--------------------------------------------------------------------------------
1 | import type { IApi } from 'dumi';
2 |
3 | export default (api: IApi) => {
4 | api.describe({ key: `dumi-theme:${require('../../package.json').name}` });
5 | // TODO: add your plugin code here
6 | };
7 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/theme/src/slots/HeroTitle/index.tsx.tpl:
--------------------------------------------------------------------------------
1 | import { useSiteData } from 'dumi';
2 | import DumiHeroTitle from 'dumi/theme-default/slots/HeroTitle';
3 | import React from 'react';
4 | import type { IThemeConfig } from '../../types';
5 |
6 | /**
7 | * example for add sup to the default hero title
8 | */
9 | const HeroTitle: typeof DumiHeroTitle = (props) => {
10 | const themeConfig = useSiteData().themeConfig as IThemeConfig;
11 |
12 | return (
13 | <>
14 |
15 | {themeConfig.hello}
16 | >
17 | );
18 | };
19 |
20 | export default HeroTitle;
21 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/theme/src/types.ts:
--------------------------------------------------------------------------------
1 | export interface IThemeConfig {
2 | // TODO: put your definitions here
3 | hello: string;
4 | }
5 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/theme/tsconfig.json.tpl:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "strict": true,
4 | "declaration": true,
5 | "esModuleInterop": true,
6 | "jsx": "react",
7 | "skipLibCheck": true,
8 | "baseUrl": "./",
9 | "paths": {
10 | "@@/*": ["example/.dumi/tmp/*"]
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/vue/.dumirc.ts.tpl:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'dumi';
2 |
3 | export default defineConfig({
4 | apiParser: {},
5 | resolve: {
6 | entryFile: 'src/index.ts',
7 | },
8 | outputPath: 'docs-dist',
9 | themeConfig: {
10 | name: '{{{ name }}}',
11 | },
12 | presets: [require.resolve('@dumijs/preset-vue')],
13 | });
14 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/vue/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 |
12 | [*.md]
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/vue/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: ['plugin:vue/vue3-recommended'],
3 | parser: 'vue-eslint-parser',
4 | parserOptions: {
5 | parser: '@typescript-eslint/parser',
6 | sourceType: 'module',
7 | ecmaVersion: 2020,
8 | ecmaFeatures: {
9 | jsx: true,
10 | },
11 | },
12 | rules: {},
13 | };
14 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/vue/.gitignore.tpl:
--------------------------------------------------------------------------------
1 | node_modules
2 | /dist
3 | .dumi/tmp
4 | .dumi/tmp-test
5 | .dumi/tmp-production
6 | .DS_Store
7 | /coverage
8 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/vue/.husky/commit-msg:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 | . "$(dirname -- "$0")/_/husky.sh"
3 |
4 | npx commitlint --edit "${1}"
5 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/vue/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 | . "$(dirname -- "$0")/_/husky.sh"
3 |
4 | npx lint-staged
5 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/vue/.prettierignore:
--------------------------------------------------------------------------------
1 | /dist
2 | *.yaml
3 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/vue/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | pluginSearchDirs: false,
3 | plugins: [
4 | require.resolve('prettier-plugin-organize-imports'),
5 | require.resolve('prettier-plugin-packagejson'),
6 | ],
7 | printWidth: 80,
8 | proseWrap: 'never',
9 | singleQuote: true,
10 | trailingComma: 'all',
11 | overrides: [
12 | {
13 | files: '*.md',
14 | options: {
15 | proseWrap: 'preserve',
16 | },
17 | },
18 | ],
19 | };
20 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/vue/.stylelintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@umijs/lint/dist/config/stylelint"
3 | }
4 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/vue/LICENSE.tpl:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) {{{ author }}}
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/vue/README.md.tpl:
--------------------------------------------------------------------------------
1 | # {{{ name }}}
2 |
3 | [](https://npmjs.org/package/{{{ name }}})
4 | [](https://npmjs.org/package/{{{ name }}})
5 |
6 | {{{ description }}}
7 |
8 | ## Usage
9 |
10 | First, introduce css file:
11 |
12 | ```ts
13 | import '{{ name }}/dist/style.css';
14 | ```
15 |
16 | Then, introduce components:
17 |
18 | ```html
19 |
22 | ```
23 |
24 | ## Options
25 |
26 | TODO
27 |
28 | ## Development
29 |
30 | ```bash
31 | # install dependencies
32 | $ {{ npmClient }} install
33 |
34 | # develop library by docs demo
35 | $ {{ npmClient }} start
36 |
37 | # build library source code
38 | $ {{ npmClient }} run build
39 |
40 | # build library source code in watch mode
41 | $ {{ npmClient }} run build:watch
42 |
43 | # build docs
44 | $ {{ npmClient }} run docs:build
45 |
46 | # Locally preview the production build.
47 | $ {{ npmClient }} run docs:preview
48 |
49 | # check your project for potential problems
50 | $ {{ npmClient }} run doctor
51 |
52 | # Test
53 | $ {{ npmClient }} test
54 |
55 | # Coverage
56 | $ {{ npmClient }} test:cov
57 |
58 | # Lint
59 | $ {{ npmClient }} lint
60 | ```
61 |
62 | ## LICENSE
63 |
64 | MIT
65 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/vue/docs/guide.md:
--------------------------------------------------------------------------------
1 | This is a guide example.
2 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/vue/docs/index.md.tpl:
--------------------------------------------------------------------------------
1 | ---
2 | hero:
3 | title: library
4 | description: {{{ description }}}
5 | actions:
6 | - text: Hello
7 | link: /
8 | - text: Vue
9 | link: /
10 | features:
11 | - title: Hello
12 | emoji: 💎
13 | description: Put hello description here
14 | - title: Vue
15 | emoji: 🌈
16 | description: Put world description here
17 | - title: '!'
18 | emoji: 🚀
19 | description: Put ! description here
20 | ---
21 |
22 | {{{ name }}}
23 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/vue/src/Bar/Bar.test.ts.tpl:
--------------------------------------------------------------------------------
1 | import { mount, type VueWrapper } from '@vue/test-utils';
2 | import { beforeEach, describe, expect, it } from 'vitest';
3 | import Bar from './RootBar.vue';
4 |
5 | describe('Bar', () => {
6 | let wrapper: VueWrapper>;
7 |
8 | beforeEach(() => {
9 | wrapper = mount(Bar, { props: { icon: 'V' } });
10 | });
11 |
12 | it('should render', () => {
13 | expect(wrapper.html()).contain('V');
14 | });
15 | });
16 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/vue/src/Bar/RootBar.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | {{ icon }}
7 |
8 |
12 |
13 |
14 |
15 |
16 |
44 |
45 |
52 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/vue/src/Bar/index.md.tpl:
--------------------------------------------------------------------------------
1 | # Bar
2 |
3 | This is an example component of Vue SFC.
4 |
5 | ```vue
6 |
12 |
13 |
14 |
15 | Hello Vue!
16 |
17 |
18 |
19 |
26 | ```
27 | ## Bar API
28 |
29 | ### Props
30 |
31 |
32 |
33 | ### Slots
34 |
35 |
36 |
37 | ### Events
38 |
39 |
40 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/vue/src/Bar/index.ts:
--------------------------------------------------------------------------------
1 | export { default as Bar } from './RootBar.vue';
2 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/vue/src/Foo/Foo.less:
--------------------------------------------------------------------------------
1 | .foo {
2 | color: red;
3 | }
4 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/vue/src/Foo/Foo.test.ts.tpl:
--------------------------------------------------------------------------------
1 | import { mount } from '@vue/test-utils';
2 | import { describe, expect, it } from 'vitest';
3 | import { Foo } from './index';
4 |
5 | describe('Foo', () => {
6 | it('should render', () => {
7 | const wrapper = mount(Foo, { props: { title: 'foo' } });
8 | expect(wrapper.html()).contain('foo');
9 | });
10 | });
11 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/vue/src/Foo/index.md.tpl:
--------------------------------------------------------------------------------
1 | # Foo
2 |
3 | This is an example component of Vue JSX.
4 |
5 | ```jsx
6 | import { Foo } from '{{{ name }}}';
7 |
8 | export default () =>
9 | ```
10 | ## Foo API
11 |
12 | ### Props
13 |
14 |
15 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/vue/src/Foo/index.tsx:
--------------------------------------------------------------------------------
1 | import { defineComponent } from 'vue';
2 | import './Foo.less';
3 |
4 | export const Foo = defineComponent({
5 | props: {
6 | /**
7 | * @description 标题
8 | */
9 | title: {
10 | type: String,
11 | default: '',
12 | },
13 | },
14 | setup({ title }) {
15 | return () => {title}
;
16 | },
17 | });
18 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/vue/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './Bar';
2 | export * from './Foo';
3 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/vue/tsconfig.build.json.tpl:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "emitDeclarationOnly": true,
5 | "declarationDir": "./dist/typings",
6 | "lib": ["esnext", "dom"]
7 | },
8 | "include": ["src/**/*"],
9 | "exclude": ["src/**/*.test.*"]
10 | }
11 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/vue/tsconfig.json.tpl:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "strict": true,
4 | "declaration": true,
5 | "skipLibCheck": true,
6 | "esModuleInterop": true,
7 | "jsx": "preserve",
8 | "jsxImportSource": "vue",
9 | "strictNullChecks": false,
10 | "baseUrl": "./",
11 | "paths": {
12 | "@@/*": [".dumi/tmp/*"],
13 | "{{{ name }}}": ["src"],
14 | "{{{ name }}}/*": ["src/*", "*"]
15 | }
16 | },
17 | "include": [".dumirc.ts", "src/**/*"]
18 | }
19 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/vue/vite.config.mts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite';
2 | import { resolve } from 'node:path';
3 | import vue from '@vitejs/plugin-vue';
4 | import vueJsx from '@vitejs/plugin-vue-jsx';
5 |
6 |
7 | const externals = ['vue'];
8 |
9 | export default defineConfig({
10 | plugins: [
11 | vue(),
12 | vueJsx(),
13 | ],
14 | build: {
15 | lib: {
16 | entry: resolve(__dirname, 'src/index.ts'),
17 | name: 'index',
18 | fileName: 'index',
19 | },
20 | rollupOptions: {
21 | external: [...externals],
22 | output: {
23 | globals: {
24 | 'vue': 'Vue',
25 | },
26 | },
27 | },
28 | outDir: 'dist',
29 | },
30 | resolve: {
31 | dedupe: ['vue'],
32 | },
33 | optimizeDeps: {
34 | include: [...externals],
35 | },
36 | });
37 |
--------------------------------------------------------------------------------
/suites/boilerplate/templates/vue/vitest.config.mts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vitest/config';
2 | import vue from '@vitejs/plugin-vue';
3 | import vueJsx from '@vitejs/plugin-vue-jsx';
4 |
5 |
6 | export default defineConfig({
7 | plugins: [
8 | vue(),
9 | vueJsx(),
10 | ],
11 | test: {
12 | environment: 'happy-dom',
13 | include: ['./**/*.test.{ts,js,tsx}'],
14 | coverage: {
15 | provider: 'v8',
16 | reporter: ['html', 'text', 'json'],
17 | },
18 | },
19 | });
20 |
--------------------------------------------------------------------------------
/suites/boilerplate/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": false
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/suites/dumi-vue-meta/.fatherrc.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'father';
2 |
3 | export default defineConfig({
4 | cjs: {},
5 | });
6 |
--------------------------------------------------------------------------------
/suites/dumi-vue-meta/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | /dist
3 | .DS_Store
4 |
--------------------------------------------------------------------------------
/suites/dumi-vue-meta/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@dumijs/vue-meta",
3 | "version": "2.4.14",
4 | "description": "Extracting the metadata of Vue components more effectively",
5 | "keywords": [
6 | "vue",
7 | "metadata",
8 | "component"
9 | ],
10 | "license": "MIT",
11 | "main": "dist/cjs/index.js",
12 | "types": "dist/cjs/index.d.ts",
13 | "files": [
14 | "dist",
15 | "compiled"
16 | ],
17 | "scripts": {
18 | "build": "father build",
19 | "build:deps": "father prebundle",
20 | "dev": "father dev",
21 | "test": "vitest"
22 | },
23 | "dependencies": {
24 | "@volar/typescript": "^1.10.4",
25 | "@vue/language-core": "^1.8.19",
26 | "dumi-assets-types": "workspace:*",
27 | "typesafe-path": "^0.2.2",
28 | "vue-component-type-helpers": "^2.0.21"
29 | },
30 | "devDependencies": {
31 | "eslint-plugin-vue": "^9.17.0",
32 | "father": "^4.1.9",
33 | "typescript": "^5.2.2",
34 | "vue": "^3.3.4",
35 | "vue-types": "^5.1.1"
36 | },
37 | "peerDependencies": {
38 | "typescript": "*"
39 | },
40 | "peerDependenciesMeta": {
41 | "typescript": {
42 | "optional": true
43 | }
44 | },
45 | "publishConfig": {
46 | "access": "public"
47 | },
48 | "authors": [
49 | "jeffwcx@icloud.com"
50 | ]
51 | }
52 |
--------------------------------------------------------------------------------
/suites/dumi-vue-meta/src/checker/index.ts:
--------------------------------------------------------------------------------
1 | export * from './Project';
2 | export * from './TypeCheckService';
3 | export type { VueLanguageService } from './createVueLanguageService';
4 | export type { Repo } from './repo';
5 |
--------------------------------------------------------------------------------
/suites/dumi-vue-meta/src/schemaResolver/custom/index.ts:
--------------------------------------------------------------------------------
1 | export * from './externalSymbol';
2 | export * from './vueOption';
3 | export * from './vueTypes';
4 |
--------------------------------------------------------------------------------
/suites/dumi-vue-meta/src/schemaResolver/custom/vueOption.ts:
--------------------------------------------------------------------------------
1 | import type { PropertyMeta, PropertySchemaResolver } from '../../types';
2 | import { getNodeOfSymbol } from '../../utils';
3 |
4 | export const vueOptionSchemaResolver: PropertySchemaResolver = (
5 | meta,
6 | { ts, targetNode, targetType },
7 | ) => {
8 | if (!targetNode || !targetType) return meta;
9 |
10 | const requiredSymbol = targetType.getProperty('required');
11 | const requiredNode = getNodeOfSymbol(requiredSymbol);
12 | if (requiredNode && ts.isPropertyAssignment(requiredNode)) {
13 | if (requiredNode.initializer.kind === ts.SyntaxKind.TrueKeyword) {
14 | meta.required = true;
15 | } else if (requiredNode.initializer.kind === ts.SyntaxKind.FalseKeyword) {
16 | meta.required = false;
17 | }
18 | }
19 | const defaultSymbol = targetType.getProperty('default');
20 | const defaultNode = getNodeOfSymbol(defaultSymbol);
21 | // If default is a function, it is too complicated. Users can set it by @default.
22 | if (defaultNode && ts.isPropertyAssignment(defaultNode)) {
23 | meta.default = defaultNode.initializer.getText();
24 | }
25 | return meta;
26 | };
27 |
--------------------------------------------------------------------------------
/suites/dumi-vue-meta/tests/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | parser: 'vue-eslint-parser',
4 | parserOptions: {
5 | parser: '@typescript-eslint/parser',
6 | sourceType: 'module',
7 | ecmaVersion: 2020,
8 | ecmaFeatures: {
9 | jsx: true,
10 | },
11 | },
12 | rules: {},
13 | };
14 |
--------------------------------------------------------------------------------
/suites/dumi-vue-meta/tests/fixtures/externalProps.ts:
--------------------------------------------------------------------------------
1 | import { PropType } from 'vue';
2 | import { object, oneOf } from 'vue-types';
3 |
4 | export const order = {
5 | type: Number,
6 | required: true,
7 | default: 0,
8 | };
9 |
10 | export const baseProps = {
11 | /**
12 | * @default {}
13 | */
14 | b: object<{ c?: string }>().def({}).isRequired,
15 | /**
16 | * @experimental
17 | */
18 | c: String as PropType<'1' | '2' | '3'>,
19 | /**
20 | * @deprecated
21 | */
22 | d: oneOf([1, 2, 3]).def(1),
23 | };
24 |
--------------------------------------------------------------------------------
/suites/dumi-vue-meta/tests/fixtures/index.ts:
--------------------------------------------------------------------------------
1 | export { default as FooSfc } from './sfc/foo.vue';
2 | export * from './tsx/index';
3 |
4 | export function fooFunc(a: string) {
5 | return a;
6 | }
7 |
--------------------------------------------------------------------------------
/suites/dumi-vue-meta/tests/fixtures/props.ts:
--------------------------------------------------------------------------------
1 | import { PropType } from 'vue';
2 | import { baseProps, order } from './externalProps';
3 |
4 | export interface AItem {
5 | id: string;
6 | }
7 |
8 | export interface A {
9 | a: AItem;
10 | }
11 |
12 | export type JSXComponent = Component & {
13 | new (): ExposedApi;
14 | };
15 |
16 | export type PromiseArgs = {
17 | args: string[];
18 | };
19 |
20 | export const fooProps = {
21 | /**
22 | * @description 标题
23 | * @default ''
24 | */
25 | title: {
26 | type: String,
27 | required: true,
28 | default: '标题',
29 | },
30 | /**
31 | * @description 顺序
32 | */
33 | order,
34 | a: {
35 | type: Array,
36 | required: true,
37 | default: [],
38 | },
39 | ...baseProps,
40 | /**
41 | * @beta
42 | */
43 | e: [Object, Number] as PropType,
44 | onConfirm: Function as PropType<(output: { children: any[] }) => void>,
45 | /**
46 | * @since 0.0.1
47 | */
48 | dom: {
49 | type: Object as PropType,
50 | default: null,
51 | },
52 | /**
53 | * @alpha
54 | * @description
55 | */
56 | func: Function as PropType<(args: PromiseArgs) => Promise<{ type?: string }>>,
57 | };
58 |
59 | export type FooSlotsType = {
60 | /**
61 | * icon
62 | * @experimental
63 | * @description icon
64 | */
65 | icon?: any;
66 | /**
67 | * item
68 | * @deprecated
69 | */
70 | item?: { list: string[]; extra?: boolean };
71 | };
72 |
--------------------------------------------------------------------------------
/suites/dumi-vue-meta/tests/fixtures/sfc-alias/index.ts:
--------------------------------------------------------------------------------
1 | export { default as Foo } from './foo.vue';
2 |
--------------------------------------------------------------------------------
/suites/dumi-vue-meta/tests/fixtures/sfc/index.ts:
--------------------------------------------------------------------------------
1 | export { default as Foo } from './foo.vue';
2 |
--------------------------------------------------------------------------------
/suites/dumi-vue-meta/tests/fixtures/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "declaration": true,
4 | "skipLibCheck": true,
5 | "esModuleInterop": true,
6 | "baseUrl": "./",
7 | "jsx": "preserve",
8 | "jsxImportSource": "vue"
9 | },
10 | "include": ["**/*"]
11 | }
12 |
--------------------------------------------------------------------------------
/suites/dumi-vue-meta/tests/fixtures/tsx/functional.tsx:
--------------------------------------------------------------------------------
1 | import type { FunctionalComponent as FC, SetupContext, SlotsType } from 'vue';
2 |
3 | type FComponentProps = {
4 | message: string;
5 | };
6 |
7 | type FComponentEvents = {
8 | sendMessage: (message: string) => void;
9 | };
10 |
11 | type FSlots = {
12 | default?: any;
13 | };
14 |
15 | export const AnonymousFComponent: FC<
16 | FComponentProps,
17 | FComponentEvents,
18 | FSlots
19 | > = (props, { emit, slots }) => {
20 | return (
21 |
24 | );
25 | };
26 |
27 | AnonymousFComponent.displayName = 'FComponent';
28 |
29 | AnonymousFComponent.props = {
30 | message: {
31 | type: String,
32 | required: true,
33 | },
34 | };
35 |
36 | AnonymousFComponent.emits = {
37 | sendMessage: (value) => typeof value === 'string',
38 | };
39 |
40 | export function NamedFComponent(
41 | props: FComponentProps,
42 | { emit, slots }: SetupContext>,
43 | ) {
44 | return (
45 |
48 | );
49 | }
50 |
51 | NamedFComponent.displayName = 'NamedComponent';
52 |
--------------------------------------------------------------------------------
/suites/dumi-vue-meta/tests/fixtures/tsx/index.ts:
--------------------------------------------------------------------------------
1 | export type { A, AItem, PromiseArgs } from '../props';
2 | export { default as Foo } from './foo';
3 | export * from './functional';
4 | export * from './list';
5 | export * from './use';
6 |
--------------------------------------------------------------------------------
/suites/dumi-vue-meta/tests/fixtures/tsx/use.ts:
--------------------------------------------------------------------------------
1 | import { h, shallowRef, watch } from 'vue';
2 |
3 | /**
4 | * @public
5 | */
6 | export function useInternalValue(
7 | upstreamValue: () => T,
8 | updator: (upstreamValue: T, oldValue?: T) => T = (v) => v,
9 | equal: (internalValue: T, newValue: T) => boolean = (i, n) => i === n,
10 | ) {
11 | const internalValue = shallowRef(updator(upstreamValue()));
12 | watch(upstreamValue, (value, oldValue) => {
13 | // user may have updated the internal value
14 | if (equal(internalValue.value, value)) return;
15 | internalValue.value = updator(value, oldValue);
16 | });
17 | return internalValue;
18 | }
19 | /**
20 | * This function will be considered a composition function
21 | */
22 | export function useVNode() {
23 | return h('div');
24 | }
25 |
26 | /**
27 | * @component
28 | * @description
29 | * Only if it is marked `@component` can it be considered a Functional Component,
30 | * otherwise it will be a plain function
31 | */
32 | export function InternalComponent(props: { a: string }) {
33 | return h('div', props.a);
34 | }
35 |
--------------------------------------------------------------------------------
/suites/dumi-vue-meta/tests/transformer.test.ts:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 | import { afterAll, expect, test } from 'vitest';
3 | import type { MetaCheckerOptions } from '../src/index';
4 | import {
5 | createProject,
6 | dumiTransformer,
7 | vueTypesSchemaResolver,
8 | } from '../src/index';
9 | import { rootPath, tsconfigPath } from './utils';
10 |
11 | const checkerOptions: MetaCheckerOptions = {
12 | propertyResovlers: [vueTypesSchemaResolver],
13 | gitRevision: 'main', // pin to the main branch
14 | externalSymbolLinkMappings: {
15 | typescript: {
16 | Promise:
17 | 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise',
18 | },
19 | },
20 | };
21 |
22 | const project = createProject({
23 | rootPath,
24 | tsconfigPath,
25 | checkerOptions,
26 | });
27 |
28 | test('dumi-assets-types transformer', () => {
29 | const entry = path.resolve(__dirname, 'fixtures/index.ts');
30 | const meta = project.service.getComponentLibraryMeta(entry, dumiTransformer);
31 | expect(meta).toMatchSnapshot();
32 | });
33 |
34 | afterAll(() => {
35 | project.close();
36 | });
37 |
--------------------------------------------------------------------------------
/suites/dumi-vue-meta/tests/utils.ts:
--------------------------------------------------------------------------------
1 | import path from 'node:path';
2 | import { ComponentItemMeta } from '../src';
3 |
4 | export function toRecord(metaArr: ComponentItemMeta[]) {
5 | return metaArr.reduce((acc, prop) => {
6 | acc[prop.name] = prop;
7 | return acc;
8 | }, {} as Record);
9 | }
10 |
11 | export const rootPath = path.resolve(__dirname, '../../../');
12 | export const fixturesPath = path.resolve(__dirname, './fixtures');
13 | export const entry = path.resolve(__dirname, 'fixtures/index.ts');
14 | export const tsconfigPath = path.resolve(__dirname, 'fixtures/tsconfig.json');
15 |
--------------------------------------------------------------------------------
/suites/dumi-vue-meta/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2021",
4 | "module": "NodeNext",
5 | "strict": true,
6 | "declaration": true,
7 | "skipLibCheck": true,
8 | "esModuleInterop": true,
9 | "baseUrl": "./"
10 | },
11 | "include": ["**/*"],
12 | "exclude": ["tests/fixtures"]
13 | }
14 |
--------------------------------------------------------------------------------
/suites/father-plugin/.fatherrc.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'father';
2 |
3 | export default defineConfig({
4 | cjs: { output: 'dist' },
5 | });
6 |
--------------------------------------------------------------------------------
/suites/father-plugin/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022-present UmiJS Team
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/suites/father-plugin/README.md:
--------------------------------------------------------------------------------
1 | # father-plugin-dumi-theme
2 |
3 | [](https://npmjs.org/package/father-plugin-dumi-theme) [](https://npmjs.org/package/father-plugin-dumi-theme)
4 |
5 | The father plugin for develop dumi theme package.
6 |
7 | ## Usage
8 |
9 | ## `create-dumi`
10 |
11 | It is recommended to use `create-dumi` to start developing a new dumi theme package:
12 |
13 | ```bash
14 | $ npx create-dumi # then select `theme`
15 | ```
16 |
17 | This plugin is included in the template.
18 |
19 | ### Manually
20 |
21 | Install this plugin in `devDependencies`:
22 |
23 | ```bash
24 | $ npm i father-plugin-dumi-theme -D
25 | ```
26 |
27 | Register it in `.fatherrc.ts`:
28 |
29 | ```ts
30 | import { defineConfig } from 'father';
31 |
32 | export default defineConfig({
33 | plugins: ['father-plugin-dumi-theme'],
34 | });
35 | ```
36 |
37 | ## Development
38 |
39 | ```bash
40 | $ pnpm install
41 | ```
42 |
43 | ```bash
44 | $ npm run dev
45 | $ npm run build
46 | ```
47 |
48 | ## LICENSE
49 |
50 | MIT
51 |
--------------------------------------------------------------------------------
/suites/father-plugin/example/.fatherrc.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: [require.resolve('..')],
3 | };
4 |
--------------------------------------------------------------------------------
/suites/father-plugin/example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "scripts": {
3 | "build": "father build"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/suites/father-plugin/example/src/layouts/DocLayout.ts:
--------------------------------------------------------------------------------
1 | import B from '../slots/B';
2 |
3 | export default () => B;
4 |
--------------------------------------------------------------------------------
/suites/father-plugin/example/src/plugin.ts:
--------------------------------------------------------------------------------
1 | export default () => {};
2 |
--------------------------------------------------------------------------------
/suites/father-plugin/example/src/slots/404.ts:
--------------------------------------------------------------------------------
1 | export default null;
2 |
--------------------------------------------------------------------------------
/suites/father-plugin/example/src/slots/A/index.ts:
--------------------------------------------------------------------------------
1 | console.log(import('../404'));
2 |
3 | export default null;
4 |
--------------------------------------------------------------------------------
/suites/father-plugin/example/src/slots/B.ts:
--------------------------------------------------------------------------------
1 | export { default as A } from './A';
2 |
3 | export default require('./A/index');
4 |
--------------------------------------------------------------------------------
/suites/father-plugin/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "father-plugin-dumi-theme",
3 | "version": "1.0.0-rc.1",
4 | "description": "The father plugin for develop dumi theme package",
5 | "keywords": [
6 | "father",
7 | "father-plugin",
8 | "dumi",
9 | "theme"
10 | ],
11 | "license": "MIT",
12 | "main": "dist/index.js",
13 | "files": [
14 | "dist"
15 | ],
16 | "scripts": {
17 | "build": "father build",
18 | "dev": "father dev"
19 | },
20 | "devDependencies": {
21 | "father": "^4.1.0"
22 | },
23 | "peerDependencies": {
24 | "father": "^4.1.0"
25 | },
26 | "publishConfig": {
27 | "access": "public"
28 | },
29 | "authors": [
30 | "PeachScript "
31 | ]
32 | }
33 |
--------------------------------------------------------------------------------
/suites/father-plugin/src/babelPlugins/transformRelativeSlots.ts:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 |
3 | function replacePath(nodePath: any, state: any) {
4 | if (
5 | nodePath.node.source &&
6 | // only process relative path
7 | nodePath.node.source.value.startsWith('.')
8 | ) {
9 | const finalPath = path
10 | .join(path.dirname(state.filename), nodePath.node.source.value)
11 | .replace(/\\/g, '/');
12 |
13 | // replace if final path is a slot component
14 | if (/\/slots\/[A-Z\d][^/]*(\/index)?$/.test(finalPath)) {
15 | nodePath.node.source.value = finalPath.replace(
16 | /.+(\/slots\/.+?)$/,
17 | 'dumi/theme$1',
18 | );
19 | }
20 | }
21 | }
22 |
23 | /**
24 | * babel plugin for transform relative slot imports to `dumi/theme/slots`
25 | * to make sure theme slots can be override by user's local theme
26 | */
27 | export default function transformRelativeSlots() {
28 | return {
29 | visitor: {
30 | ImportDeclaration: replacePath,
31 | ExportAllDeclaration: replacePath,
32 | ExportNamedDeclaration: replacePath,
33 | CallExpression(nodePath: any, state: any) {
34 | if (
35 | nodePath.node.callee?.name === 'require' ||
36 | nodePath.node.callee?.type === 'Import'
37 | ) {
38 | replacePath({ node: { source: nodePath.node.arguments[0] } }, state);
39 | }
40 | },
41 | },
42 | };
43 | }
44 |
--------------------------------------------------------------------------------
/suites/father-plugin/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "strict": true,
4 | "skipLibCheck": true,
5 | "esModuleInterop": true,
6 | "baseUrl": "./"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/suites/preset-vue/.fatherrc.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'father';
2 |
3 | export default defineConfig({
4 | cjs: {
5 | output: 'dist',
6 | ignores: ['src/vue/runtime/**'],
7 | },
8 | prebundle: {
9 | deps: {
10 | '@vue/babel-plugin-jsx': { dts: false },
11 | },
12 | },
13 | });
14 |
--------------------------------------------------------------------------------
/suites/preset-vue/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | /dist
3 | .DS_Store
4 | metafile-*.json
5 |
--------------------------------------------------------------------------------
/suites/preset-vue/compiled/@vue/babel-plugin-jsx/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@vue/babel-plugin-jsx",
3 | "version": "1.1.5",
4 | "license": "MIT",
5 | "author": "Amour1688 ",
6 | "_lastModified": "2024-01-07T08:42:45.781Z"
7 | }
8 |
--------------------------------------------------------------------------------
/suites/preset-vue/lib/preflight.mjs:
--------------------------------------------------------------------------------
1 | import { createApp, h, nextTick } from 'vue';
2 |
3 | function u(d){return new Promise((i,t)=>{let n=document.createElement("div");n.style.display="none",n.style.overflow="hidden",document.body.appendChild(n);let e;function r(){nextTick(()=>{e.config.errorHandler=void 0,e.unmount(),n.remove();});}e=createApp({mounted(){i(),r();},render(){return h(d)}}),e.config.warnHandler=o=>{r(),t(new Error(o));},e.config.errorHandler=o=>{r(),t(o);},e.mount(n);})}
4 |
5 | export { u as default };
6 |
--------------------------------------------------------------------------------
/suites/preset-vue/lib/renderer.mjs:
--------------------------------------------------------------------------------
1 | import { createApp } from 'vue';
2 |
3 | var _=async function(s,e){e.__css__&&setTimeout(()=>{document.querySelectorAll(`style[css-${e.__id__}]`).forEach(r=>r.remove()),document.head.insertAdjacentHTML("beforeend",``);},1);let t=createApp(e);return t.config.errorHandler=function(r){throw r},t.mount(s),()=>{t.unmount();}},o=_;
4 |
5 | export { o as default };
6 |
--------------------------------------------------------------------------------
/suites/preset-vue/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@dumijs/preset-vue",
3 | "version": "2.4.14",
4 | "description": "Vue support plugins for dumi",
5 | "keywords": [
6 | "dumi",
7 | "plugin",
8 | "preset",
9 | "vue"
10 | ],
11 | "license": "MIT",
12 | "main": "dist/index.js",
13 | "types": "dist/index.d.ts",
14 | "files": [
15 | "lib",
16 | "dist",
17 | "compiled"
18 | ],
19 | "scripts": {
20 | "build": "father build",
21 | "build:deps": "father prebundle",
22 | "build:lib": "tsup --minify",
23 | "dev": "father dev",
24 | "test": "vitest"
25 | },
26 | "dependencies": {
27 | "@dumijs/vue-meta": "workspace:*",
28 | "@types/hash-sum": "^1.0.0",
29 | "@umijs/bundler-webpack": "^4.0.81",
30 | "codesandbox-import-utils": "^2.2.3",
31 | "compare-versions": "^6.1.0",
32 | "hash-sum": "^2.0.0",
33 | "vue": "^3.3.4",
34 | "vue-loader": "^17.0.1"
35 | },
36 | "devDependencies": {
37 | "@types/babel__core": "^7.20.5",
38 | "@types/babel__standalone": "^7.1.7",
39 | "@vue/babel-plugin-jsx": "^1.1.6",
40 | "dumi": "workspace:*",
41 | "father": "^4.1.9",
42 | "tsup": "^8.0.1"
43 | },
44 | "peerDependencies": {
45 | "dumi": "^2.0.0",
46 | "vue": "^3.0.0"
47 | },
48 | "publishConfig": {
49 | "access": "public"
50 | },
51 | "authors": [
52 | "jeffwcx "
53 | ]
54 | }
55 |
--------------------------------------------------------------------------------
/suites/preset-vue/src/common.ts:
--------------------------------------------------------------------------------
1 | import type { IApi } from 'dumi';
2 | import { VueApiParser, type VueParserOptions } from './atomParser';
3 |
4 | export default (api: IApi) => {
5 | api.describe({
6 | key: 'preset-vue:common',
7 | });
8 |
9 | api.modifyBabelPresetOpts((memo) => {
10 | memo.presetTypeScript = {
11 | allExtensions: true,
12 | isTSX: true,
13 | };
14 | return memo;
15 | });
16 |
17 | api.modifyConfig((memo) => {
18 | const userConfig = api.userConfig;
19 |
20 | const vueConfig = userConfig?.vue;
21 |
22 | const parserOptions: Partial = {
23 | directory: vueConfig?.directory ?? api.pkg.repository?.directory,
24 | tsconfigPath: vueConfig?.tsconfigPath,
25 | checkerOptions: vueConfig?.checkerOptions,
26 | };
27 |
28 | const entryFile = userConfig?.resolve?.entryFile;
29 | const resolveDir = api.cwd;
30 | const options = {
31 | entryFile,
32 | resolveDir,
33 | };
34 | Object.assign(options, parserOptions);
35 |
36 | if (!options.entryFile || !options.resolveDir) return memo;
37 |
38 | api.service.atomParser = VueApiParser(options as VueParserOptions);
39 | return memo;
40 | });
41 | };
42 |
--------------------------------------------------------------------------------
/suites/preset-vue/src/compiler/browser.ts:
--------------------------------------------------------------------------------
1 | import type * as Babel from '@babel/standalone';
2 | import jsx from '@vue/babel-plugin-jsx';
3 | import hashId from 'hash-sum';
4 | import { COMP_IDENTIFIER, createCompiler, resolveFilename } from './index';
5 |
6 | const { compileSFC, transformTS, toCommonJS } = createCompiler({
7 | babel: {
8 | // @ts-ignore
9 | transformSync(...args) {
10 | if (typeof window !== undefined && 'Babel' in window) {
11 | // @ts-ignore
12 | return Babel.transform(...args);
13 | }
14 | console.error('@babel/standablone is loading!');
15 | return null;
16 | },
17 | },
18 | availablePlugins: {
19 | 'vue-jsx': jsx,
20 | },
21 | });
22 |
23 | export default function compile(
24 | code: string,
25 | { filename }: { filename: string },
26 | ) {
27 | const { lang } = resolveFilename(filename);
28 | if (['js', 'jsx', 'ts', 'tsx'].includes(lang)) {
29 | return transformTS(code, filename, {
30 | lang,
31 | presets: [['env', { modules: 'cjs' }]],
32 | });
33 | }
34 | const id = hashId(filename);
35 | const compiled = compileSFC({ id, filename, code });
36 |
37 | if (Array.isArray(compiled)) {
38 | throw compiled[0];
39 | }
40 | let { js, css } = compiled;
41 | if (css) {
42 | js += `\n${COMP_IDENTIFIER}.__css__ = ${JSON.stringify(css)};`;
43 | }
44 | js += `\n${COMP_IDENTIFIER}.__id__ = "${id}";
45 | export default ${COMP_IDENTIFIER};`;
46 | return toCommonJS(js)?.code || '';
47 | }
48 |
--------------------------------------------------------------------------------
/suites/preset-vue/src/compiler/node.ts:
--------------------------------------------------------------------------------
1 | import {
2 | babelCore,
3 | babelPresetEnv,
4 | babelPresetTypeScript,
5 | } from 'dumi/tech-stack-utils';
6 | import { COMP_IDENTIFIER, createCompiler, type CompileOptions } from './index';
7 |
8 | const babel = babelCore();
9 | const env = babelPresetEnv();
10 | const typescript = babelPresetTypeScript();
11 |
12 | export const compiler = createCompiler({
13 | babel,
14 | availablePlugins: {
15 | 'vue-jsx': require.resolve('../../compiled/@vue/babel-plugin-jsx'),
16 | },
17 | availablePresets: { env, typescript },
18 | });
19 |
20 | export function compile(options: CompileOptions) {
21 | const { id, filename, code } = options;
22 | const [, lang] = filename.match(/[^.]+\.([^.]+)$/) || [];
23 | if (['js', 'jsx', 'ts', 'tsx'].includes(lang)) {
24 | return compiler.transformTS(code, filename, { lang });
25 | }
26 | const compiled = compiler.compileSFC(options);
27 |
28 | if (Array.isArray(compiled)) {
29 | return compiled;
30 | }
31 | let { js, css } = compiled;
32 | if (css) {
33 | js += `\n${COMP_IDENTIFIER}.__css__ = ${JSON.stringify(css)};`;
34 | }
35 | js += `\n${COMP_IDENTIFIER}.__id__ = "${id}";
36 | export default ${COMP_IDENTIFIER};`;
37 | return js;
38 | }
39 |
--------------------------------------------------------------------------------
/suites/preset-vue/src/index.ts:
--------------------------------------------------------------------------------
1 | import { EnableBy } from '@umijs/core/dist/types';
2 | import type { IApi } from 'dumi';
3 | import './requireHook';
4 |
5 | export default (api: IApi) => {
6 | api.describe({
7 | key: 'vue',
8 | config: {
9 | schema({ zod }) {
10 | return zod.object({
11 | directory: zod.string().optional(),
12 | tsconfigPath: zod.string().optional(),
13 | checkerOptions: zod.object({}).optional(),
14 | compiler: zod
15 | .object({
16 | babelStandaloneCDN: zod.string().optional(),
17 | })
18 | .optional(),
19 | });
20 | },
21 | },
22 | enableBy: EnableBy.config,
23 | });
24 |
25 | return {
26 | plugins: [require.resolve('./common'), require.resolve('./vue')],
27 | };
28 | };
29 |
--------------------------------------------------------------------------------
/suites/preset-vue/src/shared.ts:
--------------------------------------------------------------------------------
1 | import type { IApi } from 'dumi';
2 | import { resolve, winPath } from 'dumi/plugin-utils';
3 | import { dirname, join } from 'path';
4 |
5 | export const BABEL_STANDALONE_CDN =
6 | 'https://cdn.bootcdn.net/ajax/libs/babel-standalone/7.22.17/babel.min.js';
7 |
8 | export function getPluginPath(api: IApi, filename: string) {
9 | return winPath(
10 | join(api.paths.absTmpPath, `plugin-${api.plugin.key}`, filename),
11 | );
12 | }
13 |
14 | export function hasDep(pkg: any, dep: string) {
15 | return pkg.dependencies?.[dep] || pkg.devDependencies?.[dep];
16 | }
17 |
18 | export function getPkgPath(dep: string, cwd: string) {
19 | return dirname(
20 | resolve.sync(`${dep}/package.json`, {
21 | basedir: cwd,
22 | }),
23 | );
24 | }
25 |
26 | export function getDepVersion(opts: { pkg: any; cwd: string; dep: string }) {
27 | if (hasDep(opts.pkg, opts.dep)) {
28 | const pkgPath = getPkgPath(opts.dep, opts.cwd);
29 | return require(pkgPath).version as string;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/suites/preset-vue/src/vue/checkVersion.ts:
--------------------------------------------------------------------------------
1 | import { getDepVersion, hasDep } from '@/shared';
2 | import { compare } from 'compare-versions';
3 | import type { IApi } from 'dumi';
4 | import { chalk, logger } from 'dumi/plugin-utils';
5 |
6 | export default function checkVersion(api: IApi) {
7 | const vueVersion = getDepVersion({
8 | pkg: api.pkg,
9 | cwd: api.cwd,
10 | dep: 'vue',
11 | });
12 |
13 | if (!vueVersion) {
14 | throw new Error('Please install Vue.');
15 | }
16 | logger.info(chalk.cyan.bold(`Vue v${vueVersion}`));
17 |
18 | // before 3.3.6 vue/compiler-sfc will register typscript in browser
19 | // @vue/compiler-sfc will not register typescript
20 | const shouldInstallCompiler = compare(vueVersion, '3.3.6', '<');
21 | const hasOwnCompiler = hasDep(api.pkg, '@vue/compiler-sfc');
22 |
23 | if (shouldInstallCompiler && !hasOwnCompiler) {
24 | throw new Error(`Please install @vue/compiler-sfc v${vueVersion}`);
25 | }
26 |
27 | if (shouldInstallCompiler) {
28 | api.modifyConfig((memo) => {
29 | memo.alias = {
30 | ...memo.alias,
31 | 'vue/compiler-sfc': '@vue/compiler-sfc',
32 | };
33 | return memo;
34 | });
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/suites/preset-vue/src/vue/index.ts:
--------------------------------------------------------------------------------
1 | import type { IApi } from 'dumi';
2 | import checkVersion from './checkVersion';
3 | import registerTechStack from './techStack';
4 | import modifyWebpackConfig from './webpack';
5 |
6 | export default (api: IApi) => {
7 | api.describe({
8 | key: 'preset-vue', // dont add `:`, if use `api.writeTmpFile` in plugin
9 | });
10 |
11 | checkVersion(api);
12 |
13 | modifyWebpackConfig(api);
14 |
15 | api.modifyDefaultConfig((config) => {
16 | // feature flags https://link.vuejs.org/feature-flags.
17 | config.define = {
18 | ...config.define,
19 | __VUE_OPTIONS_API__: true,
20 | __VUE_PROD_DEVTOOLS__: false,
21 | __VUE_PROD_HYDRATION_MISMATCH_DETAILS__: false, // vue3.4
22 | };
23 | return config;
24 | });
25 |
26 | registerTechStack(api);
27 | };
28 |
--------------------------------------------------------------------------------
/suites/preset-vue/src/vue/runtime/preflight.ts:
--------------------------------------------------------------------------------
1 | import { createApp, h, nextTick, type App } from 'vue';
2 |
3 | export default function preflight(component: any) {
4 | return new Promise((resolve, reject) => {
5 | const el = document.createElement('div');
6 | el.style.display = 'none';
7 | el.style.overflow = 'hidden';
8 | document.body.appendChild(el);
9 | let app!: App;
10 | function destroy() {
11 | nextTick(() => {
12 | app.config.errorHandler = void 0;
13 | app.unmount();
14 | el.remove();
15 | });
16 | }
17 | app = createApp({
18 | mounted() {
19 | resolve();
20 | destroy();
21 | },
22 | render() {
23 | return h(component);
24 | },
25 | });
26 | app.config.warnHandler = (msg) => {
27 | destroy();
28 | reject(new Error(msg));
29 | };
30 | app.config.errorHandler = (error) => {
31 | destroy();
32 | reject(error);
33 | };
34 | app.mount(el);
35 | });
36 | }
37 |
--------------------------------------------------------------------------------
/suites/preset-vue/src/vue/runtime/renderer.ts:
--------------------------------------------------------------------------------
1 | import type { IDemoCancelableFn } from 'dumi/dist/client/theme-api';
2 | import { createApp } from 'vue';
3 |
4 | const renderer: IDemoCancelableFn = async function (canvas, component) {
5 | if (component.__css__) {
6 | setTimeout(() => {
7 | document
8 | .querySelectorAll(`style[css-${component.__id__}]`)
9 | .forEach((el) => el.remove());
10 | document.head.insertAdjacentHTML(
11 | 'beforeend',
12 | ``,
13 | );
14 | }, 1);
15 | }
16 | const app = createApp(component);
17 |
18 | app.config.errorHandler = function (err) {
19 | // This code will run in a sandbox and eventually throw runtime errors to React
20 | throw err;
21 | };
22 | app.mount(canvas);
23 | return () => {
24 | app.unmount();
25 | };
26 | };
27 |
28 | export default renderer;
29 |
--------------------------------------------------------------------------------
/suites/preset-vue/src/vue/runtime/runtimePlugin.ts:
--------------------------------------------------------------------------------
1 | import { defaultTitle, getVueApp } from './getPreviewerData';
2 |
3 | export function modifyStackBlitzData(memo: any, props: any) {
4 | const { title, description } = props;
5 | const config = {
6 | title: title || defaultTitle,
7 | description,
8 | template: 'node',
9 | files: {},
10 | dependencies: {},
11 | };
12 |
13 | const files = getVueApp(props);
14 |
15 | config.files = Object.entries(files).reduce((acc, [k, v]) => {
16 | acc[k] = v.content;
17 | return acc;
18 | }, {} as Record);
19 | Object.assign(memo, config);
20 | return memo;
21 | }
22 |
23 | export function modifyCodeSandboxData(memo: any, props: any) {
24 | Object.assign(memo, { files: getVueApp(props) });
25 | return memo;
26 | }
27 |
--------------------------------------------------------------------------------
/suites/preset-vue/src/vue/webpack/index.ts:
--------------------------------------------------------------------------------
1 | import type Config from '@umijs/bundler-webpack/compiled/webpack-5-chain';
2 | import type { IApi } from 'dumi';
3 | import { getConfig } from './config';
4 |
5 | export default function (api: IApi) {
6 | api.modifyConfig((memo) => {
7 | const enableMFSU = memo.mfsu !== false;
8 | if (enableMFSU) {
9 | const msfuConfig = memo.mfsu || {};
10 | memo.mfsu = {
11 | ...msfuConfig,
12 | shared: {
13 | ...msfuConfig.shared,
14 | vue: { singleton: true },
15 | },
16 | chainWebpack(config: Config) {
17 | getConfig(config, api);
18 | return config;
19 | },
20 | };
21 | }
22 | return memo;
23 | });
24 |
25 | api.chainWebpack((memo) => {
26 | getConfig(memo, api);
27 | });
28 | }
29 |
--------------------------------------------------------------------------------
/suites/preset-vue/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "strict": true,
4 | "declaration": true,
5 | "skipLibCheck": true,
6 | "esModuleInterop": true,
7 | "baseUrl": "./",
8 | "module": "NodeNext",
9 | "paths": {
10 | "@/*": ["src/*"]
11 | }
12 | },
13 | "exclude": ["lib", "compiled", "dist"]
14 | }
15 |
--------------------------------------------------------------------------------
/suites/preset-vue/tsup.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'tsup';
2 |
3 | export default defineConfig([
4 | {
5 | name: 'compiler',
6 | entry: {
7 | compiler: 'src/compiler/browser.ts',
8 | },
9 | format: 'esm',
10 | outDir: 'lib',
11 | target: 'esnext',
12 | platform: 'browser',
13 | noExternal: ['@vue/babel-plugin-jsx', 'hash-sum'],
14 | external: ['vue/compiler-sfc'],
15 | treeshake: true,
16 | },
17 | {
18 | name: 'renderer',
19 | entry: {
20 | renderer: 'src/vue/runtime/renderer.ts',
21 | },
22 | format: 'esm',
23 | outDir: 'lib',
24 | target: 'esnext',
25 | platform: 'browser',
26 | external: ['vue'],
27 | treeshake: true,
28 | },
29 | {
30 | name: 'preflight',
31 | entry: {
32 | preflight: 'src/vue/runtime/preflight.ts',
33 | },
34 | format: 'esm',
35 | outDir: 'lib',
36 | target: 'esnext',
37 | platform: 'browser',
38 | external: ['vue'],
39 | treeshake: true,
40 | },
41 | {
42 | name: 'previewer',
43 | entry: ['src/vue/runtime/runtimePlugin.ts'],
44 | format: 'esm',
45 | outDir: 'lib',
46 | target: 'esnext',
47 | platform: 'browser',
48 | treeshake: true,
49 | },
50 | ]);
51 |
--------------------------------------------------------------------------------
/suites/preset-vue/typings.d.ts:
--------------------------------------------------------------------------------
1 | declare module '@babel/plugin-transform-typescript';
2 | declare module '@babel/plugin-transform-modules-commonjs';
3 |
--------------------------------------------------------------------------------
/suites/theme-mobile/.fatherrc.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'father';
2 |
3 | export default defineConfig({
4 | plugins: ['father-plugin-dumi-theme'],
5 | });
6 |
--------------------------------------------------------------------------------
/suites/theme-mobile/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "dumi-theme-mobile",
3 | "version": "2.3.5",
4 | "description": "dumi-theme-mobile",
5 | "keywords": [
6 | "dumi",
7 | "dumi theme",
8 | "mobile library"
9 | ],
10 | "homepage": "https://github.com/umijs/dumi/tree/master/suites/theme-mobile#readme",
11 | "bugs": "https://github.com/umijs/dumi/issues",
12 | "repository": {
13 | "type": "git",
14 | "url": "https://github.com/umijs/dumi",
15 | "directory": "suites/theme-mobile"
16 | },
17 | "license": "MIT",
18 | "sideEffects": [
19 | "**/*.less"
20 | ],
21 | "files": [
22 | "dist"
23 | ],
24 | "scripts": {
25 | "build": "father build",
26 | "dev": "father dev"
27 | },
28 | "dependencies": {
29 | "@ant-design/icons-svg": "^4.2.1",
30 | "f2-touchemulator": "^0.0.1",
31 | "qrcode.react": "^3.1.0",
32 | "umi-hd": "^5.0.1"
33 | },
34 | "devDependencies": {
35 | "dumi": "workspace:*",
36 | "father-plugin-dumi-theme": "workspace:*"
37 | },
38 | "peerDependencies": {
39 | "dumi": "^2.2.0",
40 | "react": ">=16.8",
41 | "react-dom": ">=16.8"
42 | },
43 | "authors": [
44 | "xiaohuoni <448627663@qq.com> (https://github.com/xiaohuoni)",
45 | "Peach (https://github.com/PeachScript)"
46 | ]
47 | }
48 |
--------------------------------------------------------------------------------
/suites/theme-mobile/src/layouts/DemoLayout.less:
--------------------------------------------------------------------------------
1 | @import (reference) '../styles/variables.less';
2 |
3 | .@{prefix}-demo-layout {
4 | padding: 16px 12px;
5 | min-height: 100vh;
6 | box-sizing: border-box;
7 | }
8 |
--------------------------------------------------------------------------------
/suites/theme-mobile/src/slots/Device/index.less:
--------------------------------------------------------------------------------
1 | @import (reference) '../../styles/variables.less';
2 |
3 | .@{prefix}-device {
4 | position: relative;
5 | width: @device-width;
6 | padding-top: 300px;
7 | font-size: 0;
8 | border-top-right-radius: 3px;
9 | border-bottom-right-radius: 3px;
10 | overflow: hidden;
11 |
12 | @media @desktop {
13 | padding-top: calc(@device-ratio * 100%) !important;
14 | }
15 |
16 | > iframe {
17 | position: absolute;
18 | top: 0;
19 | inset-inline-start: 0;
20 | width: 100%;
21 | height: 100%;
22 | border: 0;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/suites/theme-mobile/src/slots/Device/index.tsx:
--------------------------------------------------------------------------------
1 | import { useSiteData } from 'dumi';
2 | import type { FC } from 'react';
3 | import React from 'react';
4 | import './index.less';
5 |
6 | interface IDeviceProps {
7 | url: string;
8 | inlineHeight?: number;
9 | }
10 |
11 | const Device: FC = (props) => {
12 | const {
13 | themeConfig: { deviceWidth },
14 | } = useSiteData();
15 |
16 | return (
17 |
24 |
25 |
26 | );
27 | };
28 |
29 | export default Device;
30 |
--------------------------------------------------------------------------------
/suites/theme-mobile/src/slots/PreviewerActions/index.less:
--------------------------------------------------------------------------------
1 | @import (reference) '../../styles/variables.less';
2 |
3 | .@{prefix}-previewer {
4 | &-action-qrcode {
5 | position: relative;
6 |
7 | > canvas {
8 | position: absolute;
9 | z-index: 99999;
10 | bottom: 120%;
11 | left: 50%;
12 | border: 4px solid #fff;
13 | box-shadow: 0 2px 10px rgba(0, 0, 0, 10%);
14 | box-sizing: content-box;
15 | transition: all 0.2s ease-in-out;
16 | transform: translateX(-50%) scale(0);
17 | transform-origin: center bottom;
18 | }
19 |
20 | &:hover > canvas,
21 | &:focus > canvas {
22 | transform: translateX(-50%) scale(1);
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/suites/theme-mobile/src/styles/variables.less:
--------------------------------------------------------------------------------
1 | @import (reference) 'dumi/theme-default/styles/variables.less';
2 |
3 | @prefix: dumi-mobile;
4 | // iPhone non-infinity-display ratio
5 | @device-ratio: 1.78;
6 | @device-width: 375px;
7 |
--------------------------------------------------------------------------------
/suites/theme-mobile/src/types.ts:
--------------------------------------------------------------------------------
1 | export interface IThemeConfig {
2 | /**
3 | * hd solution configurations
4 | * @note base on https://github.com/umijs/umi-hd
5 | */
6 | hd?: {
7 | rules: {
8 | minWidth?: number;
9 | maxWidth?: number;
10 | mode: 'vl' | 'flex' | 'vw' | 'vh';
11 | options?: number | [number, number];
12 | }[];
13 | };
14 | /**
15 | * set device width in desktop
16 | */
17 | deviceWidth?: number;
18 | }
19 |
--------------------------------------------------------------------------------
/suites/theme-mobile/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "baseUrl": "./",
5 | "paths": {
6 | "@@/*": ["../../.dumi/tmp/*"],
7 | "dumi/theme/*": ["src/*"]
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/suites/theme-mobile/typings.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'f2-touchemulator';
2 | declare module 'umi-hd';
3 | declare module 'umi-hd/lib/flex';
4 | declare module 'umi-hd/lib/vw';
5 | declare module 'umi-hd/lib/vh';
6 |
--------------------------------------------------------------------------------
/tech-stack-utils.d.ts:
--------------------------------------------------------------------------------
1 | import type * as BabelCore from '@umijs/bundler-utils/compiled/@babel/core';
2 | export {
3 | ILanguageMetaParser,
4 | IPatchFile,
5 | } from './dist/assetParsers/BaseParser';
6 | export {
7 | IBaseApiParserOptions,
8 | createApiParser,
9 | } from './dist/assetParsers/utils';
10 | export * from './dist/techStacks/utils';
11 | export { BabelCore };
12 | export const babelCore: () => typeof import('@umijs/bundler-utils/compiled/@babel/core');
13 | export const babelPresetTypeScript: () => BabelCore.PluginItem;
14 | export const babelPresetEnv: () => BabelCore.PluginItem;
15 |
--------------------------------------------------------------------------------
/tech-stack-utils.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | ...require('./dist/techStacks/utils'),
3 | createApiParser: require('./dist/assetParsers/utils').createApiParser,
4 | babelCore: () => require('@umijs/bundler-utils/compiled/babel/core'),
5 | babelPresetTypeScript: () =>
6 | require('@umijs/bundler-utils/compiled/babel/preset-typescript'),
7 | babelPresetEnv: () =>
8 | require('@umijs/bundler-utils/compiled/babel/preset-env'),
9 | };
10 |
--------------------------------------------------------------------------------
/tests/build.test.ts:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 | import { execa } from 'umi/plugin-utils';
3 |
4 | test('build', () => {
5 | const bin = require.resolve('../bin/dumi');
6 |
7 | execa.execaSync('node', [bin, 'build'], {
8 | cwd: path.join(__dirname, '../examples/normal'),
9 | });
10 | });
11 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "strict": true,
4 | "declaration": true,
5 | "skipLibCheck": true,
6 | "esModuleInterop": true,
7 | "resolveJsonModule": true,
8 | "jsx": "react",
9 | "baseUrl": "./",
10 | "paths": {
11 | "@/*": ["src/*"],
12 | "@@/*": [".dumi/tmp/*"],
13 | "dumi/dist/*": ["src/*"]
14 | },
15 | "types": ["vitest/globals"]
16 | },
17 | "include": [".dumi/theme/**/*", ".dumi/app.tsx", ".dumirc.ts", "src/**/*"]
18 | }
19 |
--------------------------------------------------------------------------------
/vitest.config.ts:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 | import { defineConfig } from 'vitest/config';
3 |
4 | export default defineConfig({
5 | test: {
6 | sequence: {
7 | hooks: 'parallel',
8 | },
9 | poolOptions: {
10 | forks: {
11 | singleFork: true,
12 | },
13 | },
14 | globals: true,
15 | testTimeout: 15000,
16 | alias: {
17 | '@': path.join(__dirname, 'src'),
18 | },
19 | poolMatchGlobs: [['**/__tests__/**/*.fork.test.*', 'forks']],
20 | },
21 | });
22 |
--------------------------------------------------------------------------------