From 26105034da4fcce7ac883c899d781f016559310d Mon Sep 17 00:00:00 2001 From: ruki Date: Thu, 8 Nov 2018 00:38:48 +0800 Subject: switch to vuepress --- node_modules/vuepress/lib/app/.temp/enhanceApp.js | 1 + node_modules/vuepress/lib/app/.temp/override.styl | 1 + node_modules/vuepress/lib/app/.temp/routes.js | 178 ++++++ node_modules/vuepress/lib/app/.temp/siteData.js | 634 +++++++++++++++++++++ node_modules/vuepress/lib/app/.temp/style.styl | 1 + .../vuepress/lib/app/.temp/themeEnhanceApp.js | 1 + node_modules/vuepress/lib/app/SWUpdateEvent.js | 43 ++ node_modules/vuepress/lib/app/app.js | 102 ++++ node_modules/vuepress/lib/app/clientEntry.js | 74 +++ node_modules/vuepress/lib/app/components/Badge.vue | 46 ++ .../vuepress/lib/app/components/ClientOnly.js | 12 + .../vuepress/lib/app/components/Content.js | 17 + .../vuepress/lib/app/components/OutboundLink.vue | 12 + node_modules/vuepress/lib/app/dataMixin.js | 90 +++ node_modules/vuepress/lib/app/index.dev.html | 11 + node_modules/vuepress/lib/app/index.ssr.html | 17 + .../lib/app/root-mixins/activeHeaderLinks.js | 47 ++ node_modules/vuepress/lib/app/root-mixins/index.js | 7 + .../vuepress/lib/app/root-mixins/updateMeta.js | 59 ++ node_modules/vuepress/lib/app/serverEntry.js | 14 + node_modules/vuepress/lib/app/store.js | 7 + node_modules/vuepress/lib/app/util.js | 19 + node_modules/vuepress/lib/build.js | 200 +++++++ .../lib/default-theme/AlgoliaSearchBox.vue | 156 +++++ .../vuepress/lib/default-theme/DropdownLink.vue | 181 ++++++ .../lib/default-theme/DropdownTransition.vue | 33 ++ node_modules/vuepress/lib/default-theme/Home.vue | 162 ++++++ node_modules/vuepress/lib/default-theme/Layout.vue | 183 ++++++ .../vuepress/lib/default-theme/NavLink.vue | 49 ++ .../vuepress/lib/default-theme/NavLinks.vue | 151 +++++ node_modules/vuepress/lib/default-theme/Navbar.vue | 133 +++++ .../vuepress/lib/default-theme/NotFound.vue | 26 + node_modules/vuepress/lib/default-theme/Page.vue | 246 ++++++++ .../vuepress/lib/default-theme/SWUpdatePopup.vue | 85 +++ .../vuepress/lib/default-theme/SearchBox.vue | 238 ++++++++ .../vuepress/lib/default-theme/Sidebar.vue | 113 ++++ .../vuepress/lib/default-theme/SidebarButton.vue | 28 + .../vuepress/lib/default-theme/SidebarGroup.vue | 77 +++ .../vuepress/lib/default-theme/SidebarLink.vue | 91 +++ node_modules/vuepress/lib/default-theme/search.svg | 1 + .../vuepress/lib/default-theme/styles/arrow.styl | 22 + .../vuepress/lib/default-theme/styles/code.styl | 129 +++++ .../vuepress/lib/default-theme/styles/config.styl | 22 + .../lib/default-theme/styles/custom-blocks.styl | 28 + .../vuepress/lib/default-theme/styles/mobile.styl | 37 ++ .../lib/default-theme/styles/nprogress.styl | 48 ++ .../vuepress/lib/default-theme/styles/theme.styl | 190 ++++++ .../vuepress/lib/default-theme/styles/toc.styl | 3 + .../vuepress/lib/default-theme/styles/wrapper.styl | 9 + node_modules/vuepress/lib/default-theme/util.js | 216 +++++++ node_modules/vuepress/lib/dev.js | 149 +++++ node_modules/vuepress/lib/eject.js | 16 + node_modules/vuepress/lib/index.js | 4 + node_modules/vuepress/lib/markdown/component.js | 81 +++ node_modules/vuepress/lib/markdown/containers.js | 28 + node_modules/vuepress/lib/markdown/highlight.js | 47 ++ .../vuepress/lib/markdown/highlightLines.js | 49 ++ node_modules/vuepress/lib/markdown/hoist.js | 14 + node_modules/vuepress/lib/markdown/index.js | 78 +++ node_modules/vuepress/lib/markdown/lineNumbers.js | 26 + node_modules/vuepress/lib/markdown/link.js | 91 +++ node_modules/vuepress/lib/markdown/preWrapper.js | 19 + node_modules/vuepress/lib/markdown/slugify.js | 22 + node_modules/vuepress/lib/markdown/snippet.js | 43 ++ node_modules/vuepress/lib/prepare/codegen.js | 74 +++ node_modules/vuepress/lib/prepare/index.js | 51 ++ node_modules/vuepress/lib/prepare/loadConfig.js | 56 ++ .../vuepress/lib/prepare/resolveOptions.js | 194 +++++++ node_modules/vuepress/lib/prepare/util.js | 80 +++ .../vuepress/lib/service-worker/skip-waiting.js | 12 + node_modules/vuepress/lib/util/index.js | 83 +++ node_modules/vuepress/lib/util/logger.js | 47 ++ node_modules/vuepress/lib/util/parseHeaders.js | 56 ++ node_modules/vuepress/lib/util/shared.js | 7 + node_modules/vuepress/lib/webpack/ClientPlugin.js | 87 +++ node_modules/vuepress/lib/webpack/DevLogPlugin.js | 38 ++ node_modules/vuepress/lib/webpack/HeadPlugin.js | 22 + .../vuepress/lib/webpack/createBaseConfig.js | 315 ++++++++++ .../vuepress/lib/webpack/createClientConfig.js | 69 +++ .../vuepress/lib/webpack/createServerConfig.js | 58 ++ .../vuepress/lib/webpack/markdownLoader.js | 101 ++++ node_modules/vuepress/lib/webpack/noopModule.js | 1 + 82 files changed, 6268 insertions(+) create mode 100644 node_modules/vuepress/lib/app/.temp/enhanceApp.js create mode 100644 node_modules/vuepress/lib/app/.temp/override.styl create mode 100644 node_modules/vuepress/lib/app/.temp/routes.js create mode 100644 node_modules/vuepress/lib/app/.temp/siteData.js create mode 100644 node_modules/vuepress/lib/app/.temp/style.styl create mode 100644 node_modules/vuepress/lib/app/.temp/themeEnhanceApp.js create mode 100644 node_modules/vuepress/lib/app/SWUpdateEvent.js create mode 100644 node_modules/vuepress/lib/app/app.js create mode 100644 node_modules/vuepress/lib/app/clientEntry.js create mode 100644 node_modules/vuepress/lib/app/components/Badge.vue create mode 100644 node_modules/vuepress/lib/app/components/ClientOnly.js create mode 100644 node_modules/vuepress/lib/app/components/Content.js create mode 100644 node_modules/vuepress/lib/app/components/OutboundLink.vue create mode 100644 node_modules/vuepress/lib/app/dataMixin.js create mode 100644 node_modules/vuepress/lib/app/index.dev.html create mode 100644 node_modules/vuepress/lib/app/index.ssr.html create mode 100644 node_modules/vuepress/lib/app/root-mixins/activeHeaderLinks.js create mode 100644 node_modules/vuepress/lib/app/root-mixins/index.js create mode 100644 node_modules/vuepress/lib/app/root-mixins/updateMeta.js create mode 100644 node_modules/vuepress/lib/app/serverEntry.js create mode 100644 node_modules/vuepress/lib/app/store.js create mode 100644 node_modules/vuepress/lib/app/util.js create mode 100644 node_modules/vuepress/lib/build.js create mode 100644 node_modules/vuepress/lib/default-theme/AlgoliaSearchBox.vue create mode 100644 node_modules/vuepress/lib/default-theme/DropdownLink.vue create mode 100644 node_modules/vuepress/lib/default-theme/DropdownTransition.vue create mode 100644 node_modules/vuepress/lib/default-theme/Home.vue create mode 100644 node_modules/vuepress/lib/default-theme/Layout.vue create mode 100644 node_modules/vuepress/lib/default-theme/NavLink.vue create mode 100644 node_modules/vuepress/lib/default-theme/NavLinks.vue create mode 100644 node_modules/vuepress/lib/default-theme/Navbar.vue create mode 100644 node_modules/vuepress/lib/default-theme/NotFound.vue create mode 100644 node_modules/vuepress/lib/default-theme/Page.vue create mode 100644 node_modules/vuepress/lib/default-theme/SWUpdatePopup.vue create mode 100644 node_modules/vuepress/lib/default-theme/SearchBox.vue create mode 100644 node_modules/vuepress/lib/default-theme/Sidebar.vue create mode 100644 node_modules/vuepress/lib/default-theme/SidebarButton.vue create mode 100644 node_modules/vuepress/lib/default-theme/SidebarGroup.vue create mode 100644 node_modules/vuepress/lib/default-theme/SidebarLink.vue create mode 100644 node_modules/vuepress/lib/default-theme/search.svg create mode 100644 node_modules/vuepress/lib/default-theme/styles/arrow.styl create mode 100644 node_modules/vuepress/lib/default-theme/styles/code.styl create mode 100644 node_modules/vuepress/lib/default-theme/styles/config.styl create mode 100644 node_modules/vuepress/lib/default-theme/styles/custom-blocks.styl create mode 100644 node_modules/vuepress/lib/default-theme/styles/mobile.styl create mode 100644 node_modules/vuepress/lib/default-theme/styles/nprogress.styl create mode 100644 node_modules/vuepress/lib/default-theme/styles/theme.styl create mode 100644 node_modules/vuepress/lib/default-theme/styles/toc.styl create mode 100644 node_modules/vuepress/lib/default-theme/styles/wrapper.styl create mode 100644 node_modules/vuepress/lib/default-theme/util.js create mode 100644 node_modules/vuepress/lib/dev.js create mode 100644 node_modules/vuepress/lib/eject.js create mode 100644 node_modules/vuepress/lib/index.js create mode 100644 node_modules/vuepress/lib/markdown/component.js create mode 100644 node_modules/vuepress/lib/markdown/containers.js create mode 100644 node_modules/vuepress/lib/markdown/highlight.js create mode 100644 node_modules/vuepress/lib/markdown/highlightLines.js create mode 100644 node_modules/vuepress/lib/markdown/hoist.js create mode 100644 node_modules/vuepress/lib/markdown/index.js create mode 100644 node_modules/vuepress/lib/markdown/lineNumbers.js create mode 100644 node_modules/vuepress/lib/markdown/link.js create mode 100644 node_modules/vuepress/lib/markdown/preWrapper.js create mode 100644 node_modules/vuepress/lib/markdown/slugify.js create mode 100644 node_modules/vuepress/lib/markdown/snippet.js create mode 100644 node_modules/vuepress/lib/prepare/codegen.js create mode 100644 node_modules/vuepress/lib/prepare/index.js create mode 100644 node_modules/vuepress/lib/prepare/loadConfig.js create mode 100644 node_modules/vuepress/lib/prepare/resolveOptions.js create mode 100644 node_modules/vuepress/lib/prepare/util.js create mode 100644 node_modules/vuepress/lib/service-worker/skip-waiting.js create mode 100644 node_modules/vuepress/lib/util/index.js create mode 100644 node_modules/vuepress/lib/util/logger.js create mode 100644 node_modules/vuepress/lib/util/parseHeaders.js create mode 100644 node_modules/vuepress/lib/util/shared.js create mode 100644 node_modules/vuepress/lib/webpack/ClientPlugin.js create mode 100644 node_modules/vuepress/lib/webpack/DevLogPlugin.js create mode 100644 node_modules/vuepress/lib/webpack/HeadPlugin.js create mode 100644 node_modules/vuepress/lib/webpack/createBaseConfig.js create mode 100644 node_modules/vuepress/lib/webpack/createClientConfig.js create mode 100644 node_modules/vuepress/lib/webpack/createServerConfig.js create mode 100644 node_modules/vuepress/lib/webpack/markdownLoader.js create mode 100644 node_modules/vuepress/lib/webpack/noopModule.js (limited to 'node_modules/vuepress/lib') diff --git a/node_modules/vuepress/lib/app/.temp/enhanceApp.js b/node_modules/vuepress/lib/app/.temp/enhanceApp.js new file mode 100644 index 00000000..16a79b36 --- /dev/null +++ b/node_modules/vuepress/lib/app/.temp/enhanceApp.js @@ -0,0 +1 @@ +export { default } from "/Users/ruki/projects/personal/xmake-docs/src/.vuepress/enhanceApp.js" \ No newline at end of file diff --git a/node_modules/vuepress/lib/app/.temp/override.styl b/node_modules/vuepress/lib/app/.temp/override.styl new file mode 100644 index 00000000..c7969f18 --- /dev/null +++ b/node_modules/vuepress/lib/app/.temp/override.styl @@ -0,0 +1 @@ +@import("/Users/ruki/projects/personal/xmake-docs/src/.vuepress/override.styl") \ No newline at end of file diff --git a/node_modules/vuepress/lib/app/.temp/routes.js b/node_modules/vuepress/lib/app/.temp/routes.js new file mode 100644 index 00000000..625e7fb6 --- /dev/null +++ b/node_modules/vuepress/lib/app/.temp/routes.js @@ -0,0 +1,178 @@ +import Vue from 'vue' + +import ThemeLayout from '@themeLayout' +import ThemeNotFound from '@themeNotFound' +import { injectMixins } from '@app/util' +import rootMixins from '@app/root-mixins' + +injectMixins(ThemeLayout, rootMixins) +injectMixins(ThemeNotFound, rootMixins) + +export const routes = [ + { + name: "v-ddf4be195b958", + path: "/", + component: ThemeLayout, + beforeEnter: (to, from, next) => { + import("/Users/ruki/projects/personal/xmake-docs/src/README.md").then(comp => { + Vue.component("v-ddf4be195b958", comp.default) + next() + }) + } + }, + { + path: "/index.html", + redirect: "/" + }, + { + name: "v-8644851cf91dd", + path: "/api/introduction.html", + component: ThemeLayout, + beforeEnter: (to, from, next) => { + import("/Users/ruki/projects/personal/xmake-docs/src/api/introduction.md").then(comp => { + Vue.component("v-8644851cf91dd", comp.default) + next() + }) + } + }, + { + name: "v-b6ee77db64501", + path: "/guide/faq.html", + component: ThemeLayout, + beforeEnter: (to, from, next) => { + import("/Users/ruki/projects/personal/xmake-docs/src/guide/faq.md").then(comp => { + Vue.component("v-b6ee77db64501", comp.default) + next() + }) + } + }, + { + name: "v-fcba2a8896d43", + path: "/guide/getting-started.html", + component: ThemeLayout, + beforeEnter: (to, from, next) => { + import("/Users/ruki/projects/personal/xmake-docs/src/guide/getting-started.md").then(comp => { + Vue.component("v-fcba2a8896d43", comp.default) + next() + }) + } + }, + { + name: "v-cda17500cdb0c", + path: "/guide/introduction.html", + component: ThemeLayout, + beforeEnter: (to, from, next) => { + import("/Users/ruki/projects/personal/xmake-docs/src/guide/introduction.md").then(comp => { + Vue.component("v-cda17500cdb0c", comp.default) + next() + }) + } + }, + { + name: "v-745d22bfe3ef4", + path: "/guide/sponsors.html", + component: ThemeLayout, + beforeEnter: (to, from, next) => { + import("/Users/ruki/projects/personal/xmake-docs/src/guide/sponsors.md").then(comp => { + Vue.component("v-745d22bfe3ef4", comp.default) + next() + }) + } + }, + { + name: "v-360e42fea8e33", + path: "/plugin/introduction.html", + component: ThemeLayout, + beforeEnter: (to, from, next) => { + import("/Users/ruki/projects/personal/xmake-docs/src/plugin/introduction.md").then(comp => { + Vue.component("v-360e42fea8e33", comp.default) + next() + }) + } + }, + { + name: "v-1856b116362c7", + path: "/zh/", + component: ThemeLayout, + beforeEnter: (to, from, next) => { + import("/Users/ruki/projects/personal/xmake-docs/src/zh/README.md").then(comp => { + Vue.component("v-1856b116362c7", comp.default) + next() + }) + } + }, + { + path: "/zh/index.html", + redirect: "/zh/" + }, + { + name: "v-6812df0250aa3", + path: "/zh/api/introduction.html", + component: ThemeLayout, + beforeEnter: (to, from, next) => { + import("/Users/ruki/projects/personal/xmake-docs/src/zh/api/introduction.md").then(comp => { + Vue.component("v-6812df0250aa3", comp.default) + next() + }) + } + }, + { + name: "v-d3636509f8935", + path: "/zh/guide/faq.html", + component: ThemeLayout, + beforeEnter: (to, from, next) => { + import("/Users/ruki/projects/personal/xmake-docs/src/zh/guide/faq.md").then(comp => { + Vue.component("v-d3636509f8935", comp.default) + next() + }) + } + }, + { + name: "v-53a48e3593d7c", + path: "/zh/guide/getting-started.html", + component: ThemeLayout, + beforeEnter: (to, from, next) => { + import("/Users/ruki/projects/personal/xmake-docs/src/zh/guide/getting-started.md").then(comp => { + Vue.component("v-53a48e3593d7c", comp.default) + next() + }) + } + }, + { + name: "v-cd45bc6f8fdb", + path: "/zh/guide/introduction.html", + component: ThemeLayout, + beforeEnter: (to, from, next) => { + import("/Users/ruki/projects/personal/xmake-docs/src/zh/guide/introduction.md").then(comp => { + Vue.component("v-cd45bc6f8fdb", comp.default) + next() + }) + } + }, + { + name: "v-bef402580c1cc", + path: "/zh/guide/sponsors.html", + component: ThemeLayout, + beforeEnter: (to, from, next) => { + import("/Users/ruki/projects/personal/xmake-docs/src/zh/guide/sponsors.md").then(comp => { + Vue.component("v-bef402580c1cc", comp.default) + next() + }) + } + }, + { + name: "v-faf113f1b77ae", + path: "/zh/plugin/introduction.html", + component: ThemeLayout, + beforeEnter: (to, from, next) => { + import("/Users/ruki/projects/personal/xmake-docs/src/zh/plugin/introduction.md").then(comp => { + Vue.component("v-faf113f1b77ae", comp.default) + next() + }) + } + }, + { + path: '*', + component: ThemeNotFound + } +] \ No newline at end of file diff --git a/node_modules/vuepress/lib/app/.temp/siteData.js b/node_modules/vuepress/lib/app/.temp/siteData.js new file mode 100644 index 00000000..9af62f7a --- /dev/null +++ b/node_modules/vuepress/lib/app/.temp/siteData.js @@ -0,0 +1,634 @@ +export const siteData = { + "title": "", + "description": "", + "base": "/", + "pages": [ + { + "key": "v-ddf4be195b958", + "path": "/", + "lastUpdated": null, + "title": "Home", + "headers": [ + { + "level": 2, + "title": "Simple description", + "slug": "simple-description" + }, + { + "level": 2, + "title": "Package dependences", + "slug": "package-dependences" + }, + { + "level": 2, + "title": "Build project", + "slug": "build-project" + }, + { + "level": 2, + "title": "Run target", + "slug": "run-target" + }, + { + "level": 2, + "title": "Debug target", + "slug": "debug-target" + }, + { + "level": 2, + "title": "Configure platform", + "slug": "configure-platform" + }, + { + "level": 2, + "title": "Menu configuration", + "slug": "menu-configuration" + }, + { + "level": 2, + "title": "Package management", + "slug": "package-management" + }, + { + "level": 2, + "title": "Supported platforms", + "slug": "supported-platforms" + }, + { + "level": 2, + "title": "Supported Languages", + "slug": "supported-languages" + }, + { + "level": 2, + "title": "Supported Projects", + "slug": "supported-projects" + }, + { + "level": 2, + "title": "Builtin Plugins", + "slug": "builtin-plugins" + }, + { + "level": 2, + "title": "More Plugins", + "slug": "more-plugins" + }, + { + "level": 2, + "title": "IDE/Editor Integration", + "slug": "ide-editor-integration" + }, + { + "level": 2, + "title": "More Examples", + "slug": "more-examples" + }, + { + "level": 2, + "title": "Project Examples", + "slug": "project-examples" + }, + { + "level": 2, + "title": "Example Video", + "slug": "example-video" + }, + { + "level": 2, + "title": "Contacts", + "slug": "contacts" + } + ], + "frontmatter": { + "home": true, + "heroImage": "/hero.png", + "actionText": "Get Started →", + "actionLink": "/guide/getting-started", + "features": [ + { + "title": "Why", + "details": "Making development and building easier, so that any developer can quickly pick it up and enjoy the productivity boost when developing and building project." + }, + { + "title": "Powerful", + "details": "Provides lots of features (e.g. package, install, plugin, macro, action, option, task and etc)." + }, + { + "title": "Cross-platform", + "details": "Supports windows, macOS, linux, android, ios." + } + ], + "footer": "Apache-2.0 Licensed | Copyright © 2015-present tboox.org" + } + }, + { + "key": "v-8644851cf91dd", + "path": "/api/introduction.html", + "lastUpdated": null, + "title": "Title1", + "headers": [ + { + "level": 2, + "title": "Title2", + "slug": "title2" + }, + { + "level": 3, + "title": "Title3", + "slug": "title3" + }, + { + "level": 3, + "title": "Title3", + "slug": "title3-2" + }, + { + "level": 2, + "title": "Title2", + "slug": "title2-2" + }, + { + "level": 3, + "title": "Title3", + "slug": "title3-3" + } + ] + }, + { + "key": "v-b6ee77db64501", + "path": "/guide/faq.html", + "lastUpdated": null, + "title": "FAQ", + "headers": [ + { + "level": 2, + "title": "How to get verbose command-line arguments info?", + "slug": "how-to-get-verbose-command-line-arguments-info" + }, + { + "level": 2, + "title": "How to suppress all output info?", + "slug": "how-to-suppress-all-output-info" + }, + { + "level": 2, + "title": "How to do if xmake fails?", + "slug": "how-to-do-if-xmake-fails" + }, + { + "level": 2, + "title": "How to see verbose compiling warnings?", + "slug": "how-to-see-verbose-compiling-warnings" + }, + { + "level": 2, + "title": "How to scan source code and generate xmake.lua automaticlly", + "slug": "how-to-scan-source-code-and-generate-xmake-lua-automaticlly" + } + ] + }, + { + "key": "v-fcba2a8896d43", + "path": "/guide/getting-started.html", + "lastUpdated": null, + "title": "Getting Started", + "headers": [ + { + "level": 2, + "title": "Installation", + "slug": "installation" + }, + { + "level": 2, + "title": "Quick Start", + "slug": "quick-start" + }, + { + "level": 2, + "title": "Project Examples", + "slug": "project-examples" + }, + { + "level": 2, + "title": "Configuration", + "slug": "configuration" + }, + { + "level": 2, + "title": "Dependency Package Management", + "slug": "dependency-package-management" + } + ] + }, + { + "key": "v-cda17500cdb0c", + "path": "/guide/introduction.html", + "lastUpdated": null, + "title": "Introduction", + "headers": [ + { + "level": 2, + "title": "Introduction", + "slug": "introduction" + } + ] + }, + { + "key": "v-745d22bfe3ef4", + "path": "/guide/sponsors.html", + "lastUpdated": null, + "title": "Sponsors" + }, + { + "key": "v-360e42fea8e33", + "path": "/plugin/introduction.html", + "lastUpdated": null, + "title": "Title1", + "headers": [ + { + "level": 2, + "title": "Title2", + "slug": "title2" + }, + { + "level": 3, + "title": "Title3", + "slug": "title3" + }, + { + "level": 3, + "title": "Title3", + "slug": "title3-2" + }, + { + "level": 2, + "title": "Title2", + "slug": "title2-2" + }, + { + "level": 3, + "title": "Title3", + "slug": "title3-3" + } + ] + }, + { + "key": "v-1856b116362c7", + "path": "/zh/", + "lastUpdated": null, + "title": "Home", + "headers": [ + { + "level": 2, + "title": "简单的工程描述", + "slug": "简单的工程描述" + }, + { + "level": 2, + "title": "包依赖描述", + "slug": "包依赖描述" + }, + { + "level": 2, + "title": "构建工程", + "slug": "构建工程" + }, + { + "level": 2, + "title": "运行目标", + "slug": "运行目标" + }, + { + "level": 2, + "title": "调试程序", + "slug": "调试程序" + }, + { + "level": 2, + "title": "配置平台", + "slug": "配置平台" + }, + { + "level": 2, + "title": "图形化菜单配置", + "slug": "图形化菜单配置" + }, + { + "level": 2, + "title": "包依赖管理", + "slug": "包依赖管理" + }, + { + "level": 2, + "title": "支持平台", + "slug": "支持平台" + }, + { + "level": 2, + "title": "支持语言", + "slug": "支持语言" + }, + { + "level": 2, + "title": "工程类型", + "slug": "工程类型" + }, + { + "level": 2, + "title": "内置插件", + "slug": "内置插件" + }, + { + "level": 2, + "title": "更多插件", + "slug": "更多插件" + }, + { + "level": 2, + "title": "IDE和编辑器插件", + "slug": "ide和编辑器插件" + }, + { + "level": 2, + "title": "更多例子", + "slug": "更多例子" + }, + { + "level": 2, + "title": "项目例子", + "slug": "项目例子" + }, + { + "level": 2, + "title": "演示视频", + "slug": "演示视频" + }, + { + "level": 2, + "title": "联系方式", + "slug": "联系方式" + } + ], + "frontmatter": { + "home": true, + "heroImage": "/hero.png", + "actionText": "快速上手 →", + "actionLink": "/zh/guide/getting-started", + "features": [ + { + "title": "为什么使用", + "details": "让开发者更加关注于项目本身开发,简化项目的描述和构建,并且提供平台无关性,使得一次编写,随处构建" + }, + { + "title": "强大", + "details": "提供大量的实用特性(例如:插件扩展、脚本宏记录、批量打包、自动文档生成等常用插件)" + }, + { + "title": "跨平台", + "details": "支持windows, macOS, linux, android, ios" + } + ], + "footer": "Apache-2.0 Licensed | Copyright © 2015-present tboox.org" + } + }, + { + "key": "v-6812df0250aa3", + "path": "/zh/api/introduction.html", + "lastUpdated": null, + "title": "Title1", + "headers": [ + { + "level": 2, + "title": "Title2", + "slug": "title2" + }, + { + "level": 3, + "title": "Title3", + "slug": "title3" + }, + { + "level": 3, + "title": "Title3", + "slug": "title3-2" + }, + { + "level": 2, + "title": "Title2", + "slug": "title2-2" + }, + { + "level": 3, + "title": "Title3", + "slug": "title3-3" + } + ] + }, + { + "key": "v-d3636509f8935", + "path": "/zh/guide/faq.html", + "lastUpdated": null + }, + { + "key": "v-53a48e3593d7c", + "path": "/zh/guide/getting-started.html", + "lastUpdated": null, + "title": "快速开始", + "headers": [ + { + "level": 2, + "title": "编译", + "slug": "编译" + }, + { + "level": 2, + "title": "例子", + "slug": "例子" + } + ] + }, + { + "key": "v-cd45bc6f8fdb", + "path": "/zh/guide/introduction.html", + "lastUpdated": null, + "title": "简介", + "headers": [ + { + "level": 2, + "title": "特性", + "slug": "特性" + }, + { + "level": 2, + "title": "项目例子", + "slug": "项目例子" + }, + { + "level": 2, + "title": "联系方式", + "slug": "联系方式" + } + ] + }, + { + "key": "v-bef402580c1cc", + "path": "/zh/guide/sponsors.html", + "lastUpdated": null + }, + { + "key": "v-faf113f1b77ae", + "path": "/zh/plugin/introduction.html", + "lastUpdated": null, + "title": "Title1", + "headers": [ + { + "level": 2, + "title": "Title2", + "slug": "title2" + }, + { + "level": 3, + "title": "Title3", + "slug": "title3" + }, + { + "level": 3, + "title": "Title3", + "slug": "title3-2" + }, + { + "level": 2, + "title": "Title2", + "slug": "title2-2" + }, + { + "level": 3, + "title": "Title3", + "slug": "title3-3" + } + ] + } + ], + "themeConfig": { + "repo": "tboox/xmake", + "docsRepo": "tboox/xmake-docs", + "docsDir": "src", + "editLinks": true, + "sidebarDepth": 2, + "locales": { + "/": { + "label": "English", + "selectText": "Languages", + "editLinkText": "Edit this page on GitHub", + "lastUpdated": "Last Updated", + "nav": [ + { + "text": "Guide", + "link": "/guide/introduction" + }, + { + "text": "Plugin", + "link": "/plugin/introduction" + }, + { + "text": "API", + "link": "/api/introduction" + }, + { + "text": "Articles", + "link": "http://www.tboox.org/category/#xmake" + }, + { + "text": "Feedback", + "link": "https://github.com/tboox/xmake/issues" + }, + { + "text": "Community", + "link": "https://www.reddit.com/r/tboox/" + }, + { + "text": "Donation", + "link": "http://tboox.org/cn/donation/" + } + ], + "sidebar": { + "/guide/": [ + "introduction", + "getting-started", + "faq", + "sponsors" + ], + "/plugin/": [ + "introduction" + ], + "/api/": [ + "introduction" + ] + } + }, + "/zh/": { + "label": "简体中文", + "selectText": "选择语言", + "editLinkText": "在 GitHub 上编辑此页", + "lastUpdated": "上次更新", + "nav": [ + { + "text": "指南", + "link": "/zh/guide/introduction" + }, + { + "text": "插件", + "link": "/zh/plugin/introduction" + }, + { + "text": "接口", + "link": "/zh/api/introduction" + }, + { + "text": "文章", + "link": "http://www.tboox.org/cn/category/#xmake" + }, + { + "text": "反馈", + "link": "https://github.com/tboox/xmake/issues" + }, + { + "text": "社区", + "link": "https://www.reddit.com/r/tboox/" + }, + { + "text": "捐助", + "link": "http://tboox.org/cn/donation/" + } + ], + "sidebar": { + "/zh/guide/": [ + "introduction", + "getting-started", + "faq", + "sponsors" + ], + "/zh/plugin/": [ + "introduction" + ], + "/zh/api/": [ + "introduction" + ] + } + } + } + }, + "locales": { + "/": { + "lang": "en-US", + "title": "xmake", + "description": "A cross-platform build utility based on Lua" + }, + "/zh/": { + "lang": "zh-CN", + "title": "xmake", + "description": "一个基于Lua的轻量级跨平台自动构建工具" + } + } +} \ No newline at end of file diff --git a/node_modules/vuepress/lib/app/.temp/style.styl b/node_modules/vuepress/lib/app/.temp/style.styl new file mode 100644 index 00000000..b3deab99 --- /dev/null +++ b/node_modules/vuepress/lib/app/.temp/style.styl @@ -0,0 +1 @@ +@import("/Users/ruki/projects/personal/xmake-docs/src/.vuepress/style.styl") \ No newline at end of file diff --git a/node_modules/vuepress/lib/app/.temp/themeEnhanceApp.js b/node_modules/vuepress/lib/app/.temp/themeEnhanceApp.js new file mode 100644 index 00000000..03c095d5 --- /dev/null +++ b/node_modules/vuepress/lib/app/.temp/themeEnhanceApp.js @@ -0,0 +1 @@ +export default function () {} \ No newline at end of file diff --git a/node_modules/vuepress/lib/app/SWUpdateEvent.js b/node_modules/vuepress/lib/app/SWUpdateEvent.js new file mode 100644 index 00000000..fe6ab31c --- /dev/null +++ b/node_modules/vuepress/lib/app/SWUpdateEvent.js @@ -0,0 +1,43 @@ +export default class SWUpdateEvent { + constructor (registration) { + Object.defineProperty(this, 'registration', { + value: registration, + configurable: true, + writable: true + }) + } + + /** + * Check if the new service worker exists or not. + */ + update () { + return this.registration.update() + } + + /** + * Activate new service worker to work 'location.reload()' with new data. + */ + skipWaiting () { + const worker = this.registration.waiting + if (!worker) { + return Promise.resolve() + } + + console.log('[vuepress:sw] Doing worker.skipWaiting().') + return new Promise((resolve, reject) => { + const channel = new MessageChannel() + + channel.port1.onmessage = (event) => { + console.log('[vuepress:sw] Done worker.skipWaiting().') + if (event.data.error) { + reject(event.data.error) + } else { + resolve(event.data) + } + } + + worker.postMessage({ type: 'skip-waiting' }, [channel.port2]) + }) + } +} + diff --git a/node_modules/vuepress/lib/app/app.js b/node_modules/vuepress/lib/app/app.js new file mode 100644 index 00000000..511abbd4 --- /dev/null +++ b/node_modules/vuepress/lib/app/app.js @@ -0,0 +1,102 @@ +import Vue from 'vue' +import Router from 'vue-router' +import dataMixin from './dataMixin' +import store from './store' +import { routes } from '@temp/routes' +import { siteData } from '@temp/siteData' +import enhanceApp from '@temp/enhanceApp' +import themeEnhanceApp from '@temp/themeEnhanceApp' + +// generated from user config +import('@temp/style.styl') + +// built-in components +import Content from './components/Content' +import OutboundLink from './components/OutboundLink.vue' +import ClientOnly from './components/ClientOnly' + +// suggest dev server restart on base change +if (module.hot) { + const prevBase = siteData.base + module.hot.accept('./.temp/siteData', () => { + if (siteData.base !== prevBase) { + window.alert( + `[vuepress] Site base has changed. ` + + `Please restart dev server to ensure correct asset paths.` + ) + } + }) +} + +Vue.config.productionTip = false +Vue.use(Router) +// mixin for exposing $site and $page +Vue.mixin(dataMixin(siteData)) +// component for rendering markdown content and setting title etc. +Vue.component('Content', Content) +Vue.component('OutboundLink', OutboundLink) +Vue.component('Badge', () => import('./components/Badge.vue')) +// component for client-only content +Vue.component('ClientOnly', ClientOnly) + +// global helper for adding base path to absolute urls +Vue.prototype.$withBase = function (path) { + const base = this.$site.base + if (path.charAt(0) === '/') { + return base + path.slice(1) + } else { + return path + } +} + +export function createApp () { + const router = new Router({ + base: siteData.base, + mode: 'history', + fallback: false, + routes, + scrollBehavior: (to, from, saved) => { + if (saved) { + return saved + } else if (to.hash) { + if (store.disableScrollBehavior) { + return false + } + return { + selector: to.hash + } + } else { + return { x: 0, y: 0 } + } + } + }) + + // redirect /foo to /foo/ + router.beforeEach((to, from, next) => { + if (!/(\/|\.html)$/.test(to.path)) { + next(Object.assign({}, to, { + path: to.path + '/' + })) + } else { + next() + } + }) + + const options = {} + + themeEnhanceApp({ Vue, options, router, siteData }) + enhanceApp({ Vue, options, router, siteData }) + + const app = new Vue( + Object.assign(options, { + router, + render (h) { + return h('div', { attrs: { id: 'app' }}, [ + h('router-view', { ref: 'layout' }) + ]) + } + }) + ) + + return { app, router } +} diff --git a/node_modules/vuepress/lib/app/clientEntry.js b/node_modules/vuepress/lib/app/clientEntry.js new file mode 100644 index 00000000..dc86314a --- /dev/null +++ b/node_modules/vuepress/lib/app/clientEntry.js @@ -0,0 +1,74 @@ +/* global BASE_URL, GA_ID, ga, SW_ENABLED, VUEPRESS_VERSION, LAST_COMMIT_HASH*/ + +import { createApp } from './app' +import SWUpdateEvent from './SWUpdateEvent' +import { register } from 'register-service-worker' + +const { app, router } = createApp() + +window.__VUEPRESS_VERSION__ = { + version: VUEPRESS_VERSION, + hash: LAST_COMMIT_HASH +} + +// Google analytics integration +if (process.env.NODE_ENV === 'production' && GA_ID) { + (function (i, s, o, g, r, a, m) { + i['GoogleAnalyticsObject'] = r + i[r] = i[r] || function () { + (i[r].q = i[r].q || []).push(arguments) + } + i[r].l = 1 * new Date() + a = s.createElement(o) + m = s.getElementsByTagName(o)[0] + a.async = 1 + a.src = g + m.parentNode.insertBefore(a, m) + })(window, document, 'script', 'https://www.google-analytics.com/analytics.js', 'ga') + + ga('create', GA_ID, 'auto') + ga('send', 'pageview') + + router.afterEach(function (to) { + ga('set', 'page', app.$withBase(to.fullPath)) + ga('send', 'pageview') + }) +} + +router.onReady(() => { + app.$mount('#app') + + // Register service worker + if (process.env.NODE_ENV === 'production' && + SW_ENABLED && + window.location.protocol === 'https:') { + register(`${BASE_URL}service-worker.js`, { + ready () { + console.log('[vuepress:sw] Service worker is active.') + app.$refs.layout.$emit('sw-ready') + }, + cached (registration) { + console.log('[vuepress:sw] Content has been cached for offline use.') + app.$refs.layout.$emit('sw-cached', new SWUpdateEvent(registration)) + }, + updated (registration) { + console.log('[vuepress:sw] Content updated.') + app.$refs.layout.$emit('sw-updated', new SWUpdateEvent(registration)) + }, + offline () { + console.log('[vuepress:sw] No internet connection found. App is running in offline mode.') + app.$refs.layout.$emit('sw-offline') + }, + error (err) { + console.error('[vuepress:sw] Error during service worker registration:', err) + app.$refs.layout.$emit('sw-error', err) + if (GA_ID) { + ga('send', 'exception', { + exDescription: err.message, + exFatal: false + }) + } + } + }) + } +}) diff --git a/node_modules/vuepress/lib/app/components/Badge.vue b/node_modules/vuepress/lib/app/components/Badge.vue new file mode 100644 index 00000000..17e6ba6e --- /dev/null +++ b/node_modules/vuepress/lib/app/components/Badge.vue @@ -0,0 +1,46 @@ + + + diff --git a/node_modules/vuepress/lib/app/components/ClientOnly.js b/node_modules/vuepress/lib/app/components/ClientOnly.js new file mode 100644 index 00000000..c786d1af --- /dev/null +++ b/node_modules/vuepress/lib/app/components/ClientOnly.js @@ -0,0 +1,12 @@ +export default { + functional: true, + render (h, { parent, children }) { + if (parent._isMounted) { + return children + } else { + parent.$once('hook:mounted', () => { + parent.$forceUpdate() + }) + } + } +} diff --git a/node_modules/vuepress/lib/app/components/Content.js b/node_modules/vuepress/lib/app/components/Content.js new file mode 100644 index 00000000..46628195 --- /dev/null +++ b/node_modules/vuepress/lib/app/components/Content.js @@ -0,0 +1,17 @@ +export default { + functional: true, + + props: { + custom: { + type: Boolean, + default: true + } + }, + + render (h, { parent, props, data }) { + return h(parent.$page.key, { + class: [props.custom ? 'custom' : '', data.class, data.staticClass], + style: data.style + }) + } +} diff --git a/node_modules/vuepress/lib/app/components/OutboundLink.vue b/node_modules/vuepress/lib/app/components/OutboundLink.vue new file mode 100644 index 00000000..a3aa7f2d --- /dev/null +++ b/node_modules/vuepress/lib/app/components/OutboundLink.vue @@ -0,0 +1,12 @@ + + + diff --git a/node_modules/vuepress/lib/app/dataMixin.js b/node_modules/vuepress/lib/app/dataMixin.js new file mode 100644 index 00000000..fd003d92 --- /dev/null +++ b/node_modules/vuepress/lib/app/dataMixin.js @@ -0,0 +1,90 @@ +import Vue from 'vue' +import { findPageForPath } from './util' + +export default function dataMixin (siteData) { + prepare(siteData) + const store = new Vue({ + data: { siteData } + }) + + if (module.hot) { + module.hot.accept('./.temp/siteData', () => { + prepare(siteData) + store.siteData = siteData + }) + } + + return { + computed: { + $site () { + return store.siteData + }, + $localeConfig () { + const { locales = {}} = this.$site + let targetLang + let defaultLang + for (const path in locales) { + if (path === '/') { + defaultLang = locales[path] + } else if (this.$page.path.indexOf(path) === 0) { + targetLang = locales[path] + } + } + return targetLang || defaultLang || {} + }, + $siteTitle () { + return this.$localeConfig.title || this.$site.title || '' + }, + $title () { + const page = this.$page + const siteTitle = this.$siteTitle + const selfTitle = page.frontmatter.home ? null : ( + page.frontmatter.title || // explicit title + page.title // inferred title + ) + return siteTitle + ? selfTitle + ? (selfTitle + ' | ' + siteTitle) + : siteTitle + : selfTitle || 'VuePress' + }, + $description () { + // #565 hoist description from meta + if (this.$page.frontmatter.meta) { + const descriptionMeta = this.$page.frontmatter.meta.filter(item => item.name === 'description')[0] + if (descriptionMeta) return descriptionMeta.content + } + return this.$page.frontmatter.description || this.$localeConfig.description || this.$site.description || '' + }, + $lang () { + return this.$page.frontmatter.lang || this.$localeConfig.lang || 'en-US' + }, + $localePath () { + return this.$localeConfig.path || '/' + }, + $themeLocaleConfig () { + return (this.$site.themeConfig.locales || {})[this.$localePath] || {} + }, + $page () { + return findPageForPath( + this.$site.pages, + this.$route.path + ) + } + } + } +} + +function prepare (siteData) { + siteData.pages.forEach(page => { + if (!page.frontmatter) { + page.frontmatter = {} + } + }) + if (siteData.locales) { + Object.keys(siteData.locales).forEach(path => { + siteData.locales[path].path = path + }) + } + Object.freeze(siteData) +} diff --git a/node_modules/vuepress/lib/app/index.dev.html b/node_modules/vuepress/lib/app/index.dev.html new file mode 100644 index 00000000..06f743c5 --- /dev/null +++ b/node_modules/vuepress/lib/app/index.dev.html @@ -0,0 +1,11 @@ + + + + + + + + +
+ + diff --git a/node_modules/vuepress/lib/app/index.ssr.html b/node_modules/vuepress/lib/app/index.ssr.html new file mode 100644 index 00000000..32d310a9 --- /dev/null +++ b/node_modules/vuepress/lib/app/index.ssr.html @@ -0,0 +1,17 @@ + + + + + + {{ title }} + + {{{ userHeadTags }}} + {{{ pageMeta }}} + {{{ renderResourceHints() }}} + {{{ renderStyles() }}} + + + + {{{ renderScripts() }}} + + diff --git a/node_modules/vuepress/lib/app/root-mixins/activeHeaderLinks.js b/node_modules/vuepress/lib/app/root-mixins/activeHeaderLinks.js new file mode 100644 index 00000000..6cad69d5 --- /dev/null +++ b/node_modules/vuepress/lib/app/root-mixins/activeHeaderLinks.js @@ -0,0 +1,47 @@ +import store from '@app/store' +import throttle from 'lodash.throttle' + +export default { + mounted () { + window.addEventListener('scroll', this.onScroll) + }, + methods: { + onScroll: throttle(function () { + this.setActiveHash() + }, 300), + setActiveHash () { + const sidebarLinks = [].slice.call(document.querySelectorAll('.sidebar-link')) + const anchors = [].slice.call(document.querySelectorAll('.header-anchor')) + .filter(anchor => sidebarLinks.some(sidebarLink => sidebarLink.hash === anchor.hash)) + + const scrollTop = Math.max( + window.pageYOffset, + document.documentElement.scrollTop, + document.body.scrollTop + ) + + for (let i = 0; i < anchors.length; i++) { + const anchor = anchors[i] + const nextAnchor = anchors[i + 1] + + const isActive = i === 0 && scrollTop === 0 || + (scrollTop >= anchor.parentElement.offsetTop + 10 && + (!nextAnchor || scrollTop < nextAnchor.parentElement.offsetTop - 10)) + + if (isActive && decodeURIComponent(this.$route.hash) !== decodeURIComponent(anchor.hash)) { + store.disableScrollBehavior = true + this.$router.replace(decodeURIComponent(anchor.hash), () => { + // execute after scrollBehavior handler. + this.$nextTick(() => { + store.disableScrollBehavior = false + }) + }) + return + } + } + } + }, + beforeDestroy () { + window.removeEventListener('scroll', this.onScroll) + } +} diff --git a/node_modules/vuepress/lib/app/root-mixins/index.js b/node_modules/vuepress/lib/app/root-mixins/index.js new file mode 100644 index 00000000..fd966f39 --- /dev/null +++ b/node_modules/vuepress/lib/app/root-mixins/index.js @@ -0,0 +1,7 @@ +import updateMeta from './updateMeta' +import activeHeaderLinks from '@activeHeaderLinks' + +export default [ + updateMeta, // required + activeHeaderLinks // optional +] diff --git a/node_modules/vuepress/lib/app/root-mixins/updateMeta.js b/node_modules/vuepress/lib/app/root-mixins/updateMeta.js new file mode 100644 index 00000000..ff2703ac --- /dev/null +++ b/node_modules/vuepress/lib/app/root-mixins/updateMeta.js @@ -0,0 +1,59 @@ +export default { + created () { + if (this.$ssrContext) { + this.$ssrContext.title = this.$title + this.$ssrContext.lang = this.$lang + this.$ssrContext.description = this.$page.description || this.$description + } + }, + + mounted () { + // update title / meta tags + this.currentMetaTags = new Set() + + const updateMeta = () => { + document.title = this.$title + document.documentElement.lang = this.$lang + const userMeta = this.$page.frontmatter.meta || [] + const meta = userMeta.slice(0) + const useGlobalDescription = userMeta.filter(m => m.name === 'description').length === 0 + + // #665 Avoid duplicate description meta at runtime. + if (useGlobalDescription) { + meta.push({ name: 'description', content: this.$description }) + } + + // Including description meta coming from SSR. + const descriptionMetas = document.querySelectorAll('meta[name="description"]') + if (descriptionMetas.length) { + descriptionMetas.forEach(m => this.currentMetaTags.add(m)) + } + + this.currentMetaTags = new Set(updateMetaTags(meta, this.currentMetaTags)) + } + this.$watch('$page', updateMeta) + updateMeta() + }, + + beforeDestroy () { + updateMetaTags(null, this.currentMetaTags) + } +} + +function updateMetaTags (meta, current) { + if (current) { + [...current].forEach(c => { + document.head.removeChild(c) + }) + } + if (meta) { + return meta.map(m => { + const tag = document.createElement('meta') + Object.keys(m).forEach(key => { + tag.setAttribute(key, m[key]) + }) + document.head.appendChild(tag) + return tag + }) + } +} diff --git a/node_modules/vuepress/lib/app/serverEntry.js b/node_modules/vuepress/lib/app/serverEntry.js new file mode 100644 index 00000000..715fc956 --- /dev/null +++ b/node_modules/vuepress/lib/app/serverEntry.js @@ -0,0 +1,14 @@ +import { createApp } from './app' + +export default context => new Promise((resolve, reject) => { + const { app, router } = createApp() + const { url } = context + const { fullPath } = router.resolve(url).route + + if (fullPath !== url) { + return reject({ url: fullPath }) + } + + router.push(url) + router.onReady(() => resolve(app)) +}) diff --git a/node_modules/vuepress/lib/app/store.js b/node_modules/vuepress/lib/app/store.js new file mode 100644 index 00000000..115fe0b9 --- /dev/null +++ b/node_modules/vuepress/lib/app/store.js @@ -0,0 +1,7 @@ +// It is not yet time to use Vuex to manage the global state +// singleton object as a global store. +const state = { + disableScrollBehavior: false +} + +export default state diff --git a/node_modules/vuepress/lib/app/util.js b/node_modules/vuepress/lib/app/util.js new file mode 100644 index 00000000..c1117224 --- /dev/null +++ b/node_modules/vuepress/lib/app/util.js @@ -0,0 +1,19 @@ +export function injectMixins (options, mixins) { + if (!options.mixins) { + options.mixins = [] + } + options.mixins.push(...mixins) +} + +export function findPageForPath (pages, path) { + for (let i = 0; i < pages.length; i++) { + const page = pages[i] + if (page.path === path) { + return page + } + } + return { + path: '', + frontmatter: {} + } +} diff --git a/node_modules/vuepress/lib/build.js b/node_modules/vuepress/lib/build.js new file mode 100644 index 00000000..f7efb49b --- /dev/null +++ b/node_modules/vuepress/lib/build.js @@ -0,0 +1,200 @@ +module.exports = async function build (sourceDir, cliOptions = {}) { + process.env.NODE_ENV = 'production' + + const fs = require('fs-extra') + const path = require('path') + const chalk = require('chalk') + const webpack = require('webpack') + const readline = require('readline') + const escape = require('escape-html') + + const logger = require('./util/logger') + const prepare = require('./prepare') + const createClientConfig = require('./webpack/createClientConfig') + const createServerConfig = require('./webpack/createServerConfig') + const { createBundleRenderer } = require('vue-server-renderer') + const { normalizeHeadTag, applyUserWebpackConfig } = require('./util') + + logger.wait('\nExtracting site metadata...') + const options = await prepare(sourceDir) + if (cliOptions.outDir) { + options.outDir = cliOptions.outDir + } + + const { outDir } = options + if (path.resolve() === outDir) { + return console.error(logger.error(chalk.red('Unexpected option: outDir cannot be set to the current working directory.\n'), false)) + } + await fs.remove(outDir) + + let clientConfig = createClientConfig(options, cliOptions).toConfig() + let serverConfig = createServerConfig(options, cliOptions).toConfig() + + // apply user config... + const userConfig = options.siteConfig.configureWebpack + if (userConfig) { + clientConfig = applyUserWebpackConfig(userConfig, clientConfig, false) + serverConfig = applyUserWebpackConfig(userConfig, serverConfig, true) + } + + // compile! + const stats = await compile([clientConfig, serverConfig]) + + const serverBundle = require(path.resolve(outDir, 'manifest/server.json')) + const clientManifest = require(path.resolve(outDir, 'manifest/client.json')) + + // remove manifests after loading them. + await fs.remove(path.resolve(outDir, 'manifest')) + + // find and remove empty style chunk caused by + // https://github.com/webpack-contrib/mini-css-extract-plugin/issues/85 + // TODO remove when it's fixed + await workaroundEmptyStyleChunk() + + // create server renderer using built manifests + const renderer = createBundleRenderer(serverBundle, { + clientManifest, + runInNewContext: false, + inject: false, + shouldPrefetch: options.siteConfig.shouldPrefetch || (() => true), + template: await fs.readFile(path.resolve(__dirname, 'app/index.ssr.html'), 'utf-8') + }) + + // pre-render head tags from user config + const userHeadTags = (options.siteConfig.head || []) + .map(renderHeadTag) + .join('\n ') + + // render pages + logger.wait('Rendering static HTML...') + for (const page of options.siteData.pages) { + await renderPage(page) + } + + // if the user does not have a custom 404.md, generate the theme's default + if (!options.siteData.pages.some(p => p.path === '/404.html')) { + await renderPage({ path: '/404.html' }) + } + + readline.clearLine(process.stdout, 0) + readline.cursorTo(process.stdout, 0) + + if (options.siteConfig.serviceWorker) { + logger.wait('\nGenerating service worker...') + const wbb = require('workbox-build') + await wbb.generateSW({ + swDest: path.resolve(outDir, 'service-worker.js'), + globDirectory: outDir, + globPatterns: ['**\/*.{js,css,html,png,jpg,jpeg,gif,svg,woff,woff2,eot,ttf,otf}'] + }) + await fs.writeFile( + path.resolve(outDir, 'service-worker.js'), + await fs.readFile(path.resolve(__dirname, 'service-worker/skip-waiting.js'), 'utf8'), + { flag: 'a' } + ) + } + + // DONE. + const relativeDir = path.relative(process.cwd(), outDir) + logger.success(`\n${chalk.green('Success!')} Generated static files in ${chalk.cyan(relativeDir)}.\n`) + + // --- helpers --- + + function compile (config) { + return new Promise((resolve, reject) => { + webpack(config, (err, stats) => { + if (err) { + return reject(err) + } + if (stats.hasErrors()) { + stats.toJson().errors.forEach(err => { + console.error(err) + }) + reject(new Error(`Failed to compile with errors.`)) + return + } + if (cliOptions.debug && stats.hasWarnings()) { + stats.toJson().warnings.forEach(warning => { + console.warn(warning) + }) + } + resolve(stats.toJson({ modules: false })) + }) + }) + } + + function renderHeadTag (tag) { + const { tagName, attributes, innerHTML, closeTag } = normalizeHeadTag(tag) + return `<${tagName}${renderAttrs(attributes)}>${innerHTML}${closeTag ? `` : ``}` + } + + function renderAttrs (attrs = {}) { + const keys = Object.keys(attrs) + if (keys.length) { + return ' ' + keys.map(name => `${name}="${escape(attrs[name])}"`).join(' ') + } else { + return '' + } + } + + async function renderPage (page) { + const pagePath = page.path + readline.clearLine(process.stdout, 0) + readline.cursorTo(process.stdout, 0) + process.stdout.write(`Rendering page: ${pagePath}`) + + // #565 Avoid duplicate description meta at SSR. + const meta = (page.frontmatter && page.frontmatter.meta || []).filter(item => item.name !== 'description') + const pageMeta = renderPageMeta(meta) + + const context = { + url: pagePath, + userHeadTags, + pageMeta, + title: 'VuePress', + lang: 'en', + description: '' + } + + let html + try { + html = await renderer.renderToString(context) + } catch (e) { + console.error(logger.error(chalk.red(`Error rendering ${pagePath}:`), false)) + throw e + } + const filename = decodeURIComponent(pagePath.replace(/\/$/, '/index.html').replace(/^\//, '')) + const filePath = path.resolve(outDir, filename) + await fs.ensureDir(path.dirname(filePath)) + await fs.writeFile(filePath, html) + } + + function renderPageMeta (meta) { + if (!meta) return '' + return meta.map(m => { + let res = ` { + res += ` ${key}="${escape(m[key])}"` + }) + return res + `>` + }).join('') + } + + async function workaroundEmptyStyleChunk () { + const styleChunk = stats.children[0].assets.find(a => { + return /styles\.\w{8}\.js$/.test(a.name) + }) + if (!styleChunk) return + const styleChunkPath = path.resolve(outDir, styleChunk.name) + const styleChunkContent = await fs.readFile(styleChunkPath, 'utf-8') + await fs.remove(styleChunkPath) + // prepend it to app.js. + // this is necessary for the webpack runtime to work properly. + const appChunk = stats.children[0].assets.find(a => { + return /app\.\w{8}\.js$/.test(a.name) + }) + const appChunkPath = path.resolve(outDir, appChunk.name) + const appChunkContent = await fs.readFile(appChunkPath, 'utf-8') + await fs.writeFile(appChunkPath, styleChunkContent + appChunkContent) + } +} diff --git a/node_modules/vuepress/lib/default-theme/AlgoliaSearchBox.vue b/node_modules/vuepress/lib/default-theme/AlgoliaSearchBox.vue new file mode 100644 index 00000000..0334ae0b --- /dev/null +++ b/node_modules/vuepress/lib/default-theme/AlgoliaSearchBox.vue @@ -0,0 +1,156 @@ + + + + + diff --git a/node_modules/vuepress/lib/default-theme/DropdownLink.vue b/node_modules/vuepress/lib/default-theme/DropdownLink.vue new file mode 100644 index 00000000..e6000a60 --- /dev/null +++ b/node_modules/vuepress/lib/default-theme/DropdownLink.vue @@ -0,0 +1,181 @@ + + + + + diff --git a/node_modules/vuepress/lib/default-theme/DropdownTransition.vue b/node_modules/vuepress/lib/default-theme/DropdownTransition.vue new file mode 100644 index 00000000..8c711a15 --- /dev/null +++ b/node_modules/vuepress/lib/default-theme/DropdownTransition.vue @@ -0,0 +1,33 @@ + + + + + diff --git a/node_modules/vuepress/lib/default-theme/Home.vue b/node_modules/vuepress/lib/default-theme/Home.vue new file mode 100644 index 00000000..a172eb9b --- /dev/null +++ b/node_modules/vuepress/lib/default-theme/Home.vue @@ -0,0 +1,162 @@ + + + + + diff --git a/node_modules/vuepress/lib/default-theme/Layout.vue b/node_modules/vuepress/lib/default-theme/Layout.vue new file mode 100644 index 00000000..ee2e6abe --- /dev/null +++ b/node_modules/vuepress/lib/default-theme/Layout.vue @@ -0,0 +1,183 @@ + + + + + + diff --git a/node_modules/vuepress/lib/default-theme/NavLink.vue b/node_modules/vuepress/lib/default-theme/NavLink.vue new file mode 100644 index 00000000..d9fa4886 --- /dev/null +++ b/node_modules/vuepress/lib/default-theme/NavLink.vue @@ -0,0 +1,49 @@ + + + diff --git a/node_modules/vuepress/lib/default-theme/NavLinks.vue b/node_modules/vuepress/lib/default-theme/NavLinks.vue new file mode 100644 index 00000000..4037f288 --- /dev/null +++ b/node_modules/vuepress/lib/default-theme/NavLinks.vue @@ -0,0 +1,151 @@ + + + + + diff --git a/node_modules/vuepress/lib/default-theme/Navbar.vue b/node_modules/vuepress/lib/default-theme/Navbar.vue new file mode 100644 index 00000000..12795665 --- /dev/null +++ b/node_modules/vuepress/lib/default-theme/Navbar.vue @@ -0,0 +1,133 @@ + + + + + diff --git a/node_modules/vuepress/lib/default-theme/NotFound.vue b/node_modules/vuepress/lib/default-theme/NotFound.vue new file mode 100644 index 00000000..6aefe797 --- /dev/null +++ b/node_modules/vuepress/lib/default-theme/NotFound.vue @@ -0,0 +1,26 @@ + + + diff --git a/node_modules/vuepress/lib/default-theme/Page.vue b/node_modules/vuepress/lib/default-theme/Page.vue new file mode 100644 index 00000000..37d65b41 --- /dev/null +++ b/node_modules/vuepress/lib/default-theme/Page.vue @@ -0,0 +1,246 @@ + + + + + diff --git a/node_modules/vuepress/lib/default-theme/SWUpdatePopup.vue b/node_modules/vuepress/lib/default-theme/SWUpdatePopup.vue new file mode 100644 index 00000000..b224db31 --- /dev/null +++ b/node_modules/vuepress/lib/default-theme/SWUpdatePopup.vue @@ -0,0 +1,85 @@ + + + + + diff --git a/node_modules/vuepress/lib/default-theme/SearchBox.vue b/node_modules/vuepress/lib/default-theme/SearchBox.vue new file mode 100644 index 00000000..98608552 --- /dev/null +++ b/node_modules/vuepress/lib/default-theme/SearchBox.vue @@ -0,0 +1,238 @@ + + + + + diff --git a/node_modules/vuepress/lib/default-theme/Sidebar.vue b/node_modules/vuepress/lib/default-theme/Sidebar.vue new file mode 100644 index 00000000..7bcf0059 --- /dev/null +++ b/node_modules/vuepress/lib/default-theme/Sidebar.vue @@ -0,0 +1,113 @@ + + + + + diff --git a/node_modules/vuepress/lib/default-theme/SidebarButton.vue b/node_modules/vuepress/lib/default-theme/SidebarButton.vue new file mode 100644 index 00000000..0a222434 --- /dev/null +++ b/node_modules/vuepress/lib/default-theme/SidebarButton.vue @@ -0,0 +1,28 @@ + + + diff --git a/node_modules/vuepress/lib/default-theme/SidebarGroup.vue b/node_modules/vuepress/lib/default-theme/SidebarGroup.vue new file mode 100644 index 00000000..119dfa14 --- /dev/null +++ b/node_modules/vuepress/lib/default-theme/SidebarGroup.vue @@ -0,0 +1,77 @@ + + + + + diff --git a/node_modules/vuepress/lib/default-theme/SidebarLink.vue b/node_modules/vuepress/lib/default-theme/SidebarLink.vue new file mode 100644 index 00000000..8288bf96 --- /dev/null +++ b/node_modules/vuepress/lib/default-theme/SidebarLink.vue @@ -0,0 +1,91 @@ + + + diff --git a/node_modules/vuepress/lib/default-theme/search.svg b/node_modules/vuepress/lib/default-theme/search.svg new file mode 100644 index 00000000..03d83913 --- /dev/null +++ b/node_modules/vuepress/lib/default-theme/search.svg @@ -0,0 +1 @@ + diff --git a/node_modules/vuepress/lib/default-theme/styles/arrow.styl b/node_modules/vuepress/lib/default-theme/styles/arrow.styl new file mode 100644 index 00000000..20bffc0d --- /dev/null +++ b/node_modules/vuepress/lib/default-theme/styles/arrow.styl @@ -0,0 +1,22 @@ +@require './config' + +.arrow + display inline-block + width 0 + height 0 + &.up + border-left 4px solid transparent + border-right 4px solid transparent + border-bottom 6px solid $arrowBgColor + &.down + border-left 4px solid transparent + border-right 4px solid transparent + border-top 6px solid $arrowBgColor + &.right + border-top 4px solid transparent + border-bottom 4px solid transparent + border-left 6px solid $arrowBgColor + &.left + border-top 4px solid transparent + border-bottom 4px solid transparent + border-right 6px solid $arrowBgColor diff --git a/node_modules/vuepress/lib/default-theme/styles/code.styl b/node_modules/vuepress/lib/default-theme/styles/code.styl new file mode 100644 index 00000000..8383c6e3 --- /dev/null +++ b/node_modules/vuepress/lib/default-theme/styles/code.styl @@ -0,0 +1,129 @@ +@require './config' + +.content + code + color lighten($textColor, 20%) + padding 0.25rem 0.5rem + margin 0 + font-size 0.85em + background-color rgba(27,31,35,0.05) + border-radius 3px + +.content + pre, pre[class*="language-"] + line-height 1.4 + padding 1.25rem 1.5rem + margin 0.85rem 0 + background-color $codeBgColor + border-radius 6px + overflow auto + code + color #fff + padding 0 + background-color transparent + border-radius 0 + +div[class*="language-"] + position relative + background-color $codeBgColor + border-radius 6px + .highlight-lines + user-select none + padding-top 1.3rem + position absolute + top 0 + left 0 + width 100% + line-height 1.4 + .highlighted + background-color rgba(0, 0, 0, 66%) + pre, pre[class*="language-"] + background transparent + position relative + z-index 1 + &::before + position absolute + z-index 3 + top 0.8em + right 1em + font-size 0.75rem + color rgba(255, 255, 255, 0.4) + &:not(.line-numbers-mode) + .line-numbers-wrapper + display none + &.line-numbers-mode + .highlight-lines .highlighted + position relative + &:before + content ' ' + position absolute + z-index 3 + left 0 + top 0 + display block + width $lineNumbersWrapperWidth + height 100% + background-color rgba(0, 0, 0, 66%) + pre + padding-left $lineNumbersWrapperWidth + 1 rem + vertical-align middle + .line-numbers-wrapper + position absolute + top 0 + width $lineNumbersWrapperWidth + text-align center + color rgba(255, 255, 255, 0.3) + padding 1.25rem 0 + line-height 1.4 + br + user-select none + .line-number + position relative + z-index 4 + user-select none + font-size 0.85em + &::after + content '' + position absolute + z-index 2 + top 0 + left 0 + width $lineNumbersWrapperWidth + height 100% + border-radius 6px 0 0 6px + border-right 1px solid rgba(0, 0, 0, 66%) + background-color $codeBgColor + + +for lang in $codeLang + div{'[class~="language-' + lang + '"]'} + &:before + content ('' + lang) + +div[class~="language-javascript"] + &:before + content "js" + +div[class~="language-typescript"] + &:before + content "ts" + +div[class~="language-markup"] + &:before + content "html" + +div[class~="language-markdown"] + &:before + content "md" + +div[class~="language-json"]:before + content "json" + +div[class~="language-ruby"]:before + content "rb" + +div[class~="language-python"]:before + content "py" + +div[class~="language-bash"]:before + content "sh" diff --git a/node_modules/vuepress/lib/default-theme/styles/config.styl b/node_modules/vuepress/lib/default-theme/styles/config.styl new file mode 100644 index 00000000..a57d4484 --- /dev/null +++ b/node_modules/vuepress/lib/default-theme/styles/config.styl @@ -0,0 +1,22 @@ +// colors +$accentColor = #3eaf7c +$textColor = #2c3e50 +$borderColor = #eaecef +$codeBgColor = #282c34 +$arrowBgColor = #ccc + +// layout +$navbarHeight = 3.6rem +$sidebarWidth = 20rem +$contentWidth = 740px + +// responsive breakpoints +$MQNarrow = 959px +$MQMobile = 719px +$MQMobileNarrow = 419px + +// code +$lineNumbersWrapperWidth = 3.5rem +$codeLang = js ts html md vue css sass scss less stylus go java c sh yaml py + +@import '~@temp/override.styl' diff --git a/node_modules/vuepress/lib/default-theme/styles/custom-blocks.styl b/node_modules/vuepress/lib/default-theme/styles/custom-blocks.styl new file mode 100644 index 00000000..3ccc2df2 --- /dev/null +++ b/node_modules/vuepress/lib/default-theme/styles/custom-blocks.styl @@ -0,0 +1,28 @@ +.custom-block + .custom-block-title + font-weight 600 + margin-bottom -0.4rem + &.tip, &.warning, &.danger + padding .1rem 1.5rem + border-left-width .5rem + border-left-style solid + margin 1rem 0 + &.tip + background-color #f3f5f7 + border-color #42b983 + &.warning + background-color rgba(255,229,100,.3) + border-color darken(#ffe564, 35%) + color darken(#ffe564, 70%) + .custom-block-title + color darken(#ffe564, 50%) + a + color $textColor + &.danger + background-color #ffe6e6 + border-color darken(red, 20%) + color darken(red, 70%) + .custom-block-title + color darken(red, 40%) + a + color $textColor diff --git a/node_modules/vuepress/lib/default-theme/styles/mobile.styl b/node_modules/vuepress/lib/default-theme/styles/mobile.styl new file mode 100644 index 00000000..b35e59c5 --- /dev/null +++ b/node_modules/vuepress/lib/default-theme/styles/mobile.styl @@ -0,0 +1,37 @@ +@require './config' + +$mobileSidebarWidth = $sidebarWidth * 0.82 + +// narrow desktop / iPad +@media (max-width: $MQNarrow) + .sidebar + font-size 15px + width $mobileSidebarWidth + .page + padding-left $mobileSidebarWidth + +// wide mobile +@media (max-width: $MQMobile) + .sidebar + top 0 + padding-top $navbarHeight + transform translateX(-100%) + transition transform .2s ease + .page + padding-left 0 + .theme-container + &.sidebar-open + .sidebar + transform translateX(0) + &.no-navbar + .sidebar + padding-top: 0 + +// narrow mobile +@media (max-width: $MQMobileNarrow) + h1 + font-size 1.9rem + .content + div[class*="language-"] + margin 0.85rem -1.5rem + border-radius 0 diff --git a/node_modules/vuepress/lib/default-theme/styles/nprogress.styl b/node_modules/vuepress/lib/default-theme/styles/nprogress.styl new file mode 100644 index 00000000..f186a67e --- /dev/null +++ b/node_modules/vuepress/lib/default-theme/styles/nprogress.styl @@ -0,0 +1,48 @@ +#nprogress + pointer-events none + .bar + background $accentColor + position fixed + z-index 1031 + top 0 + left 0 + width 100% + height 2px + .peg + display block + position absolute + right 0px + width 100px + height 100% + box-shadow 0 0 10px $accentColor, 0 0 5px $accentColor + opacity 1.0 + transform rotate(3deg) translate(0px, -4px) + .spinner + display block + position fixed + z-index 1031 + top 15px + right 15px + .spinner-icon + width 18px + height 18px + box-sizing border-box + border solid 2px transparent + border-top-color $accentColor + border-left-color $accentColor + border-radius 50% + animation nprogress-spinner 400ms linear infinite + +.nprogress-custom-parent + overflow hidden + position relative + +.nprogress-custom-parent #nprogress .spinner, +.nprogress-custom-parent #nprogress .bar + position absolute + +@keyframes nprogress-spinner + 0% + transform rotate(0deg) + 100% + transform rotate(360deg) diff --git a/node_modules/vuepress/lib/default-theme/styles/theme.styl b/node_modules/vuepress/lib/default-theme/styles/theme.styl new file mode 100644 index 00000000..a733861f --- /dev/null +++ b/node_modules/vuepress/lib/default-theme/styles/theme.styl @@ -0,0 +1,190 @@ +@require './config' +@require './nprogress' +@require './code' +@require './custom-blocks' +@require './arrow' +@require './wrapper' +@require './toc' + +html, body + padding 0 + margin 0 + +body + font-family -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif + -webkit-font-smoothing antialiased + -moz-osx-font-smoothing grayscale + font-size 16px + color $textColor + +.page + padding-left $sidebarWidth + +.navbar + position fixed + z-index 20 + top 0 + left 0 + right 0 + height $navbarHeight + background-color #fff + box-sizing border-box + border-bottom 1px solid $borderColor + +.sidebar-mask + position fixed + z-index 9 + top 0 + left 0 + width 100vw + height 100vh + display none + +.sidebar + font-size 15px + background-color #fff + width $sidebarWidth + position fixed + z-index 10 + margin 0 + top $navbarHeight + left 0 + bottom 0 + box-sizing border-box + border-right 1px solid $borderColor + overflow-y auto + +.content:not(.custom) + @extend $wrapper + > *:first-child + margin-top $navbarHeight + a:hover + text-decoration underline + p.demo + padding 1rem 1.5rem + border 1px solid #ddd + border-radius 4px + img + max-width 100% + +.content.custom + padding 0 + margin 0 + img + max-width 100% + +a + font-weight 500 + color $accentColor + text-decoration none + +p a code + font-weight 400 + color $accentColor + +kbd + background #eee + border solid 0.15rem #ddd + border-bottom solid 0.25rem #ddd + border-radius 0.15rem + padding 0 0.15em + +blockquote + font-size 1.2rem + color #999 + border-left .25rem solid #dfe2e5 + margin-left 0 + padding-left 1rem + +ul, ol + padding-left 1.2em + +strong + font-weight 600 + +h1, h2, h3, h4, h5, h6 + font-weight 600 + line-height 1.25 + .content:not(.custom) > & + margin-top (0.5rem - $navbarHeight) + padding-top ($navbarHeight + 1rem) + margin-bottom 0 + &:first-child + margin-top -1.5rem + margin-bottom 1rem + + p, + pre, + .custom-block + margin-top 2rem + &:hover .header-anchor + opacity: 1 + +h1 + font-size 2.2rem + +h2 + font-size 1.65rem + padding-bottom .3rem + border-bottom 1px solid $borderColor + +h3 + font-size 1.35rem + +a.header-anchor + font-size 0.85em + float left + margin-left -0.87em + padding-right 0.23em + margin-top 0.125em + opacity 0 + &:hover + text-decoration none + +code, kbd, .line-number + font-family source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace + +p, ul, ol + line-height 1.7 + +hr + border 0 + border-top 1px solid $borderColor + +table + border-collapse collapse + margin 1rem 0 + display: block + overflow-x: auto + +tr + border-top 1px solid #dfe2e5 + &:nth-child(2n) + background-color #f6f8fa + +th, td + border 1px solid #dfe2e5 + padding .6em 1em + +.custom-layout + padding-top $navbarHeight + +.theme-container + &.sidebar-open + .sidebar-mask + display: block + &.no-navbar + .content:not(.custom) > h1, h2, h3, h4, h5, h6 + margin-top 1.5rem + padding-top 0 + .sidebar + top 0 + .custom-layout + padding-top 0 + + +@media (min-width: ($MQMobile + 1px)) + .theme-container.no-sidebar + .sidebar + display none + .page + padding-left 0 + +@require './mobile.styl' diff --git a/node_modules/vuepress/lib/default-theme/styles/toc.styl b/node_modules/vuepress/lib/default-theme/styles/toc.styl new file mode 100644 index 00000000..d3e71069 --- /dev/null +++ b/node_modules/vuepress/lib/default-theme/styles/toc.styl @@ -0,0 +1,3 @@ +.table-of-contents + .badge + vertical-align middle diff --git a/node_modules/vuepress/lib/default-theme/styles/wrapper.styl b/node_modules/vuepress/lib/default-theme/styles/wrapper.styl new file mode 100644 index 00000000..a99262c7 --- /dev/null +++ b/node_modules/vuepress/lib/default-theme/styles/wrapper.styl @@ -0,0 +1,9 @@ +$wrapper + max-width $contentWidth + margin 0 auto + padding 2rem 2.5rem + @media (max-width: $MQNarrow) + padding 2rem + @media (max-width: $MQMobileNarrow) + padding 1.5rem + diff --git a/node_modules/vuepress/lib/default-theme/util.js b/node_modules/vuepress/lib/default-theme/util.js new file mode 100644 index 00000000..ef95bea5 --- /dev/null +++ b/node_modules/vuepress/lib/default-theme/util.js @@ -0,0 +1,216 @@ +export const hashRE = /#.*$/ +export const extRE = /\.(md|html)$/ +export const endingSlashRE = /\/$/ +export const outboundRE = /^(https?:|mailto:|tel:)/ + +export function normalize (path) { + return decodeURI(path) + .replace(hashRE, '') + .replace(extRE, '') +} + +export function getHash (path) { + const match = path.match(hashRE) + if (match) { + return match[0] + } +} + +export function isExternal (path) { + return outboundRE.test(path) +} + +export function isMailto (path) { + return /^mailto:/.test(path) +} + +export function isTel (path) { + return /^tel:/.test(path) +} + +export function ensureExt (path) { + if (isExternal(path)) { + return path + } + const hashMatch = path.match(hashRE) + const hash = hashMatch ? hashMatch[0] : '' + const normalized = normalize(path) + + if (endingSlashRE.test(normalized)) { + return path + } + return normalized + '.html' + hash +} + +export function isActive (route, path) { + const routeHash = route.hash + const linkHash = getHash(path) + if (linkHash && routeHash !== linkHash) { + return false + } + const routePath = normalize(route.path) + const pagePath = normalize(path) + return routePath === pagePath +} + +export function resolvePage (pages, rawPath, base) { + if (base) { + rawPath = resolvePath(rawPath, base) + } + const path = normalize(rawPath) + for (let i = 0; i < pages.length; i++) { + if (normalize(pages[i].path) === path) { + return Object.assign({}, pages[i], { + type: 'page', + path: ensureExt(rawPath) + }) + } + } + console.error(`[vuepress] No matching page found for sidebar item "${rawPath}"`) + return {} +} + +function resolvePath (relative, base, append) { + const firstChar = relative.charAt(0) + if (firstChar === '/') { + return relative + } + + if (firstChar === '?' || firstChar === '#') { + return base + relative + } + + const stack = base.split('/') + + // remove trailing segment if: + // - not appending + // - appending to trailing slash (last segment is empty) + if (!append || !stack[stack.length - 1]) { + stack.pop() + } + + // resolve relative path + const segments = relative.replace(/^\//, '').split('/') + for (let i = 0; i < segments.length; i++) { + const segment = segments[i] + if (segment === '..') { + stack.pop() + } else if (segment !== '.') { + stack.push(segment) + } + } + + // ensure leading slash + if (stack[0] !== '') { + stack.unshift('') + } + + return stack.join('/') +} + +export function resolveSidebarItems (page, route, site, localePath) { + const { pages, themeConfig } = site + + const localeConfig = localePath && themeConfig.locales + ? themeConfig.locales[localePath] || themeConfig + : themeConfig + + const pageSidebarConfig = page.frontmatter.sidebar || localeConfig.sidebar || themeConfig.sidebar + if (pageSidebarConfig === 'auto') { + return resolveHeaders(page) + } + + const sidebarConfig = localeConfig.sidebar || themeConfig.sidebar + if (!sidebarConfig) { + return [] + } else { + const { base, config } = resolveMatchingConfig(route, sidebarConfig) + return config + ? config.map(item => resolveItem(item, pages, base)) + : [] + } +} + +function resolveHeaders (page) { + const headers = groupHeaders(page.headers || []) + return [{ + type: 'group', + collapsable: false, + title: page.title, + children: headers.map(h => ({ + type: 'auto', + title: h.title, + basePath: page.path, + path: page.path + '#' + h.slug, + children: h.children || [] + })) + }] +} + +export function groupHeaders (headers) { + // group h3s under h2 + headers = headers.map(h => Object.assign({}, h)) + let lastH2 + headers.forEach(h => { + if (h.level === 2) { + lastH2 = h + } else if (lastH2) { + (lastH2.children || (lastH2.children = [])).push(h) + } + }) + return headers.filter(h => h.level === 2) +} + +export function resolveNavLinkItem (linkItem) { + return Object.assign(linkItem, { + type: linkItem.items && linkItem.items.length ? 'links' : 'link' + }) +} + +export function resolveMatchingConfig (route, config) { + if (Array.isArray(config)) { + return { + base: '/', + config: config + } + } + for (const base in config) { + if (ensureEndingSlash(route.path).indexOf(base) === 0) { + return { + base, + config: config[base] + } + } + } + return {} +} + +function ensureEndingSlash (path) { + return /(\.html|\/)$/.test(path) + ? path + : path + '/' +} + +function resolveItem (item, pages, base, isNested) { + if (typeof item === 'string') { + return resolvePage(pages, item, base) + } else if (Array.isArray(item)) { + return Object.assign(resolvePage(pages, item[0], base), { + title: item[1] + }) + } else { + if (isNested) { + console.error( + '[vuepress] Nested sidebar groups are not supported. ' + + 'Consider using navbar + categories instead.' + ) + } + const children = item.children || [] + return { + type: 'group', + title: item.title, + children: children.map(child => resolveItem(child, pages, base, true)), + collapsable: item.collapsable !== false + } + } +} diff --git a/node_modules/vuepress/lib/dev.js b/node_modules/vuepress/lib/dev.js new file mode 100644 index 00000000..ba909d99 --- /dev/null +++ b/node_modules/vuepress/lib/dev.js @@ -0,0 +1,149 @@ +module.exports = async function dev (sourceDir, cliOptions = {}) { + const fs = require('fs') + const path = require('path') + const chalk = require('chalk') + const webpack = require('webpack') + const chokidar = require('chokidar') + const serve = require('webpack-serve') + const convert = require('koa-connect') + const mount = require('koa-mount') + const range = require('koa-range') + const serveStatic = require('koa-static') + const history = require('connect-history-api-fallback') + + const prepare = require('./prepare') + const logger = require('./util/logger') + const HeadPlugin = require('./webpack/HeadPlugin') + const DevLogPlugin = require('./webpack/DevLogPlugin') + const createClientConfig = require('./webpack/createClientConfig') + const { applyUserWebpackConfig } = require('./util') + const { frontmatterEmitter } = require('./webpack/markdownLoader') + + logger.wait('\nExtracting site metadata...') + const options = await prepare(sourceDir) + + // setup watchers to update options and dynamically generated files + const update = () => { + prepare(sourceDir).catch(err => { + console.error(logger.error(chalk.red(err.stack), false)) + }) + } + + // watch add/remove of files + const pagesWatcher = chokidar.watch([ + '**/*.md', + '.vuepress/components/**/*.vue' + ], { + cwd: sourceDir, + ignored: '.vuepress/**/*.md', + ignoreInitial: true + }) + pagesWatcher.on('add', update) + pagesWatcher.on('unlink', update) + pagesWatcher.on('addDir', update) + pagesWatcher.on('unlinkDir', update) + + // watch config file + const configWatcher = chokidar.watch([ + '.vuepress/config.js', + '.vuepress/config.yml', + '.vuepress/config.toml' + ], { + cwd: sourceDir, + ignoreInitial: true + }) + configWatcher.on('change', update) + + // also listen for frontmatter changes from markdown files + frontmatterEmitter.on('update', update) + + // resolve webpack config + let config = createClientConfig(options, cliOptions) + + config + .plugin('html') + // using a fork of html-webpack-plugin to avoid it requiring webpack + // internals from an incompatible version. + .use(require('vuepress-html-webpack-plugin'), [{ + template: path.resolve(__dirname, 'app/index.dev.html') + }]) + + config + .plugin('site-data') + .use(HeadPlugin, [{ + tags: options.siteConfig.head || [] + }]) + + const port = await resolvePort(cliOptions.port || options.siteConfig.port) + const { host, displayHost } = await resolveHost(cliOptions.host || options.siteConfig.host) + + config + .plugin('vuepress-log') + .use(DevLogPlugin, [{ + port, + displayHost, + publicPath: options.publicPath + }]) + + config = config.toConfig() + const userConfig = options.siteConfig.configureWebpack + if (userConfig) { + config = applyUserWebpackConfig(userConfig, config, false /* isServer */) + } + + const compiler = webpack(config) + + const nonExistentDir = path.resolve(__dirname, 'non-existent') + await serve({ + // avoid project cwd from being served. Otherwise if the user has index.html + // in cwd it would break the server + content: [nonExistentDir], + compiler, + host, + dev: { logLevel: 'warn' }, + hot: { + port: port + 1, + logLevel: 'error' + }, + logLevel: 'error', + port, + add: app => { + const userPublic = path.resolve(sourceDir, '.vuepress/public') + + // enable range request + app.use(range) + + // respect base when serving static files... + if (fs.existsSync(userPublic)) { + app.use(mount(options.publicPath, serveStatic(userPublic))) + } + + app.use(convert(history({ + rewrites: [ + { from: /\.html$/, to: '/' } + ] + }))) + } + }) +} + +function resolveHost (host) { + // webpack-serve hot updates doesn't work properly over 0.0.0.0 on Windows, + // but localhost does not allow visiting over network :/ + const defaultHost = process.platform === 'win32' ? 'localhost' : '0.0.0.0' + host = host || defaultHost + const displayHost = host === defaultHost && process.platform !== 'win32' + ? 'localhost' + : host + return { + displayHost, + host + } +} + +async function resolvePort (port) { + const portfinder = require('portfinder') + portfinder.basePort = parseInt(port) || 8080 + port = await portfinder.getPortPromise() + return port +} diff --git a/node_modules/vuepress/lib/eject.js b/node_modules/vuepress/lib/eject.js new file mode 100644 index 00000000..e96b00fd --- /dev/null +++ b/node_modules/vuepress/lib/eject.js @@ -0,0 +1,16 @@ +const fs = require('fs-extra') +const path = require('path') +const chalk = require('chalk') +const logger = require('./util/logger') + +module.exports = async (dir) => { + const source = path.resolve(__dirname, 'default-theme') + const target = path.resolve(dir, '.vuepress/theme') + await fs.copy(source, target) + // remove the import to default theme override + const styleConfig = path.resolve(target, 'styles/config.styl') + const content = await fs.readFile(styleConfig, 'utf-8') + const transformed = content.split('\n').slice(0, -2).join('\n') + await fs.writeFile(styleConfig, transformed) + logger.success(`\nCopied default theme into ${chalk.cyan(target)}.\n`) +} diff --git a/node_modules/vuepress/lib/index.js b/node_modules/vuepress/lib/index.js new file mode 100644 index 00000000..8e4d4a19 --- /dev/null +++ b/node_modules/vuepress/lib/index.js @@ -0,0 +1,4 @@ +exports.dev = require('./dev') +exports.build = require('./build') +exports.eject = require('./eject') +Object.assign(exports, require('./util')) diff --git a/node_modules/vuepress/lib/markdown/component.js b/node_modules/vuepress/lib/markdown/component.js new file mode 100644 index 00000000..1aab66e8 --- /dev/null +++ b/node_modules/vuepress/lib/markdown/component.js @@ -0,0 +1,81 @@ +// Replacing the default htmlBlock rule to allow using custom components at +// root level + +const blockNames = require('markdown-it/lib/common/html_blocks') +const HTML_OPEN_CLOSE_TAG_RE = require('markdown-it/lib/common/html_re').HTML_OPEN_CLOSE_TAG_RE + +// An array of opening and corresponding closing sequences for html tags, +// last argument defines whether it can terminate a paragraph or not +const HTML_SEQUENCES = [ + [/^<(script|pre|style)(?=(\s|>|$))/i, /<\/(script|pre|style)>/i, true], + [/^/, true], + [/^<\?/, /\?>/, true], + [/^/, true], + [/^/, true], + // PascalCase Components + [/^<[A-Z]/, />/, true], + // custom elements with hyphens + [/^<\w+\-/, />/, true], + [new RegExp('^|$))', 'i'), /^$/, true], + [new RegExp(HTML_OPEN_CLOSE_TAG_RE.source + '\\s*$'), /^$/, false] +] + +module.exports = md => { + md.block.ruler.at('html_block', htmlBlock) +} + +function htmlBlock (state, startLine, endLine, silent) { + let i, nextLine, lineText + let pos = state.bMarks[startLine] + state.tShift[startLine] + let max = state.eMarks[startLine] + + // if it's indented more than 3 spaces, it should be a code block + if (state.sCount[startLine] - state.blkIndent >= 4) { return false } + + if (!state.md.options.html) { return false } + + if (state.src.charCodeAt(pos) !== 0x3C/* < */) { return false } + + lineText = state.src.slice(pos, max) + + for (i = 0; i < HTML_SEQUENCES.length; i++) { + if (HTML_SEQUENCES[i][0].test(lineText)) { break } + } + + if (i === HTML_SEQUENCES.length) { + console.log(lineText) + return false + } + + if (silent) { + // true if this sequence can be a terminator, false otherwise + return HTML_SEQUENCES[i][2] + } + + nextLine = startLine + 1 + + // If we are here - we detected HTML block. + // Let's roll down till block end. + if (!HTML_SEQUENCES[i][1].test(lineText)) { + for (; nextLine < endLine; nextLine++) { + if (state.sCount[nextLine] < state.blkIndent) { break } + + pos = state.bMarks[nextLine] + state.tShift[nextLine] + max = state.eMarks[nextLine] + lineText = state.src.slice(pos, max) + + if (HTML_SEQUENCES[i][1].test(lineText)) { + if (lineText.length !== 0) { nextLine++ } + break + } + } + } + + state.line = nextLine + + const token = state.push('html_block', '', 0) + token.map = [startLine, nextLine] + token.content = state.getLines(startLine, nextLine, state.blkIndent, true) + + return true +} diff --git a/node_modules/vuepress/lib/markdown/containers.js b/node_modules/vuepress/lib/markdown/containers.js new file mode 100644 index 00000000..215c4534 --- /dev/null +++ b/node_modules/vuepress/lib/markdown/containers.js @@ -0,0 +1,28 @@ +const container = require('markdown-it-container') + +module.exports = md => { + md + .use(...createContainer('tip', 'TIP')) + .use(...createContainer('warning', 'WARNING')) + .use(...createContainer('danger', 'WARNING')) + // explicitly escape Vue syntax + .use(container, 'v-pre', { + render: (tokens, idx) => tokens[idx].nesting === 1 + ? `
\n` + : `
\n` + }) +} + +function createContainer (klass, defaultTitle) { + return [container, klass, { + render (tokens, idx) { + const token = tokens[idx] + const info = token.info.trim().slice(klass.length).trim() + if (token.nesting === 1) { + return `

${info || defaultTitle}

\n` + } else { + return `
\n` + } + } + }] +} diff --git a/node_modules/vuepress/lib/markdown/highlight.js b/node_modules/vuepress/lib/markdown/highlight.js new file mode 100644 index 00000000..02d5e3ff --- /dev/null +++ b/node_modules/vuepress/lib/markdown/highlight.js @@ -0,0 +1,47 @@ +const chalk = require('chalk') +const prism = require('prismjs') +const loadLanguages = require('prismjs/components/index') +const escapeHtml = require('escape-html') +const logger = require('../util/logger') + +// required to make embedded highlighting work... +loadLanguages(['markup', 'css', 'javascript']) + +function wrap (code, lang) { + if (lang === 'text') { + code = escapeHtml(code) + } + return `
${code}
` +} + +module.exports = (str, lang) => { + if (!lang) { + return wrap(str, 'text') + } + lang = lang.toLowerCase() + const rawLang = lang + if (lang === 'vue' || lang === 'html') { + lang = 'markup' + } + if (lang === 'md') { + lang = 'markdown' + } + if (lang === 'ts') { + lang = 'typescript' + } + if (lang === 'py') { + lang = 'python' + } + if (!prism.languages[lang]) { + try { + loadLanguages([lang]) + } catch (e) { + logger.warn(chalk.yellow(`[vuepress] Syntax highlight for language "${lang}" is not supported.`)) + } + } + if (prism.languages[lang]) { + const code = prism.highlight(str, prism.languages[lang], lang) + return wrap(code, rawLang) + } + return wrap(str, 'text') +} diff --git a/node_modules/vuepress/lib/markdown/highlightLines.js b/node_modules/vuepress/lib/markdown/highlightLines.js new file mode 100644 index 00000000..dd833432 --- /dev/null +++ b/node_modules/vuepress/lib/markdown/highlightLines.js @@ -0,0 +1,49 @@ +// Modified from https://github.com/egoist/markdown-it-highlight-lines + +const RE = /{([\d,-]+)}/ +const wrapperRE = /^
/
+
+module.exports = md => {
+  const fence = md.renderer.rules.fence
+  md.renderer.rules.fence = (...args) => {
+    const [tokens, idx, options] = args
+    const token = tokens[idx]
+
+    const rawInfo = token.info
+    if (!rawInfo || !RE.test(rawInfo)) {
+      return fence(...args)
+    }
+
+    const langName = rawInfo.replace(RE, '').trim()
+    // ensure the next plugin get the correct lang.
+    token.info = langName
+
+    const lineNumbers = RE.exec(rawInfo)[1]
+      .split(',')
+      .map(v => v.split('-').map(v => parseInt(v, 10)))
+
+    const code = options.highlight
+      ? options.highlight(token.content, langName)
+      : token.content
+
+    const rawCode = code.replace(wrapperRE, '')
+    const highlightLinesCode = rawCode.split('\n').map((split, index) => {
+      const lineNumber = index + 1
+      const inRange = lineNumbers.some(([start, end]) => {
+        if (start && end) {
+          return lineNumber >= start && lineNumber <= end
+        }
+        return lineNumber === start
+      })
+      if (inRange) {
+        return `
 
` + } + return '
' + }).join('') + + const highlightLinesWrapperCode = + `
${highlightLinesCode}
` + + return highlightLinesWrapperCode + code + } +} diff --git a/node_modules/vuepress/lib/markdown/hoist.js b/node_modules/vuepress/lib/markdown/hoist.js new file mode 100644 index 00000000..a6383398 --- /dev/null +++ b/node_modules/vuepress/lib/markdown/hoist.js @@ -0,0 +1,14 @@ +module.exports = md => { + const RE = /^<(script|style)(?=(\s|>|$))/i + + md.renderer.rules.html_block = (tokens, idx) => { + const content = tokens[idx].content + const hoistedTags = md.__data.hoistedTags || (md.__data.hoistedTags = []) + if (RE.test(content.trim())) { + hoistedTags.push(content) + return '' + } else { + return content + } + } +} diff --git a/node_modules/vuepress/lib/markdown/index.js b/node_modules/vuepress/lib/markdown/index.js new file mode 100644 index 00000000..f4942f6c --- /dev/null +++ b/node_modules/vuepress/lib/markdown/index.js @@ -0,0 +1,78 @@ +const highlight = require('./highlight') +const highlightLines = require('./highlightLines') +const preWrapper = require('./preWrapper') +const lineNumbers = require('./lineNumbers') +const component = require('./component') +const hoistScriptStyle = require('./hoist') +const convertRouterLink = require('./link') +const containers = require('./containers') +const snippet = require('./snippet') +const emoji = require('markdown-it-emoji') +const anchor = require('markdown-it-anchor') +const toc = require('markdown-it-table-of-contents') +const _slugify = require('./slugify') +const { parseHeaders } = require('../util/parseHeaders') + +module.exports = ({ markdown = {}} = {}) => { + // allow user config slugify + const slugify = markdown.slugify || _slugify + + const md = require('markdown-it')({ + html: true, + highlight + }) + // custom plugins + .use(component) + .use(highlightLines) + .use(preWrapper) + .use(snippet) + .use(convertRouterLink, Object.assign({ + target: '_blank', + rel: 'noopener noreferrer' + }, markdown.externalLinks)) + .use(hoistScriptStyle) + .use(containers) + + // 3rd party plugins + .use(emoji) + .use(anchor, Object.assign({ + slugify, + permalink: true, + permalinkBefore: true, + permalinkSymbol: '#' + }, markdown.anchor)) + .use(toc, Object.assign({ + slugify, + includeLevel: [2, 3], + format: parseHeaders + }, markdown.toc)) + + // apply user config + if (markdown.config) { + markdown.config(md) + } + + if (markdown.lineNumbers) { + md.use(lineNumbers) + } + + module.exports.dataReturnable(md) + + // expose slugify + md.slugify = slugify + + return md +} + +module.exports.dataReturnable = function dataReturnable (md) { + // override render to allow custom plugins return data + const render = md.render + md.render = (...args) => { + md.__data = {} + const html = render.call(md, ...args) + return { + html, + data: md.__data + } + } +} diff --git a/node_modules/vuepress/lib/markdown/lineNumbers.js b/node_modules/vuepress/lib/markdown/lineNumbers.js new file mode 100644 index 00000000..bc95d2b0 --- /dev/null +++ b/node_modules/vuepress/lib/markdown/lineNumbers.js @@ -0,0 +1,26 @@ +// markdown-it plugin for generating line numbers. +// It depends on preWrapper plugin. + +module.exports = md => { + const fence = md.renderer.rules.fence + md.renderer.rules.fence = (...args) => { + const rawCode = fence(...args) + const code = rawCode.slice( + rawCode.indexOf(''), + rawCode.indexOf('') + ) + + const lines = code.split('\n') + const lineNumbersCode = [...Array(lines.length - 1)] + .map((line, index) => `${index + 1}
`).join('') + + const lineNumbersWrapperCode = + `
${lineNumbersCode}
` + + const finalCode = rawCode + .replace('', `${lineNumbersWrapperCode}`) + .replace('extra-class', 'line-numbers-mode') + + return finalCode + } +} diff --git a/node_modules/vuepress/lib/markdown/link.js b/node_modules/vuepress/lib/markdown/link.js new file mode 100644 index 00000000..bbe9550d --- /dev/null +++ b/node_modules/vuepress/lib/markdown/link.js @@ -0,0 +1,91 @@ +// markdown-it plugin for: +// 1. adding target="_blank" to external links +// 2. converting internal links to + +const indexRE = /(.*)(index|readme).md(#?.*)$/i + +module.exports = (md, externalAttrs) => { + let hasOpenRouterLink = false + let hasOpenExternalLink = false + + md.renderer.rules.link_open = (tokens, idx, options, env, self) => { + const token = tokens[idx] + const hrefIndex = token.attrIndex('href') + if (hrefIndex >= 0) { + const link = token.attrs[hrefIndex] + const href = link[1] + const isExternal = /^https?:/.test(href) + const isSourceLink = /(\/|\.md|\.html)(#.*)?$/.test(href) + if (isExternal) { + Object.entries(externalAttrs).forEach(([key, val]) => { + token.attrSet(key, val) + }) + if (/_blank/i.test(externalAttrs['target'])) { + hasOpenExternalLink = true + } + } else if (isSourceLink) { + hasOpenRouterLink = true + tokens[idx] = toRouterLink(token, link) + } + } + return self.renderToken(tokens, idx, options) + } + + function toRouterLink (token, link) { + link[0] = 'to' + let to = link[1] + + // convert link to filename and export it for existence check + const links = md.__data.links || (md.__data.links = []) + links.push(to) + + const indexMatch = to.match(indexRE) + if (indexMatch) { + const [, path, , hash] = indexMatch + to = path + hash + } else { + to = to + .replace(/\.md$/, '.html') + .replace(/\.md(#.*)$/, '.html$1') + } + + // relative path usage. + if (!to.startsWith('/')) { + to = ensureBeginningDotSlash(to) + } + + // markdown-it encodes the uri + link[1] = decodeURI(to) + + // export the router links for testing + const routerLinks = md.__data.routerLinks || (md.__data.routerLinks = []) + routerLinks.push(to) + + return Object.assign({}, token, { + tag: 'router-link' + }) + } + + md.renderer.rules.link_close = (tokens, idx, options, env, self) => { + const token = tokens[idx] + if (hasOpenRouterLink) { + token.tag = 'router-link' + hasOpenRouterLink = false + } + if (hasOpenExternalLink) { + hasOpenExternalLink = false + // add OutBoundLink to the beforeend of this link if it opens in _blank. + return '' + self.renderToken(tokens, idx, options) + } + return self.renderToken(tokens, idx, options) + } +} + +const beginningSlashRE = /^\.\// + +function ensureBeginningDotSlash (path) { + if (beginningSlashRE.test(path)) { + return path + } + return './' + path +} diff --git a/node_modules/vuepress/lib/markdown/preWrapper.js b/node_modules/vuepress/lib/markdown/preWrapper.js new file mode 100644 index 00000000..8f1b1d73 --- /dev/null +++ b/node_modules/vuepress/lib/markdown/preWrapper.js @@ -0,0 +1,19 @@ +// markdown-it plugin for wrapping
 ... 
. +// +// If your plugin was chained before preWrapper, you can add additional eleemnt directly. +// If your plugin was chained after preWrapper, you can use these slots: +// 1. +// 2. +// 3. +// 4. + +module.exports = md => { + const fence = md.renderer.rules.fence + md.renderer.rules.fence = (...args) => { + const [tokens, idx] = args + const token = tokens[idx] + const rawCode = fence(...args) + return `
` + + `${rawCode}
` + } +} diff --git a/node_modules/vuepress/lib/markdown/slugify.js b/node_modules/vuepress/lib/markdown/slugify.js new file mode 100644 index 00000000..47a9d6e9 --- /dev/null +++ b/node_modules/vuepress/lib/markdown/slugify.js @@ -0,0 +1,22 @@ +// string.js slugify drops non ascii chars so we have to +// use a custom implementation here +const removeDiacritics = require('diacritics').remove +// eslint-disable-next-line no-control-regex +const rControl = /[\u0000-\u001f]/g +const rSpecial = /[\s~`!@#$%^&*()\-_+=[\]{}|\\;:"'<>,.?/]+/g + +module.exports = function slugify (str) { + return removeDiacritics(str) + // Remove control characters + .replace(rControl, '') + // Replace special characters + .replace(rSpecial, '-') + // Remove continous separators + .replace(/\-{2,}/g, '-') + // Remove prefixing and trailing separtors + .replace(/^\-+|\-+$/g, '') + // ensure it doesn't start with a number (#121) + .replace(/^(\d)/, '_$1') + // lowercase + .toLowerCase() +} diff --git a/node_modules/vuepress/lib/markdown/snippet.js b/node_modules/vuepress/lib/markdown/snippet.js new file mode 100644 index 00000000..a677e12f --- /dev/null +++ b/node_modules/vuepress/lib/markdown/snippet.js @@ -0,0 +1,43 @@ +const fs = require('fs') + +module.exports = function snippet (md, options = {}) { + const root = options.root || process.cwd() + function parser (state, startLine, endLine, silent) { + const CH = '<'.charCodeAt(0) + const pos = state.bMarks[startLine] + state.tShift[startLine] + const max = state.eMarks[startLine] + + // if it's indented more than 3 spaces, it should be a code block + if (state.sCount[startLine] - state.blkIndent >= 4) { + return false + } + + for (let i = 0; i < 3; ++i) { + const ch = state.src.charCodeAt(pos + i) + if (ch !== CH || pos + i >= max) return false + } + + if (silent) { + return true + } + + const start = pos + 3 + const end = state.skipSpacesBack(max, pos) + const rawPath = state.src.slice(start, end).trim().replace(/^@/, root) + const filename = rawPath.split(/[{:\s]/).shift() + const content = fs.existsSync(filename) ? fs.readFileSync(filename).toString() : 'Not found: ' + filename + const meta = rawPath.replace(filename, '') + + state.line = startLine + 1 + + const token = state.push('fence', 'code', 0) + token.info = filename.split('.').pop() + meta + token.content = content + token.markup = '```' + token.map = [startLine, startLine + 1] + + return true + } + + md.block.ruler.before('fence', 'snippet', parser) +} diff --git a/node_modules/vuepress/lib/prepare/codegen.js b/node_modules/vuepress/lib/prepare/codegen.js new file mode 100644 index 00000000..52e85355 --- /dev/null +++ b/node_modules/vuepress/lib/prepare/codegen.js @@ -0,0 +1,74 @@ +const path = require('path') +const { fileToComponentName, resolveComponents } = require('./util') + +exports.genRoutesFile = async function ({ + siteData: { pages }, + sourceDir, + pageFiles +}) { + function genRoute ({ path: pagePath, key: componentName }, index) { + const file = pageFiles[index] + const filePath = path.resolve(sourceDir, file) + let code = ` + { + name: ${JSON.stringify(componentName)}, + path: ${JSON.stringify(pagePath)}, + component: ThemeLayout, + beforeEnter: (to, from, next) => { + import(${JSON.stringify(filePath)}).then(comp => { + Vue.component(${JSON.stringify(componentName)}, comp.default) + next() + }) + } + }` + + const dncodedPath = decodeURIComponent(pagePath) + if (dncodedPath !== pagePath) { + code += `, + { + path: ${JSON.stringify(dncodedPath)}, + redirect: ${JSON.stringify(pagePath)} + }` + } + + if (/\/$/.test(pagePath)) { + code += `, + { + path: ${JSON.stringify(pagePath + 'index.html')}, + redirect: ${JSON.stringify(pagePath)} + }` + } + + return code + } + + const notFoundRoute = `, + { + path: '*', + component: ThemeNotFound + }` + + return ( + `import ThemeLayout from '@themeLayout'\n` + + `import ThemeNotFound from '@themeNotFound'\n` + + `import { injectMixins } from '@app/util'\n` + + `import rootMixins from '@app/root-mixins'\n\n` + + `injectMixins(ThemeLayout, rootMixins)\n` + + `injectMixins(ThemeNotFound, rootMixins)\n\n` + + `export const routes = [${pages.map(genRoute).join(',')}${notFoundRoute}\n]` + ) +} + +exports.genComponentRegistrationFile = async function ({ sourceDir }) { + function genImport (file) { + const name = fileToComponentName(file) + const baseDir = path.resolve(sourceDir, '.vuepress/components') + const absolutePath = path.resolve(baseDir, file) + const code = `Vue.component(${JSON.stringify(name)}, () => import(${JSON.stringify(absolutePath)}))` + return code + } + + const components = (await resolveComponents(sourceDir)) || [] + return `import Vue from 'vue'\n` + components.map(genImport).join('\n') +} + diff --git a/node_modules/vuepress/lib/prepare/index.js b/node_modules/vuepress/lib/prepare/index.js new file mode 100644 index 00000000..7561f791 --- /dev/null +++ b/node_modules/vuepress/lib/prepare/index.js @@ -0,0 +1,51 @@ +const path = require('path') +const fs = require('fs-extra') +const resolveOptions = require('./resolveOptions') +const { genRoutesFile, genComponentRegistrationFile } = require('./codegen') +const { writeTemp, writeEnhanceTemp } = require('./util') +const logger = require('../util/logger') +const chalk = require('chalk') + +module.exports = async function prepare (sourceDir) { + // 1. load options + const options = await resolveOptions(sourceDir) + + // 2. generate routes & user components registration code + const routesCode = await genRoutesFile(options) + const componentCode = await genComponentRegistrationFile(options) + + await writeTemp('routes.js', [ + componentCode, + routesCode + ].join('\n')) + + // 3. generate siteData + const dataCode = `export const siteData = ${JSON.stringify(options.siteData, null, 2)}` + await writeTemp('siteData.js', dataCode) + + // 4. handle user override + const overridePath = path.resolve(sourceDir, '.vuepress/override.styl').replace(/[\\]+/g, '/') + const hasUserOverride = fs.existsSync(overridePath) + await writeTemp('override.styl', hasUserOverride ? `@import(${JSON.stringify(overridePath)})` : ``) + + const stylePath = path.resolve(sourceDir, '.vuepress/style.styl').replace(/[\\]+/g, '/') + const hasUserStyle = fs.existsSync(stylePath) + await writeTemp('style.styl', hasUserStyle ? `@import(${JSON.stringify(stylePath)})` : ``) + + // Temporary tip, will be removed at next release. + if (hasUserOverride && !hasUserStyle) { + logger.tip( + `${chalk.magenta('override.styl')} has been split into 2 APIs, we recommend you upgrade to continue.\n` + + ` See: ${chalk.magenta('https://vuepress.vuejs.org/default-theme-config/#simple-css-override')}` + ) + } + + // 5. handle enhanceApp.js + const enhanceAppPath = path.resolve(sourceDir, '.vuepress/enhanceApp.js') + await writeEnhanceTemp('enhanceApp.js', enhanceAppPath) + + // 6. handle the theme enhanceApp.js + await writeEnhanceTemp('themeEnhanceApp.js', options.themeEnhanceAppPath) + + return options +} diff --git a/node_modules/vuepress/lib/prepare/loadConfig.js b/node_modules/vuepress/lib/prepare/loadConfig.js new file mode 100644 index 00000000..aa913ba0 --- /dev/null +++ b/node_modules/vuepress/lib/prepare/loadConfig.js @@ -0,0 +1,56 @@ +const fs = require('fs-extra') +const path = require('path') +const yamlParser = require('js-yaml') +const tomlParser = require('toml') + +module.exports = function loadConfig (vuepressDir, bustCache = true) { + const configPath = path.resolve(vuepressDir, 'config.js') + const configYmlPath = path.resolve(vuepressDir, 'config.yml') + const configTomlPath = path.resolve(vuepressDir, 'config.toml') + + if (bustCache) { + delete require.cache[configPath] + } + + // resolve siteConfig + let siteConfig = {} + if (fs.existsSync(configYmlPath)) { + siteConfig = parseConfig(configYmlPath) + } else if (fs.existsSync(configTomlPath)) { + siteConfig = parseConfig(configTomlPath) + } else if (fs.existsSync(configPath)) { + siteConfig = require(configPath) + } + + return siteConfig +} + +function parseConfig (file) { + const content = fs.readFileSync(file, 'utf-8') + const [extension] = /.\w+$/.exec(file) + let data + + switch (extension) { + case '.yml': + case '.yaml': + data = yamlParser.safeLoad(content) + break + + case '.toml': + data = tomlParser.parse(content) + // reformat to match config since TOML does not allow different data type + // https://github.com/toml-lang/toml#array + const format = [] + if (data.head) { + Object.keys(data.head).forEach(meta => { + data.head[meta].forEach(values => { + format.push([meta, values]) + }) + }) + } + data.head = format + break + } + + return data || {} +} diff --git a/node_modules/vuepress/lib/prepare/resolveOptions.js b/node_modules/vuepress/lib/prepare/resolveOptions.js new file mode 100644 index 00000000..9b97ca8e --- /dev/null +++ b/node_modules/vuepress/lib/prepare/resolveOptions.js @@ -0,0 +1,194 @@ +const fs = require('fs-extra') +const path = require('path') +const globby = require('globby') +const createMarkdown = require('../markdown') +const loadConfig = require('./loadConfig') +const { encodePath, fileToPath, sort, getGitLastUpdatedTimeStamp } = require('./util') +const { + inferTitle, + extractHeaders, + parseFrontmatter +} = require('../util/index') + +module.exports = async function resolveOptions (sourceDir) { + const vuepressDir = path.resolve(sourceDir, '.vuepress') + const siteConfig = loadConfig(vuepressDir) + + // normalize head tag urls for base + const base = siteConfig.base || '/' + if (base !== '/' && siteConfig.head) { + siteConfig.head.forEach(tag => { + const attrs = tag[1] + if (attrs) { + for (const name in attrs) { + if (name === 'src' || name === 'href') { + const value = attrs[name] + if (value.charAt(0) === '/') { + attrs[name] = base + value.slice(1) + } + } + } + } + }) + } + + // resolve outDir + const outDir = siteConfig.dest + ? path.resolve(siteConfig.dest) + : path.resolve(sourceDir, '.vuepress/dist') + + // resolve theme + const useDefaultTheme = ( + !siteConfig.theme && + !fs.existsSync(path.resolve(vuepressDir, 'theme')) + ) + const defaultThemePath = path.resolve(__dirname, '../default-theme') + let themePath = null + let themeLayoutPath = null + let themeNotFoundPath = null + let themeEnhanceAppPath = null + + if (useDefaultTheme) { + // use default theme + themePath = defaultThemePath + themeLayoutPath = path.resolve(defaultThemePath, 'Layout.vue') + themeNotFoundPath = path.resolve(defaultThemePath, 'NotFound.vue') + } else { + // resolve theme Layout + if (siteConfig.theme) { + // use external theme + try { + themeLayoutPath = require.resolve(`vuepress-theme-${siteConfig.theme}/Layout.vue`, { + paths: [ + path.resolve(__dirname, '../../node_modules'), + path.resolve(sourceDir) + ] + }) + themePath = path.dirname(themeLayoutPath) + } catch (e) { + throw new Error(`[vuepress] Failed to load custom theme "${ + siteConfig.theme + }". File vuepress-theme-${siteConfig.theme}/Layout.vue does not exist.`) + } + } else { + // use custom theme + themePath = path.resolve(vuepressDir, 'theme') + themeLayoutPath = path.resolve(themePath, 'Layout.vue') + if (!fs.existsSync(themeLayoutPath)) { + throw new Error(`[vuepress] Cannot resolve Layout.vue file in .vuepress/theme.`) + } + } + + // resolve theme NotFound + themeNotFoundPath = path.resolve(themePath, 'NotFound.vue') + if (!fs.existsSync(themeNotFoundPath)) { + themeNotFoundPath = path.resolve(defaultThemePath, 'NotFound.vue') + } + + // resolve theme enhanceApp + themeEnhanceAppPath = path.resolve(themePath, 'enhanceApp.js') + if (!fs.existsSync(themeEnhanceAppPath)) { + themeEnhanceAppPath = null + } + } + + // resolve theme config + const themeConfig = siteConfig.themeConfig || {} + + // resolve algolia + const isAlgoliaSearch = ( + themeConfig.algolia || + Object.keys(siteConfig.locales && themeConfig.locales || {}) + .some(base => themeConfig.locales[base].algolia) + ) + + // resolve markdown + const markdown = createMarkdown(siteConfig) + + // resolve pageFiles + const patterns = ['**/*.md', '!.vuepress', '!node_modules'] + if (siteConfig.dest) { + // #654 exclude dest folder when dest dir was set in + // sourceDir but not in '.vuepress' + const outDirRelative = path.relative(sourceDir, outDir) + if (!outDirRelative.includes('..')) { + patterns.push('!' + outDirRelative) + } + } + const pageFiles = sort(await globby(patterns, { cwd: sourceDir })) + + // resolve lastUpdated + const shouldResolveLastUpdated = ( + themeConfig.lastUpdated || + Object.keys(siteConfig.locales && themeConfig.locales || {}) + .some(base => themeConfig.locales[base].lastUpdated) + ) + + // resolve pagesData + const pagesData = await Promise.all(pageFiles.map(async (file) => { + const filepath = path.resolve(sourceDir, file) + const key = 'v-' + Math.random().toString(16).slice(2) + const data = { + key, + path: encodePath(fileToPath(file)) + } + + if (shouldResolveLastUpdated) { + data.lastUpdated = getGitLastUpdatedTimeStamp(filepath) + } + + // extract yaml frontmatter + const content = await fs.readFile(filepath, 'utf-8') + const frontmatter = parseFrontmatter(content) + // infer title + const title = inferTitle(frontmatter) + if (title) { + data.title = title + } + const headers = extractHeaders( + frontmatter.content, + ['h2', 'h3'], + markdown + ) + if (headers.length) { + data.headers = headers + } + if (Object.keys(frontmatter.data).length) { + data.frontmatter = frontmatter.data + } + if (frontmatter.excerpt) { + const { html } = markdown.render(frontmatter.excerpt) + data.excerpt = html + } + return data + })) + + // resolve site data + const siteData = { + title: siteConfig.title || '', + description: siteConfig.description || '', + base, + pages: pagesData, + themeConfig, + locales: siteConfig.locales + } + + const options = { + siteConfig, + siteData, + sourceDir, + outDir, + publicPath: base, + pageFiles, + pagesData, + themePath, + themeLayoutPath, + themeNotFoundPath, + themeEnhanceAppPath, + useDefaultTheme, + isAlgoliaSearch, + markdown + } + + return options +} diff --git a/node_modules/vuepress/lib/prepare/util.js b/node_modules/vuepress/lib/prepare/util.js new file mode 100644 index 00000000..efa96cc9 --- /dev/null +++ b/node_modules/vuepress/lib/prepare/util.js @@ -0,0 +1,80 @@ +const path = require('path') +const spawn = require('cross-spawn') +const fs = require('fs-extra') +const globby = require('globby') + +const tempPath = path.resolve(__dirname, '../app/.temp') +fs.ensureDirSync(tempPath) + +const tempCache = new Map() +exports.writeTemp = async function (file, content) { + // cache write to avoid hitting the dist if it didn't change + const cached = tempCache.get(file) + if (cached !== content) { + await fs.writeFile(path.join(tempPath, file), content) + tempCache.set(file, content) + } +} + +exports.writeEnhanceTemp = async function (destName, srcPath) { + await exports.writeTemp( + destName, + fs.existsSync(srcPath) + ? `export { default } from ${JSON.stringify(srcPath)}` + : `export default function () {}` + ) +} + +const indexRE = /(^|.*\/)(index|readme)\.md$/i +const extRE = /\.(vue|md)$/ + +exports.fileToPath = function (file) { + if (exports.isIndexFile(file)) { + // README.md -> / + // foo/README.md -> /foo/ + return file.replace(indexRE, '/$1') + } else { + // foo.md -> /foo.html + // foo/bar.md -> /foo/bar.html + return `/${file.replace(extRE, '').replace(/\\/g, '/')}.html` + } +} + +exports.fileToComponentName = function (file) { + let normalizedName = file + .replace(/\/|\\/g, '-') + .replace(extRE, '') + if (exports.isIndexFile(file)) { + normalizedName = normalizedName.replace(/readme$/i, 'index') + } + const pagePrefix = /\.md$/.test(file) ? `page-` : `` + return `${pagePrefix}${normalizedName}` +} + +exports.isIndexFile = function (file) { + return indexRE.test(file) +} + +exports.resolveComponents = async function (sourceDir) { + const componentDir = path.resolve(sourceDir, '.vuepress/components') + if (!fs.existsSync(componentDir)) { + return + } + return exports.sort(await globby(['**/*.vue'], { cwd: componentDir })) +} + +exports.sort = function (arr) { + return arr.sort((a, b) => { + if (a < b) return -1 + if (a > b) return 1 + return 0 + }) +} + +exports.encodePath = function (userpath) { + return userpath.split('/').map(item => encodeURIComponent(item)).join('/') +} + +exports.getGitLastUpdatedTimeStamp = function (filepath) { + return parseInt(spawn.sync('git', ['log', '-1', '--format=%ct', filepath]).stdout.toString('utf-8')) * 1000 +} diff --git a/node_modules/vuepress/lib/service-worker/skip-waiting.js b/node_modules/vuepress/lib/service-worker/skip-waiting.js new file mode 100644 index 00000000..54fd8d37 --- /dev/null +++ b/node_modules/vuepress/lib/service-worker/skip-waiting.js @@ -0,0 +1,12 @@ +addEventListener('message', event => { + const replyPort = event.ports[0] + const message = event.data + if (replyPort && message && message.type === 'skip-waiting') { + event.waitUntil( + self.skipWaiting().then( + () => replyPort.postMessage({ error: null }), + error => replyPort.postMessage({ error }) + ) + ) + } +}) diff --git a/node_modules/vuepress/lib/util/index.js b/node_modules/vuepress/lib/util/index.js new file mode 100644 index 00000000..52044a8f --- /dev/null +++ b/node_modules/vuepress/lib/util/index.js @@ -0,0 +1,83 @@ +const { deeplyParseHeaders } = require('./parseHeaders') + +exports.normalizeHeadTag = function (tag) { + if (typeof tag === 'string') { + tag = [tag] + } + const tagName = tag[0] + return { + tagName, + attributes: tag[1] || {}, + innerHTML: tag[2] || '', + closeTag: !(tagName === 'meta' || tagName === 'link') + } +} + +exports.applyUserWebpackConfig = function (userConfig, config, isServer) { + const merge = require('webpack-merge') + if (typeof userConfig === 'object') { + return merge(config, userConfig) + } + if (typeof userConfig === 'function') { + const res = userConfig(config, isServer) + if (res && typeof res === 'object') { + return merge(config, res) + } + } + return config +} + +exports.inferTitle = function (frontmatter) { + if (frontmatter.data.home) { + return 'Home' + } + if (frontmatter.data.title) { + return deeplyParseHeaders(frontmatter.data.title) + } + const match = frontmatter.content.trim().match(/^#+\s+(.*)/m) + if (match) { + return deeplyParseHeaders(match[1]) + } +} + +exports.parseFrontmatter = function (content) { + const matter = require('gray-matter') + const toml = require('toml') + + return matter(content, { + excerpt_separator: '', + engines: { + toml: toml.parse.bind(toml), + excerpt: false + } + }) +} + +const LRU = require('lru-cache') +const cache = LRU({ max: 1000 }) + +exports.extractHeaders = function (content, include = [], md) { + const key = content + include.join(',') + const hit = cache.get(key) + if (hit) { + return hit + } + + const tokens = md.parse(content, {}) + + const res = [] + tokens.forEach((t, i) => { + if (t.type === 'heading_open' && include.includes(t.tag)) { + const title = tokens[i + 1].content + const slug = t.attrs.find(([name]) => name === 'id')[1] + res.push({ + level: parseInt(t.tag.slice(1), 10), + title: deeplyParseHeaders(title), + slug: slug || md.slugify(title) + }) + } + }) + + cache.set(key, res) + return res +} diff --git a/node_modules/vuepress/lib/util/logger.js b/node_modules/vuepress/lib/util/logger.js new file mode 100644 index 00000000..84e07e86 --- /dev/null +++ b/node_modules/vuepress/lib/util/logger.js @@ -0,0 +1,47 @@ +const chalk = require('chalk') + +const logger = {} + +const logTypes = { + success: { + color: 'green', + label: 'DONE' + }, + error: { + color: 'red', + label: 'FAIL' + }, + warn: { + color: 'yellow', + label: 'WARN' + }, + tip: { + color: 'cyan', + label: 'TIP' + }, + wait: { + color: 'blue', + label: 'WAIT' + } +} + +const getLoggerFn = (color, label) => (msg, log = true) => { + let newLine = false + if (msg.startsWith('\n')) { + if (log) msg = msg.slice(1) + newLine = true + } + msg = chalk.reset.inverse.bold[color](` ${label} `) + ' ' + msg + if (log) { + console.log(newLine ? '\n' + msg : msg) + } else { + return msg + } +} + +for (const type in logTypes) { + const { color, label } = logTypes[type] + logger[type] = getLoggerFn(color, label) +} + +module.exports = logger diff --git a/node_modules/vuepress/lib/util/parseHeaders.js b/node_modules/vuepress/lib/util/parseHeaders.js new file mode 100644 index 00000000..2c9fe641 --- /dev/null +++ b/node_modules/vuepress/lib/util/parseHeaders.js @@ -0,0 +1,56 @@ +// Since VuePress needs to extract the header from the markdown source +// file and display it in the sidebar or title (#238), this file simply +// removes some unnecessary elements to make header displays well at +// sidebar or title. +// +// But header's parsing in the markdown content is done by the markdown +// loader based on markdown-it. markdown-it parser will will always keep +// HTML in headers, so in VuePress, after being parsed by the markdiwn +// loader, the raw HTML in headers will finally be parsed by Vue-loader. +// so that we can write HTML/Vue in the header. One exception is the HTML +// wrapped by (markdown token: '`') tag. + +const { compose } = require('./shared') + +const parseEmojis = str => { + const emojiData = require('markdown-it-emoji/lib/data/full.json') + return String(str).replace(/:(.+?):/g, (placeholder, key) => emojiData[key] || placeholder) +} + +const unescapeHtml = html => String(html) + .replace(/"/g, '"') + .replace(/'/g, '\'') + .replace(/:/g, ':') + .replace(/</g, '<') + .replace(/>/g, '>') + +const removeMarkdownTokens = str => String(str) + .replace(/\[(.*)\]\(.*\)/, '$1') // []() + .replace(/(`|\*{1,3}|_)(.*?[^\\])\1/g, '$2') // `{t}` | *{t}* | **{t}** | ***{t}*** | _{t}_ + .replace(/(\\)(\*|_|`|\!)/g, '$2') // remove escape char '\' + +const trim = str => str.trim() + +// This method remove the raw HTML but reserve the HTML wrapped by ``. +// e.g. +// Input: " b", Output: "b" +// Input: "`` b", Output: "`` b" +exports.removeNonCodeWrappedHTML = (str) => { + return String(str).replace(/(^|[^><`])<.*>([^><`]|$)/g, '$1$2') +} + +// Unescape html, parse emojis and remove some md tokens. +exports.parseHeaders = compose( + unescapeHtml, + parseEmojis, + removeMarkdownTokens, + trim +) + +// Also clean the html that isn't wrapped by code. +// Because we want to support using VUE components in headers. +// e.g. https://vuepress.vuejs.org/guide/using-vue.html#badge +exports.deeplyParseHeaders = compose( + exports.removeNonCodeWrappedHTML, + exports.parseHeaders, +) diff --git a/node_modules/vuepress/lib/util/shared.js b/node_modules/vuepress/lib/util/shared.js new file mode 100644 index 00000000..023f63bd --- /dev/null +++ b/node_modules/vuepress/lib/util/shared.js @@ -0,0 +1,7 @@ +exports.compose = (...processors) => { + if (processors.length === 0) return input => input + if (processors.length === 1) return processors[0] + return processors.reduce((prev, next) => { + return (...args) => next(prev(...args)) + }) +} diff --git a/node_modules/vuepress/lib/webpack/ClientPlugin.js b/node_modules/vuepress/lib/webpack/ClientPlugin.js new file mode 100644 index 00000000..82028475 --- /dev/null +++ b/node_modules/vuepress/lib/webpack/ClientPlugin.js @@ -0,0 +1,87 @@ +// Temporarily hacked dev build + +var isJS = function (file) { return /\.js(\?[^.]+)?$/.test(file) } + +var isCSS = function (file) { return /\.css(\?[^.]+)?$/.test(file) } + +var onEmit = function (compiler, name, hook) { + if (compiler.hooks) { + // Webpack >= 4.0.0 + compiler.hooks.emit.tapAsync(name, hook) + } else { + // Webpack < 4.0.0 + compiler.plugin('emit', hook) + } +} + +var hash = require('hash-sum') +var uniq = require('lodash.uniq') +var VueSSRClientPlugin = function VueSSRClientPlugin (options) { + if (options === void 0) options = {} + + this.options = Object.assign({ + filename: 'vue-ssr-client-manifest.json' + }, options) +} + +VueSSRClientPlugin.prototype.apply = function apply (compiler) { + var this$1 = this + + onEmit(compiler, 'vue-client-plugin', function (compilation, cb) { + var stats = compilation.getStats().toJson() + + var allFiles = uniq(stats.assets + .map(function (a) { return a.name })) + // Avoid preloading / injecting the style chunk + .filter(file => !/styles\.\w{8}\.js$/.test(file)) + + var initialFiles = uniq(Object.keys(stats.entrypoints) + .map(function (name) { return stats.entrypoints[name].assets }) + .reduce(function (assets, all) { return all.concat(assets) }, []) + .filter(function (file) { return isJS(file) || isCSS(file) })) + // Avoid preloading / injecting the style chunk + .filter(file => !/styles\.\w{8}\.js$/.test(file)) + + var asyncFiles = allFiles + .filter(function (file) { return isJS(file) || isCSS(file) }) + .filter(function (file) { return initialFiles.indexOf(file) < 0 }) + + var manifest = { + publicPath: stats.publicPath, + all: allFiles, + initial: initialFiles, + async: asyncFiles, + modules: { /* [identifier: string]: Array */ } + } + + var assetModules = stats.modules.filter(function (m) { return m.assets.length }) + var fileToIndex = function (file) { return manifest.all.indexOf(file) } + stats.modules.forEach(function (m) { + // ignore modules duplicated in multiple chunks + if (m.chunks.length === 1) { + var cid = m.chunks[0] + var chunk = stats.chunks.find(function (c) { return c.id === cid }) + if (!chunk || !chunk.files) { + return + } + var id = m.identifier.replace(/\s\w+$/, '') // remove appended hash + var files = manifest.modules[hash(id)] = chunk.files.map(fileToIndex) + // find all asset modules associated with the same chunk + assetModules.forEach(function (m) { + if (m.chunks.some(function (id) { return id === cid })) { + files.push.apply(files, m.assets.map(fileToIndex)) + } + }) + } + }) + + var json = JSON.stringify(manifest, null, 2) + compilation.assets[this$1.options.filename] = { + source: function () { return json }, + size: function () { return json.length } + } + cb() + }) +} + +module.exports = VueSSRClientPlugin diff --git a/node_modules/vuepress/lib/webpack/DevLogPlugin.js b/node_modules/vuepress/lib/webpack/DevLogPlugin.js new file mode 100644 index 00000000..8fd18d4a --- /dev/null +++ b/node_modules/vuepress/lib/webpack/DevLogPlugin.js @@ -0,0 +1,38 @@ +const chalk = require('chalk') +const logger = require('../util/logger') + +module.exports = class DevLogPlugin { + constructor (options) { + this.options = options + } + + apply (compiler) { + let isFirst = true + compiler.hooks.done.tap('vuepress-log', stats => { + clearScreen() + + const { displayHost, port, publicPath } = this.options + const time = new Date().toTimeString().match(/^[\d:]+/)[0] + const displayUrl = `http://${displayHost}:${port}${publicPath}` + + logger.success( + `\n${chalk.gray(`[${time}]`)} Build ${chalk.italic(stats.hash.slice(0, 6))} ` + + `finished in ${stats.endTime - stats.startTime} ms! ` + + ( + isFirst + ? '' + : `${chalk.gray(`(${displayUrl})`)}` + ) + ) + if (isFirst) { + isFirst = false + console.log(`\n${chalk.gray('>')} VuePress dev server listening at ${chalk.cyan(displayUrl)}`) + } + }) + compiler.hooks.invalid.tap('vuepress-log', clearScreen) + } +} + +function clearScreen () { + process.stdout.write('\x1Bc') +} diff --git a/node_modules/vuepress/lib/webpack/HeadPlugin.js b/node_modules/vuepress/lib/webpack/HeadPlugin.js new file mode 100644 index 00000000..2ccb46e0 --- /dev/null +++ b/node_modules/vuepress/lib/webpack/HeadPlugin.js @@ -0,0 +1,22 @@ +const { normalizeHeadTag } = require('../util') + +module.exports = class SiteDataPlugin { + constructor ({ tags }) { + this.tags = tags + } + + apply (compiler) { + compiler.hooks.compilation.tap('vuepress-site-data', compilation => { + compilation.hooks.htmlWebpackPluginAlterAssetTags.tapAsync('vuepress-site-data', (data, cb) => { + try { + this.tags.forEach(tag => { + data.head.push(normalizeHeadTag(tag)) + }) + } catch (e) { + return cb(e) + } + cb(null, data) + }) + }) + } +} diff --git a/node_modules/vuepress/lib/webpack/createBaseConfig.js b/node_modules/vuepress/lib/webpack/createBaseConfig.js new file mode 100644 index 00000000..dcf7fab3 --- /dev/null +++ b/node_modules/vuepress/lib/webpack/createBaseConfig.js @@ -0,0 +1,315 @@ +const path = require('path') + +module.exports = function createBaseConfig ({ + siteConfig, + siteData, + sourceDir, + outDir, + publicPath, + themePath, + themeLayoutPath, + themeNotFoundPath, + isAlgoliaSearch, + markdown +}, { debug } = {}, isServer) { + const Config = require('webpack-chain') + const { VueLoaderPlugin } = require('vue-loader') + const CSSExtractPlugin = require('mini-css-extract-plugin') + + const isProd = process.env.NODE_ENV === 'production' + const inlineLimit = 10000 + + const config = new Config() + + config + .mode(isProd && !debug ? 'production' : 'development') + .output + .path(outDir) + .filename(isProd ? 'assets/js/[name].[chunkhash:8].js' : 'assets/js/[name].js') + .publicPath(isProd ? publicPath : '/') + + if (debug) { + config.devtool('source-map') + } else if (!isProd) { + config.devtool('cheap-module-eval-source-map') + } + + config.resolve + .set('symlinks', true) + .alias + .set('@theme', themePath) + .set('@themeLayout', themeLayoutPath) + .set('@themeNotFound', themeNotFoundPath) + .set('@source', sourceDir) + .set('@app', path.resolve(__dirname, '../app')) + .set('@temp', path.resolve(__dirname, '../app/.temp')) + .set('@default-theme', path.resolve(__dirname, '../default-theme')) + .set('@AlgoliaSearchBox', isAlgoliaSearch + ? path.resolve(__dirname, '../default-theme/AlgoliaSearchBox.vue') + : path.resolve(__dirname, './noopModule.js')) + .end() + .extensions + .merge(['.js', '.jsx', '.vue', '.json', '.styl']) + .end() + .modules + // prioritize our own + .add(path.resolve(__dirname, '../../node_modules')) + .add(path.resolve(__dirname, '../../../')) + .add('node_modules') + + // Load extra root mixins on demand. + const { activeHeaderLinks = true } = siteData.themeConfig + const rootMixinsLoadingConfig = { activeHeaderLinks } + for (const mixinName in rootMixinsLoadingConfig) { + const enabled = rootMixinsLoadingConfig[mixinName] + config.resolve + .alias.set(`@${mixinName}`, enabled + ? path.resolve(__dirname, `../app/root-mixins/${mixinName}.js`) + : path.resolve(__dirname, './noopModule.js') + ) + } + + config.resolveLoader + .set('symlinks', true) + .modules + // prioritize our own + .add(path.resolve(__dirname, '../../node_modules')) + .add(path.resolve(__dirname, '../../../')) + .add('node_modules') + + config.module + .noParse(/^(vue|vue-router|vuex|vuex-router-sync)$/) + + const cacheDirectory = path.resolve(__dirname, '../../node_modules/.cache/vuepress') + const cacheIdentifier = JSON.stringify({ + vuepress: require('../../package.json').version, + 'cache-loader': require('cache-loader').version, + 'vue-loader': require('vue-loader').version, + isProd, + isServer, + config: ( + (siteConfig.markdown ? JSON.stringify(siteConfig.markdown) : '') + + (siteConfig.chainWebpack || '').toString() + + (siteConfig.configureWebpack || '').toString() + ) + }) + + function applyVuePipeline (rule) { + rule + .use('cache-loader') + .loader('cache-loader') + .options({ + cacheDirectory, + cacheIdentifier + }) + + rule + .use('vue-loader') + .loader('vue-loader') + .options({ + compilerOptions: { + preserveWhitespace: true + }, + cacheDirectory, + cacheIdentifier + }) + } + + const vueRule = config.module + .rule('vue') + .test(/\.vue$/) + + applyVuePipeline(vueRule) + + const mdRule = config.module + .rule('markdown') + .test(/\.md$/) + + applyVuePipeline(mdRule) + + mdRule + .use('markdown-loader') + .loader(require.resolve('./markdownLoader')) + .options({ sourceDir, markdown }) + + config.module + .rule('pug') + .test(/\.pug$/) + .use('pug-plain-loader') + .loader('pug-plain-loader') + .end() + + if (!siteConfig.evergreen) { + const libDir = path.join(__dirname, '..') + config.module + .rule('js') + .test(/\.js$/) + .exclude.add(filepath => { + // Always transpile lib directory + if (filepath.startsWith(libDir)) { + return false + } + // always transpile js in vue files + if (/\.vue\.js$/.test(filepath)) { + return false + } + // Don't transpile node_modules + return /node_modules/.test(filepath) + }).end() + .use('cache-loader') + .loader('cache-loader') + .options({ + cacheDirectory, + cacheIdentifier + }) + .end() + .use('babel-loader') + .loader('babel-loader') + .options({ + // do not pick local project babel config (.babelrc) + babelrc: false, + // do not pick local project babel config (babel.config.js) + // ref: http://babeljs.io/docs/en/config-files + configFile: false, + presets: [ + require.resolve('@vue/babel-preset-app') + ] + }) + } + + config.module + .rule('images') + .test(/\.(png|jpe?g|gif)(\?.*)?$/) + .use('url-loader') + .loader('url-loader') + .options({ + limit: inlineLimit, + name: `assets/img/[name].[hash:8].[ext]` + }) + + // do not base64-inline SVGs. + // https://github.com/facebookincubator/create-react-app/pull/1180 + config.module + .rule('svg') + .test(/\.(svg)(\?.*)?$/) + .use('file-loader') + .loader('file-loader') + .options({ + name: `assets/img/[name].[hash:8].[ext]` + }) + + config.module + .rule('media') + .test(/\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/) + .use('url-loader') + .loader('url-loader') + .options({ + limit: inlineLimit, + name: `assets/media/[name].[hash:8].[ext]` + }) + + config.module + .rule('fonts') + .test(/\.(woff2?|eot|ttf|otf)(\?.*)?$/i) + .use('url-loader') + .loader('url-loader') + .options({ + limit: inlineLimit, + name: `assets/fonts/[name].[hash:8].[ext]` + }) + + function createCSSRule (lang, test, loader, options) { + const baseRule = config.module.rule(lang).test(test) + const modulesRule = baseRule.oneOf('modules').resourceQuery(/module/) + const normalRule = baseRule.oneOf('normal') + + applyLoaders(modulesRule, true) + applyLoaders(normalRule, false) + + function applyLoaders (rule, modules) { + if (!isServer) { + if (isProd) { + rule.use('extract-css-loader').loader(CSSExtractPlugin.loader) + } else { + rule.use('vue-style-loader').loader('vue-style-loader') + } + } + + rule.use('css-loader') + .loader(isServer ? 'css-loader/locals' : 'css-loader') + .options({ + modules, + localIdentName: `[local]_[hash:base64:8]`, + importLoaders: 1, + sourceMap: !isProd + }) + + rule.use('postcss-loader').loader('postcss-loader').options(Object.assign({ + plugins: [require('autoprefixer')], + sourceMap: !isProd + }, siteConfig.postcss)) + + if (loader) { + rule.use(loader).loader(loader).options(options) + } + } + } + + createCSSRule('css', /\.css$/) + createCSSRule('postcss', /\.p(ost)?css$/) + createCSSRule('scss', /\.scss$/, 'sass-loader', siteConfig.scss) + createCSSRule('sass', /\.sass$/, 'sass-loader', Object.assign({ indentedSyntax: true }, siteConfig.sass)) + createCSSRule('less', /\.less$/, 'less-loader', siteConfig.less) + createCSSRule('stylus', /\.styl(us)?$/, 'stylus-loader', Object.assign({ + preferPathResolver: 'webpack' + }, siteConfig.stylus)) + + config + .plugin('vue-loader') + .use(VueLoaderPlugin) + + if (isProd && !isServer) { + config + .plugin('extract-css') + .use(CSSExtractPlugin, [{ + filename: 'assets/css/styles.[chunkhash:8].css' + }]) + + // ensure all css are extracted together. + // since most of the CSS will be from the theme and very little + // CSS will be from async chunks + config.optimization.splitChunks({ + cacheGroups: { + styles: { + name: 'styles', + // necessary to ensure async chunks are also extracted + test: m => /css-extract/.test(m.type), + chunks: 'all', + enforce: true + } + } + }) + } + + // inject constants + config + .plugin('injections') + .use(require('webpack/lib/DefinePlugin'), [{ + BASE_URL: JSON.stringify(siteConfig.base || '/'), + GA_ID: siteConfig.ga ? JSON.stringify(siteConfig.ga) : false, + SW_ENABLED: !!siteConfig.serviceWorker, + VUEPRESS_VERSION: JSON.stringify(require('../../package.json').version), + LAST_COMMIT_HASH: JSON.stringify(getLastCommitHash()) + }]) + + return config +} + +function getLastCommitHash () { + const spawn = require('cross-spawn') + let hash + try { + hash = spawn.sync('git', ['log', '-1', '--format=%h']).stdout.toString('utf-8').trim() + } catch (error) {} + return hash +} diff --git a/node_modules/vuepress/lib/webpack/createClientConfig.js b/node_modules/vuepress/lib/webpack/createClientConfig.js new file mode 100644 index 00000000..8c276633 --- /dev/null +++ b/node_modules/vuepress/lib/webpack/createClientConfig.js @@ -0,0 +1,69 @@ +module.exports = function createClientConfig (options, cliOptions) { + const path = require('path') + const WebpackBar = require('webpackbar') + const createBaseConfig = require('./createBaseConfig') + + const config = createBaseConfig(options, cliOptions) + + config + .entry('app') + .add(path.resolve(__dirname, '../app/clientEntry.js')) + + config.node + .merge({ + // prevent webpack from injecting useless setImmediate polyfill because Vue + // source contains it (although only uses it if it's native). + setImmediate: false, + global: false, + process: false, + // prevent webpack from injecting mocks to Node native modules + // that does not make sense for the client + dgram: 'empty', + fs: 'empty', + net: 'empty', + tls: 'empty', + child_process: 'empty' + }) + + // generate client manifest only during build + if (process.env.NODE_ENV === 'production') { + // This is a temp build of vue-server-renderer/client-plugin. + // TODO Switch back to original after problems are resolved. + // Fixes two things: + // 1. Include CSS in preload files + // 2. filter out useless styles.xxxxx.js chunk from mini-css-extract-plugin + // https://github.com/webpack-contrib/mini-css-extract-plugin/issues/85 + config + .plugin('ssr-client') + .use(require('./ClientPlugin'), [{ + filename: 'manifest/client.json' + }]) + + config + .plugin('optimize-css') + .use(require('optimize-css-assets-webpack-plugin'), [{ + canPrint: false, + cssProcessorOptions: { + safe: true, + autoprefixer: { disable: true }, + mergeLonghand: false + } + }]) + } + + if (!cliOptions.debug) { + config + .plugin('bar') + .use(WebpackBar, [{ + name: 'Client', + color: '#41b883', + compiledIn: false + }]) + } + + if (options.siteConfig.chainWebpack) { + options.siteConfig.chainWebpack(config, false /* isServer */) + } + + return config +} diff --git a/node_modules/vuepress/lib/webpack/createServerConfig.js b/node_modules/vuepress/lib/webpack/createServerConfig.js new file mode 100644 index 00000000..e9f09835 --- /dev/null +++ b/node_modules/vuepress/lib/webpack/createServerConfig.js @@ -0,0 +1,58 @@ +module.exports = function createServerConfig (options, cliOptions) { + const fs = require('fs') + const path = require('path') + const WebpackBar = require('webpackbar') + const createBaseConfig = require('./createBaseConfig') + const VueSSRServerPlugin = require('vue-server-renderer/server-plugin') + const CopyPlugin = require('copy-webpack-plugin') + + const config = createBaseConfig(options, cliOptions, true /* isServer */) + const { sourceDir, outDir } = options + + config + .target('node') + .externals([/^vue|vue-router$/]) + .devtool('source-map') + + // no need to minimize server build + config.optimization.minimize(false) + + config + .entry('app') + .add(path.resolve(__dirname, '../app/serverEntry.js')) + + config.output + .filename('server-bundle.js') + .libraryTarget('commonjs2') + + config + .plugin('ssr-server') + .use(VueSSRServerPlugin, [{ + filename: 'manifest/server.json' + }]) + + const publicDir = path.resolve(sourceDir, '.vuepress/public') + if (fs.existsSync(publicDir)) { + config + .plugin('copy') + .use(CopyPlugin, [[ + { from: publicDir, to: outDir } + ]]) + } + + if (!cliOptions.debug) { + config + .plugin('bar') + .use(WebpackBar, [{ + name: 'Server', + color: 'blue', + compiledIn: false + }]) + } + + if (options.siteConfig.chainWebpack) { + options.siteConfig.chainWebpack(config, true /* isServer */) + } + + return config +} diff --git a/node_modules/vuepress/lib/webpack/markdownLoader.js b/node_modules/vuepress/lib/webpack/markdownLoader.js new file mode 100644 index 00000000..3c49b747 --- /dev/null +++ b/node_modules/vuepress/lib/webpack/markdownLoader.js @@ -0,0 +1,101 @@ +const fs = require('fs') +const path = require('path') +const hash = require('hash-sum') +const { EventEmitter } = require('events') +const { getOptions } = require('loader-utils') +const { inferTitle, extractHeaders, parseFrontmatter } = require('../util') +const LRU = require('lru-cache') + +const cache = LRU({ max: 1000 }) +const devCache = LRU({ max: 1000 }) + +module.exports = function (src) { + const isProd = process.env.NODE_ENV === 'production' + const isServer = this.target === 'node' + const { markdown, sourceDir } = getOptions(this) + + // we implement a manual cache here because this loader is chained before + // vue-loader, and will be applied on the same file multiple times when + // selecting the individual blocks. + const file = this.resourcePath + const key = hash(file + src) + const cached = cache.get(key) + if (cached && (isProd || /\?vue/.test(this.resourceQuery))) { + return cached + } + + const frontmatter = parseFrontmatter(src) + const content = frontmatter.content + + if (!isProd && !isServer) { + const inferredTitle = inferTitle(frontmatter) + const headers = extractHeaders(content, ['h2', 'h3'], markdown) + delete frontmatter.content + + // diff frontmatter and title, since they are not going to be part of the + // returned component, changes in frontmatter do not trigger proper updates + const cachedData = devCache.get(file) + if (cachedData && ( + cachedData.inferredTitle !== inferredTitle || + JSON.stringify(cachedData.frontmatter) !== JSON.stringify(frontmatter) || + headersChanged(cachedData.headers, headers) + )) { + // frontmatter changed... need to do a full reload + module.exports.frontmatterEmitter.emit('update') + } + + devCache.set(file, { + headers, + frontmatter, + inferredTitle + }) + } + + // the render method has been augmented to allow plugins to + // register data during render + const { html, data: { hoistedTags, links }} = markdown.render(content) + + // check if relative links are valid + links && links.forEach(link => { + link = decodeURIComponent(link) + const shortname = link + .replace(/#.*$/, '') + .replace(/\.html$/, '.md') + const filename = shortname + .replace(/\/$/, '/README.md') + .replace(/^\//, sourceDir + '/') + const altname = shortname + .replace(/\/$/, '/index.md') + .replace(/^\//, sourceDir + '/') + const dir = path.dirname(this.resourcePath) + const file = path.resolve(dir, filename) + const altfile = altname !== filename ? path.resolve(dir, altname) : null + if (!fs.existsSync(file) && (!altfile || !fs.existsSync(altfile))) { + this.emitWarning( + new Error( + `\nFile for relative link "${link}" does not exist.\n` + + `(Resolved file: ${file})\n` + ) + ) + } + }) + + const res = ( + `\n` + + (hoistedTags || []).join('\n') + ) + cache.set(key, res) + return res +} + +function headersChanged (a, b) { + if (a.length !== b.length) return true + return a.some((h, i) => ( + h.title !== b[i].title || + h.level !== b[i].level + )) +} + +module.exports.frontmatterEmitter = new EventEmitter() diff --git a/node_modules/vuepress/lib/webpack/noopModule.js b/node_modules/vuepress/lib/webpack/noopModule.js new file mode 100644 index 00000000..b1c6ea43 --- /dev/null +++ b/node_modules/vuepress/lib/webpack/noopModule.js @@ -0,0 +1 @@ +export default {} -- cgit v1.2.3