├── .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 | [![NPM version](https://img.shields.io/npm/v/dumi)](https://npmjs.org/package/dumi) [![NPM downloads](https://img.shields.io/npm/dm/dumi)](https://npmjs.org/package/dumi) [![GitHub CI](https://github.com/umijs/dumi/workflows/CI/badge.svg)](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: [![dumi](https://img.shields.io/badge/docs%20by-dumi-blue)](https://github.com/umijs/dumi) 20 | 21 | ``` 22 | [![dumi](https://img.shields.io/badge/docs%20by-dumi-blue)](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 | 12312 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 | 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 | 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 |
{props.children}
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 | '<>

{"CodeGroup"}

{$$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 | ![img](https://d.umijs.org) 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 | [![NPM version](https://img.shields.io/npm/v/{{{ name }}}.svg?style=flat)](https://npmjs.org/package/{{{ name }}}) 4 | [![NPM downloads](http://img.shields.io/npm/dm/{{{ name }}}.svg?style=flat)](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 | [![NPM version](https://img.shields.io/npm/v/{{{ name }}}.svg?style=flat)](https://npmjs.org/package/{{{ name }}}) 4 | [![NPM downloads](http://img.shields.io/npm/dm/{{{ name }}}.svg?style=flat)](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 | 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 | 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 | [![NPM version](https://img.shields.io/npm/v/father-plugin-dumi-theme.svg?style=flat)](https://npmjs.org/package/father-plugin-dumi-theme) [![NPM downloads](http://img.shields.io/npm/dm/father-plugin-dumi-theme.svg?style=flat)](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 |