chore(conflicts): resolve conflicts

This commit is contained in:
giraud florent
2019-09-06 18:44:49 -04:00
351 changed files with 13576 additions and 7626 deletions
+159
View File
@@ -0,0 +1,159 @@
{
"projectName": "VuePress",
"projectOwner": "Ulivz",
"repoType": "github",
"repoHost": "https://github.com",
"files": [
"README.md"
],
"imageSize": 100,
"commit": false,
"commitConvention": "none",
"contributors": [
{
"login": "yyx990803",
"name": "Evan You",
"avatar_url": "https://avatars1.githubusercontent.com/u/499550?v=4",
"profile": "http://evanyou.me",
"contributions": [
"code"
]
},
{
"login": "ulivz",
"name": "ULIVZ",
"avatar_url": "https://avatars1.githubusercontent.com/u/23133919?v=4",
"profile": "http://ulivz.com",
"contributions": [
"code",
"doc"
]
},
{
"login": "jlooper",
"name": "Jen Looper",
"avatar_url": "https://avatars2.githubusercontent.com/u/1450004?v=4",
"profile": "http://www.jenlooper.com",
"contributions": [
"doc",
"design"
]
},
{
"login": "sarahdayan",
"name": "Sarah Dayan",
"avatar_url": "https://avatars0.githubusercontent.com/u/5370675?v=4",
"profile": "https://frontstuff.io/",
"contributions": [
"code",
"doc"
]
},
{
"login": "kefranabg",
"name": "Franck Abgrall",
"avatar_url": "https://avatars3.githubusercontent.com/u/9840435?v=4",
"profile": "https://www.franck-abgrall.me/",
"contributions": [
"code",
"question"
]
},
{
"login": "sobolevn",
"name": "Nikita Sobolev",
"avatar_url": "https://avatars1.githubusercontent.com/u/4660275?v=4",
"profile": "https://sobolevn.me",
"contributions": [
"code",
"doc"
]
},
{
"login": "f3ltron",
"name": "Giraud Florent",
"avatar_url": "https://avatars1.githubusercontent.com/u/11556276?v=4",
"profile": "https://github.com/f3ltron",
"contributions": [
"code"
]
},
{
"login": "CodesOfRa",
"name": "Ramona",
"avatar_url": "https://avatars0.githubusercontent.com/u/945186?v=4",
"profile": "https://twitter.com/CodesOfRa",
"contributions": [
"code"
]
},
{
"login": "fatihacet",
"name": "Fatih Acet",
"avatar_url": "https://avatars3.githubusercontent.com/u/712419?v=4",
"profile": "https://fatihacet.com",
"contributions": [
"code"
]
},
{
"login": "filrak",
"name": "Filip Rakowski",
"avatar_url": "https://avatars2.githubusercontent.com/u/15185752?v=4",
"profile": "https://rakowski.dev",
"contributions": [
"code"
]
},
{
"login": "farcaller",
"name": "Vladimir Pouzanov",
"avatar_url": "https://avatars2.githubusercontent.com/u/693?v=4",
"profile": "http://farcaller.net/",
"contributions": [
"code"
]
},
{
"login": "vicbergquist",
"name": "Victoria Bergquist",
"avatar_url": "https://avatars0.githubusercontent.com/u/25737281?v=4",
"profile": "https://twitter.com/vicbergquist",
"contributions": [
"code",
"design"
]
},
{
"login": "newsbielt703",
"name": "Billyyyyy3320",
"avatar_url": "https://avatars0.githubusercontent.com/u/38957202?v=4",
"profile": "https://billychin.netlify.com/",
"contributions": [
"code"
]
},
{
"login": "bencodezen",
"name": "Ben Hong",
"avatar_url": "https://avatars0.githubusercontent.com/u/4836334?v=4",
"profile": "http://www.bencodezen.io",
"contributions": [
"code",
"blog"
]
},
{
"login": "vinayakkulkarni",
"name": "Vinayak Kulkarni",
"avatar_url": "https://avatars2.githubusercontent.com/u/19776877?v=4",
"profile": "https://twitter.com/_vinayak_k",
"contributions": [
"plugin",
"code",
"blog"
]
},
],
"contributorsPerLine": 7
}
+37
View File
@@ -0,0 +1,37 @@
# Javascript Node CircleCI 2.0 configuration file
#
# Check https://circleci.com/docs/2.0/language-javascript/ for more details
#
version: 2
jobs:
build:
docker:
# specify the version you desire here
- image: circleci/node:10.15.2
# Specify service dependencies here if necessary
# CircleCI maintains a library of pre-built images
# documented at https://circleci.com/docs/2.0/circleci-images/
# - image: circleci/mongo:3.4.4
working_directory: ~/repo
steps:
- checkout
# Download and cache dependencies
- restore_cache:
keys:
- v1-dependencies-{{ checksum "package.json" }}
# fallback to using the latest cache if no exact match is found
- v1-dependencies-
- run: yarn bootstrap
- save_cache:
paths:
- node_modules
key: v1-dependencies-{{ checksum "package.json" }}
# run tests!
- run: yarn build && yarn lint && yarn test
+82
View File
@@ -0,0 +1,82 @@
## Development Setup
This project uses a monorepo setup that requires using [Yarn](https://yarnpkg.com) because it relies on [Yarn workspaces](https://yarnpkg.com/blog/2017/08/02/introducing-workspaces/).
``` sh
# Install dependencies & compile TypeScript utilities.
yarn bootstrap
# Clean dependencies.
yarn clean
# Useful when creating new submodules.
yarn boot
# Serve the docs.
yarn dev
# Build the docs.
yarn build
# Execute all the test suites.
yarn test
```
## Core packages
- **docs**: Docs of VuePress (do not publish to npm).
- **vuepress**: VuePress CLI.
- **packages**
- `core`: containing the Node.js API, the Plugin API, the Theme API, the Client SPA, etc.
- `markdown`: internal Markdown compiler.
- `markdown-loader`: internal Markdown loader.
- `plugin-active-header-links`: a plugin for active sidebar heading links.
- `plugin-google-analytics`: Google Analytics integration.
- `plugin-last-updated`: implementation of "last updated" feature.
- `plugin-medium-zoom`: `medium-zoom` integration.
- `plugin-nprogress`: `nprogress` integration.
- `plugin-pwa`: PWA plugin.
- `plugin-search`: search plugin, providing the `SearchBox` component.
- `shared-utils`: TypeScript utilities.
- `test-utils`: test utilities.
- `theme-default`: default theme.
- `theme-vue`: a theme tweak from default theme, used for the official Vue project.
## Core packages not in main project
> Previously, for quick iteration, these projects were kept in ULIVZ's workspace. In the future, we may want to build an independent GitHub group.
- [awesome-vuepress](https://github.com/ulivz/awesome-vuepress)
- [@vuepress/plugin-blog](https://github.com/ulivz/vuepress-plugin-blog)
- [@vuepress/theme-blog](https://github.com/ulivz/vuepress-theme-blog)
## Workflow
### Issue
> TODO
### Pull requests
- Create a feature branch from the default branch (`master`) and merge back against that branch.
- It's OK to have multiple small commits as you work on the PR - GitHub automatically squashes them before merging.
- Make sure tests pass.
- If adding a new feature:
- Add accompanying test case(s).
- Provide a convincing reason to add this feature. Ideally, you should open a suggestion issue first and have it approved before working on it.
- If fixing bug:
- If you are resolving an open issue, add `(fix #xxxx)` (`#xxxx` being the issue ID) in your PR title for a better release log, e.g. `chore(feat): implement SSR (fix #1234)`.
- Provide a detailed description of the bug in the PR. Live demo preferred.
- Add appropriate test coverage if applicable.
### Substantial Changes
> RFC flow, TODO
## Code Specification
> TODO
## Commit specification
Commit messages should follow the [commit message convention](./COMMIT_CONVENTION.md) so that changelogs can be automatically generated.
+12
View File
@@ -0,0 +1,12 @@
# These are supported funding model platforms
github: ulivz # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: ulivz # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # Replace with a single custom sponsorship URL
+2 -9
View File
@@ -20,13 +20,11 @@ about: Create a report to help us improve
## Bug report
#### Version
#### Steps to reproduce
<!-- If you are reporting a bug that can ONLY be reproduced on your repository, PLEASE provide this repo link. That takes guessing work out of the way and saves us time. -->
<!-- If your repo isn't public, you can use this boilerplate to create a proper reproduction repo quickly: https://github.com/ulivz/template-vuepress -->
<!-- If your repo isn't public, you can use `codesandbox` or `yarn create vuepress` to create a minimal reproduction -->
#### What is expected?
@@ -34,9 +32,4 @@ about: Create a report to help us improve
#### Other relevant information
- Your OS:
- Node.js version:
- Browser version:
- Is this a global or local install?
- Which package manager did you use for the install?
- Does this issue occur when all plugins are disabled?
- Output of `npx vuepress info` in my VuePress project:
+1 -1
View File
@@ -19,4 +19,4 @@ about: Suggest an idea for this project
#### How should this be implemented in your opinion?
#### Are you willing to work on this yourself?**
#### Are you willing to work on this yourself?
+2
View File
@@ -0,0 +1,2 @@
titleAndCommits: true
allowMergeCommits: false
+6
View File
@@ -0,0 +1,6 @@
ports:
- port: 8080
onOpen: open-preview
tasks:
- init: yarn bootstrap
command: yarn dev
+3
View File
@@ -0,0 +1,3 @@
{
"*.{js,vue}": ["eslint --fix", "git add"]
}
+291 -10
View File
@@ -1,5 +1,286 @@
# [](https://github.com/vuejs/vuepress/compare/v1.0.4...v) (2019-09-06)
## [1.0.4](https://github.com/vuejs/vuepress/compare/v1.0.3...v1.0.4) (2019-09-06)
### Bug Fixes
* **$core:** do not transpile core packages' dependencies ([b69b107](https://github.com/vuejs/vuepress/commit/b69b107))
* **avatar:** fix url ([45e31b4](https://github.com/vuejs/vuepress/commit/45e31b4))
* **contributors:** fix for good contributions ([a709a4f](https://github.com/vuejs/vuepress/commit/a709a4f))
* **contributors:** fix url parameters ([db71238](https://github.com/vuejs/vuepress/commit/db71238))
* add missing hash in [#1706](https://github.com/vuejs/vuepress/issues/1706) ([#1780](https://github.com/vuejs/vuepress/issues/1780)) ([25777e4](https://github.com/vuejs/vuepress/commit/25777e4))
* contributors commit link ([#1809](https://github.com/vuejs/vuepress/issues/1809)) ([bb61182](https://github.com/vuejs/vuepress/commit/bb61182))
### Features
* **$core:** use any custom protocol for outboundRE ([#1731](https://github.com/vuejs/vuepress/issues/1731)) ([120d885](https://github.com/vuejs/vuepress/commit/120d885))
* Disable next and prev links from global config ([#1761](https://github.com/vuejs/vuepress/issues/1761)) ([92a1c02](https://github.com/vuejs/vuepress/commit/92a1c02))
# [](https://github.com/vuejs/vuepress/compare/v1.0.3...v) (2019-07-29)
## [1.0.3](https://github.com/vuejs/vuepress/compare/v1.0.2...v1.0.3) (2019-07-29)
### Bug Fixes
* **$core:** `'[Vue warn]: Unknown custom element'` when using `<Content />` in a custom page without markdown ([#1699](https://github.com/vuejs/vuepress/issues/1699)) ([2a59800](https://github.com/vuejs/vuepress/commit/2a59800)), closes [#1173](https://github.com/vuejs/vuepress/issues/1173) [#1426](https://github.com/vuejs/vuepress/issues/1426)
* **$core:** prioritise vuepress dependencies over cwd node_modules (close: [#1708](https://github.com/vuejs/vuepress/issues/1708)) ([#1720](https://github.com/vuejs/vuepress/issues/1720)) ([52f421b](https://github.com/vuejs/vuepress/commit/52f421b))
* **$core:** transpile all scripts under core (close: [#1623](https://github.com/vuejs/vuepress/issues/1623)) ([#1685](https://github.com/vuejs/vuepress/issues/1685)) ([6460b0c](https://github.com/vuejs/vuepress/commit/6460b0c))
* **$plugin-medium-zoom:** disable zoom for links ([#1719](https://github.com/vuejs/vuepress/issues/1719)) ([e3393e3](https://github.com/vuejs/vuepress/commit/e3393e3))
* **$theme-default:** `sidebarDepth: 0` not working in YAML frontmatter (close: [#1701](https://github.com/vuejs/vuepress/issues/1701)) ([#1702](https://github.com/vuejs/vuepress/issues/1702)) ([0624828](https://github.com/vuejs/vuepress/commit/0624828))
### Features
* **$theme-default:** support custom URL scheme for external links ([#1677](https://github.com/vuejs/vuepress/issues/1677)) ([27f005b](https://github.com/vuejs/vuepress/commit/27f005b))
* **$theme-default:** use router for Algolia search to reach no refresh ([#1706](https://github.com/vuejs/vuepress/issues/1706)) ([644142b](https://github.com/vuejs/vuepress/commit/644142b))
## [1.0.2](https://github.com/vuejs/vuepress/compare/v1.0.1...v1.0.2) (2019-06-22)
### Bug Fixes
* **$core:** cannot resolve agreement file from parent theme ([1aaa6e3](https://github.com/vuejs/vuepress/commit/1aaa6e3))
* **$core:** url display in dev log ([#1670](https://github.com/vuejs/vuepress/issues/1670)) ([17ba325](https://github.com/vuejs/vuepress/commit/17ba325))
* **$plugin-medium-zoom:** doesn't work with default plugin options in default theme ([42f19e0](https://github.com/vuejs/vuepress/commit/42f19e0))
### Features
* **$plugin-medium-zoom:** custom options ([#1649](https://github.com/vuejs/vuepress/issues/1649)) ([54bb2f3](https://github.com/vuejs/vuepress/commit/54bb2f3))
## [1.0.1](https://github.com/vuejs/vuepress/compare/v1.0.0...v1.0.1) (2019-06-10)
### Bug Fixes
* **$theme-default:** using '.theme-default-content' to replace '.content' in 404 layout ([#1646](https://github.com/vuejs/vuepress/issues/1646)) ([f0d1344](https://github.com/vuejs/vuepress/commit/f0d1344))
### Features
* **$theme-default:** enhance the styles of `blockquote` ([9d20d5f](https://github.com/vuejs/vuepress/commit/9d20d5f))
# [1.0.0](https://github.com/vuejs/vuepress/compare/v1.0.0-rc.1...v1.0.0) (2019-06-08)
# [1.0.0-rc.1](https://github.com/vuejs/vuepress/compare/v1.0.0-beta.2...v1.0.0-rc.1) (2019-06-08)
It seems no any changes here.
<a name="1.0.0-beta.2"></a>
# [1.0.0-beta.2](https://github.com/vuejs/vuepress/compare/v1.0.0-beta.1...v1.0.0-beta.2) (2019-06-05)
### Bug Fixes
* **$core:** failed to render `<Content />` with dynamic pageKey from current $page. ([83b02ba](https://github.com/vuejs/vuepress/commit/83b02ba))
<a name=""></a>
# [](https://github.com/vuejs/vuepress/compare/v1.0.0-alpha.40...v) (2019-02-27)
# [](https://github.com/vuejs/vuepress/compare/v1.0.0-beta.1...v) (2019-06-05)
<a name="1.0.0-beta.1"></a>
# [1.0.0-beta.1](https://github.com/vuejs/vuepress/compare/v1.0.0-alpha.50...v1.0.0-beta.1) (2019-06-04)
### Features
* **New Blog Plugin**: [@vuepress/plugin-blog](https://github.com/ulivz/vuepress-plugin-blog)
* A VuePress Blog Theme implemented in around 70 lines**: https://github.com/ulivz/70-lines-of-vuepress-blog-theme
* **Default Blog Theme**: [@vuepress/plugin-blog](https://github.com/ulivz/vuepress-theme-blog)
* Live Example: http://example.vuepress-theme-blog.ulivz.com
### BREAKING CHANGES
* Deprecated `@vuepress/plugin-blog` at alpha stage. ([10dfb66](https://github.com/vuejs/vuepress/commit/10dfb66))
* Deprecated `@vuepress/plugin-pagination` at alpha stage. ([3722192](https://github.com/vuejs/vuepress/commit/3722192))
<a name="1.0.0-alpha.50"></a>
# [1.0.0-alpha.50](https://github.com/vuejs/vuepress/compare/v1.0.0-alpha.49...v1.0.0-alpha.50) (2019-06-04)
### Bug Fixes
* **$core:** window is not defined ([d30e078](https://github.com/vuejs/vuepress/commit/d30e078))
<a name="1.0.0-alpha.49"></a>
# [1.0.0-alpha.49](https://github.com/vuejs/vuepress/compare/v1.0.0-alpha.48...v1.0.0-alpha.49) (2019-06-03)
### Bug Fixes
* **$core:** cannot load theme with shortcut. ([7eed1f4](https://github.com/vuejs/vuepress/commit/7eed1f4))
* **$core:** cannot retrieve the correct theme name when them path is a local absolute path linked to a javascript file. ([50f64b4](https://github.com/vuejs/vuepress/commit/50f64b4))
### Features
* **$core:** global variable "__VUEPRESS__" to store runtime key infos ([bd0bdf9](https://github.com/vuejs/vuepress/commit/bd0bdf9))
* **$core:** safer class name of content outlet ([7d0542e](https://github.com/vuejs/vuepress/commit/7d0542e))
* **$theme-default:** using '.theme-default-content' to replace '.content' in `<Content />` outlet. ([85ff630](https://github.com/vuejs/vuepress/commit/85ff630))
<a name="1.0.0-alpha.48"></a>
# [1.0.0-alpha.48](https://github.com/vuejs/vuepress/compare/v1.0.0-alpha.47...v1.0.0-alpha.48) (2019-05-14)
### Bug Fixes
* **$core:** Failed to load theme when using `layouts/Layout.vue` as entry file (close: [#1563](https://github.com/vuejs/vuepress/issues/1563)) ([#1564](https://github.com/vuejs/vuepress/issues/1564)) ([057d8bf](https://github.com/vuejs/vuepress/commit/057d8bf))
* **$core:** Search result cannot jump to the correct hash anchor (close: [#1594](https://github.com/vuejs/vuepress/issues/1594)) ([#1599](https://github.com/vuejs/vuepress/issues/1599)) ([e6af68f](https://github.com/vuejs/vuepress/commit/e6af68f))
* **$core:** Use directory name to compute slug if filename is readme or index (close: [#1443](https://github.com/vuejs/vuepress/issues/1443)) ([#1535](https://github.com/vuejs/vuepress/issues/1535)) ([9efc678](https://github.com/vuejs/vuepress/commit/9efc678))
* **$core:** webpack externals (ref: [#451](https://github.com/vuejs/vuepress/issues/451)) ([fb324d5](https://github.com/vuejs/vuepress/commit/fb324d5))
### Features
* **$core:** Infer page's date via directory name ([#1553](https://github.com/vuejs/vuepress/issues/1553)) ([2c930c9](https://github.com/vuejs/vuepress/commit/2c930c9))
* **$core:** `info` command ([#1573](https://github.com/vuejs/vuepress/issues/1573)) ([3eeb080](https://github.com/vuejs/vuepress/commit/3eeb080))
* **$core:** Prevent duplicate route ([#1525](https://github.com/vuejs/vuepress/issues/1525)) ([441f023](https://github.com/vuejs/vuepress/commit/441f023))
* **$theme-default:** Support external links in sidebar (close: [#764](https://github.com/vuejs/vuepress/issues/764))([#1534](https://github.com/vuejs/vuepress/issues/1534)) ([141bd11](https://github.com/vuejs/vuepress/commit/141bd11))
<a name="1.0.0-alpha.47"></a>
# [1.0.0-alpha.47](https://github.com/vuejs/vuepress/compare/v1.0.0-alpha.46...v1.0.0-alpha.47) (2019-04-06)
### Bug Fixes
* **$core:** `index.styl` is not injected at the end of the style bundle (close: [#1523](https://github.com/vuejs/vuepress/issues/1523)) ([dabf506](https://github.com/vuejs/vuepress/commit/dabf506))
* **$core:** `routerBase` will always get '/' (close: [#1503](https://github.com/vuejs/vuepress/issues/1503)) ([9fba549](https://github.com/vuejs/vuepress/commit/9fba549))
* **$markdown:** Snippets should allow spaces in file path (closes [#1505](https://github.com/vuejs/vuepress/issues/1505)) ([#1517](https://github.com/vuejs/vuepress/issues/1517)) ([5c307c9](https://github.com/vuejs/vuepress/commit/5c307c9))
### Features
* **$core:** assert return type for functional plugin ([#1516](https://github.com/vuejs/vuepress/issues/1516)) ([74887c5](https://github.com/vuejs/vuepress/commit/74887c5))
* **$core:** emit warning if the source directory doesn't exist (close: [#1521](https://github.com/vuejs/vuepress/issues/1521)) ([6da9a5f](https://github.com/vuejs/vuepress/commit/6da9a5f))
* **$plugin-pwa:** allow using local workbox files (close: [#539](https://github.com/vuejs/vuepress/issues/539)) ([4640614](https://github.com/vuejs/vuepress/commit/4640614))
<a name="1.0.0-alpha.46"></a>
# [1.0.0-alpha.46](https://github.com/vuejs/vuepress/compare/v1.0.0-alpha.45...v1.0.0-alpha.46) (2019-04-01)
### Bug Fixes
* **$core:** regression of introducing dynamic `routerBase` (close: [#1498](https://github.com/vuejs/vuepress/issues/1498)) ([5e12b49](https://github.com/vuejs/vuepress/commit/5e12b49))
<a name="1.0.0-alpha.45"></a>
# [1.0.0-alpha.45](https://github.com/vuejs/vuepress/compare/v1.0.0-alpha.44...v1.0.0-alpha.45) (2019-03-31)
### Bug Fixes
* **$core:** do not register component in render function ([#1449](https://github.com/vuejs/vuepress/issues/1449)) ([ef82c47](https://github.com/vuejs/vuepress/commit/ef82c47))
* **$core:** do not use stylus in outbound link ([d34e038](https://github.com/vuejs/vuepress/commit/d34e038))
* **$core:** should default host be 0.0.0.0 ([699492a](https://github.com/vuejs/vuepress/commit/699492a))
* **$markdown:** treat styl as stylus language ([#1433](https://github.com/vuejs/vuepress/issues/1433)) ([f44e2db](https://github.com/vuejs/vuepress/commit/f44e2db))
* **$markdown-loader:** always use `/` instead of `\` in `relPath` ([#1484](https://github.com/vuejs/vuepress/issues/1484)) ([944ebe4](https://github.com/vuejs/vuepress/commit/944ebe4))
* **$plugin-active-header-links:** side navigation edge case bug ([#1477](https://github.com/vuejs/vuepress/issues/1477)) ([8a11d14](https://github.com/vuejs/vuepress/commit/8a11d14))
* **$plugin-blog:** inconsistent paths of tag and category pages with index page ([#1420](https://github.com/vuejs/vuepress/issues/1420)) ([5c0e62f](https://github.com/vuejs/vuepress/commit/5c0e62f))
* **$plugin-pwa:** fix a typo in `opacity` ([#1444](https://github.com/vuejs/vuepress/issues/1444)) ([c174f0d](https://github.com/vuejs/vuepress/commit/c174f0d))
* **$theme-default:** fix wrong editLink (close: [#1115](https://github.com/vuejs/vuepress/issues/1115), [#1125](https://github.com/vuejs/vuepress/issues/1125)) ([#1419](https://github.com/vuejs/vuepress/issues/1419)) ([3b14375](https://github.com/vuejs/vuepress/commit/3b14375))
* **$theme-default:** nav url change bug (close: [#865](https://github.com/vuejs/vuepress/issues/865)) ([#1475](https://github.com/vuejs/vuepress/issues/1475)) ([521dddd](https://github.com/vuejs/vuepress/commit/521dddd))
### Features
* **$core:** allow dynamic routeBase at runtime ([fc99d59](https://github.com/vuejs/vuepress/commit/fc99d59))
* **$core:** decode page path for better readability ([#1438](https://github.com/vuejs/vuepress/issues/1438)) ([93b2ca1](https://github.com/vuejs/vuepress/commit/93b2ca1))
* **$core:** export version ([#1486](https://github.com/vuejs/vuepress/issues/1486)) ([d7b8daf](https://github.com/vuejs/vuepress/commit/d7b8daf))
* **$core:** functional siteConfig.evergreen ([#1489](https://github.com/vuejs/vuepress/issues/1489)) ([19e0569](https://github.com/vuejs/vuepress/commit/19e0569))
* **$core:** support array as plugin options ([#1493](https://github.com/vuejs/vuepress/issues/1493)) ([9e07b1e](https://github.com/vuejs/vuepress/commit/9e07b1e))
* **$markdown:** markdown plugin (close: [#585](https://github.com/vuejs/vuepress/issues/585)) ([#1422](https://github.com/vuejs/vuepress/issues/1422)) ([9734a58](https://github.com/vuejs/vuepress/commit/9734a58))
* **$plugin-register-components:** custom name registration (close: [#656](https://github.com/vuejs/vuepress/issues/656)) ([#1418](https://github.com/vuejs/vuepress/issues/1418)) ([9c6a00b](https://github.com/vuejs/vuepress/commit/9c6a00b))
### Breaking Changes
* Deprecated [@vuepress/plugin-container](https://www.npmjs.com/package/@vuepress/plugin-container) and moved it to [vuepress-plugin-container](https://www.npmjs.com/package/vuepress-plugin-container).
* Deprecated [@vuepress/plugin-clean-urls](https://www.npmjs.com/package/@vuepress/plugin-clean-urls) and moved it to [vuepress-plugin-clean-urls](https://www.npmjs.com/package/vuepress-plugin-clean-urls).
<a name="1.0.0-alpha.44"></a>
# [1.0.0-alpha.44](https://github.com/vuejs/vuepress/compare/v1.0.0-alpha.43...v1.0.0-alpha.44) (2019-03-10)
### Features
* **$core:** optional `callback` when socket connection is ready under dev. ([547e4f9](https://github.com/vuejs/vuepress/commit/547e4f9))
* **$core:** return current app instance in node api ([1c2a6b2](https://github.com/vuejs/vuepress/commit/1c2a6b2))
<a name="1.0.0-alpha.43"></a>
# [1.0.0-alpha.43](https://github.com/vuejs/vuepress/compare/v1.0.0-alpha.42...v1.0.0-alpha.43) (2019-03-09)
### Bug Fixes
* **$core:** cannot render another page with pageKey (close: [#1173](https://github.com/vuejs/vuepress/issues/1173)) ([9d01514](https://github.com/vuejs/vuepress/commit/9d01514))
* **$core:** global components cannot be used as layouts (close: [#1321](https://github.com/vuejs/vuepress/issues/1321)) ([0306574](https://github.com/vuejs/vuepress/commit/0306574))
* **$core:** PascalCase layouts cannot be used with camelCase nor hyphen-delimited (close: [#1391](https://github.com/vuejs/vuepress/issues/1391)) ([3e91eba](https://github.com/vuejs/vuepress/commit/3e91eba))
* **$plugin-blog:** read `layoutComponents` from themeAPI ([#1396](https://github.com/vuejs/vuepress/issues/1396)) ([5bf4d24](https://github.com/vuejs/vuepress/commit/5bf4d24))
### Features
* **$core:** refine node api ([#1395](https://github.com/vuejs/vuepress/issues/1395)) ([e5d8ed4](https://github.com/vuejs/vuepress/commit/e5d8ed4))
* **$cli:** `--no-clear-screen` flag (close: [#1421](https://github.com/vuejs/vuepress/issues/1421)) ([e5f51de](https://github.com/vuejs/vuepress/commit/e5f51de))
<a name="1.0.0-alpha.42"></a>
# [1.0.0-alpha.42](https://github.com/vuejs/vuepress/compare/v1.0.0-alpha.41...v1.0.0-alpha.42) (2019-03-03)
### Bug Fixes
* **$theme-default:** support slot and v-pre container (close: [#1387](https://github.com/vuejs/vuepress/issues/1387)) ([#1389](https://github.com/vuejs/vuepress/issues/1389)) ([c85f62d](https://github.com/vuejs/vuepress/commit/c85f62d))
<a name="1.0.0-alpha.41"></a>
# [1.0.0-alpha.41](https://github.com/vuejs/vuepress/compare/v1.0.0-alpha.40...v1.0.0-alpha.41) (2019-03-02)
### Bug Fixes
* **$core:** Generated js have SyntaxError when source-map is enabled (close: [#1367](https://github.com/vuejs/vuepress/issues/1367)) ([#1378](https://github.com/vuejs/vuepress/issues/1378)) ([b53324d](https://github.com/vuejs/vuepress/commit/b53324d))
* **$core:** skip plugin on error (Related to [#1371](https://github.com/vuejs/vuepress/issues/1371)) [#1383](https://github.com/vuejs/vuepress/issues/1383) ([7d2c065](https://github.com/vuejs/vuepress/commit/7d2c065))
* **$core:** support theme index file which is not at root (close: [#1362](https://github.com/vuejs/vuepress/issues/1362)) ([#1376](https://github.com/vuejs/vuepress/issues/1376)) ([204cbe4](https://github.com/vuejs/vuepress/commit/204cbe4))
* **$markdown:** fix line highlighting (close: [#1364](https://github.com/vuejs/vuepress/issues/1364)) ([#1369](https://github.com/vuejs/vuepress/issues/1369)) ([5a111a2](https://github.com/vuejs/vuepress/commit/5a111a2))
* **$theme-default:** encodeURI for sidebar items which contain CJK characters (close: [#717](https://github.com/vuejs/vuepress/issues/717)) ([285b368](https://github.com/vuejs/vuepress/commit/285b368))
### Features
* **$plugin-container:** init ([#1381](https://github.com/vuejs/vuepress/issues/1381)) ([ad0ff72](https://github.com/vuejs/vuepress/commit/ad0ff72))
* **$markdown:** TOC component (close: [#1275](https://github.com/vuejs/vuepress/issues/1275)) ([#1375](https://github.com/vuejs/vuepress/issues/1375)) ([760f90b](https://github.com/vuejs/vuepress/commit/760f90b))
* **$plugin-nprogress:** allow nprogress use different color and add docs ([#1366](https://github.com/vuejs/vuepress/issues/1366)) ([1a928c7](https://github.com/vuejs/vuepress/commit/1a928c7))
@@ -120,7 +401,7 @@
### Features
* **$core:** Support extra watching files ([02cc268](https://github.com/vuejs/vuepress/commit/02cc268)),
* **$core:** Support extra watching files ([02cc268](https://github.com/vuejs/vuepress/commit/02cc268)),
<br>e.g.
```js
// .vuepress/config.js
@@ -142,7 +423,7 @@
* **$core:** Leverage `webpack-dev-server` and sunset `webpack-serve` ([#1195](https://github.com/vuejs/vuepress/issues/1195)) ([81e3ef6](https://github.com/vuejs/vuepress/commit/81e3ef6))
- Add new plugin option api [beforeDevServer](https://v1.vuepress.vuejs.org/plugin/option-api.html#beforedevserver) and [afterDevServer](https://v1.vuepress.vuejs.org/plugin/option-api.html#afterdevserver).
- Remove `enhanceDevServer`.
- Remove `enhanceDevServer`.
- Publish [vuepress-plugin-export](https://github.com/ulivz/vuepress-plugin-export).
* **$core:** Allow a theme package using a sub directory (close [#1204](https://github.com/vuejs/vuepress/issues/1204)) ([#1206](https://github.com/vuejs/vuepress/issues/1206)) ([febe3a7](https://github.com/vuejs/vuepress/commit/febe3a7))
* **$theme-default:** Support shortcut for `sh` and `yml`. (close: [#1221](https://github.com/vuejs/vuepress/issues/1221)) ([fc5dba8](https://github.com/vuejs/vuepress/commit/fc5dba8))
@@ -201,7 +482,7 @@
### Breaking Changes
* **$core:** remove `contentLoading` and refine scroll behavior ([#1117](https://github.com/vuejs/vuepress/issues/1117)) ([0a7d85b](https://github.com/vuejs/vuepress/commit/0a7d85b))
- It also fixes the exisitng issues about anchor links. (Refs: [#1113](https://github.com/vuejs/vuepress/issues/1113), [#1016](https://github.com/vuejs/vuepress/issues/1016), [#1011](https://github.com/vuejs/vuepress/issues/1011), [#895](https://github.com/vuejs/vuepress/issues/895))
- It also fixes the existing issues about anchor links. (Refs: [#1113](https://github.com/vuejs/vuepress/issues/1113), [#1016](https://github.com/vuejs/vuepress/issues/1016), [#1011](https://github.com/vuejs/vuepress/issues/1011), [#895](https://github.com/vuejs/vuepress/issues/895))
### Performance Improvements
@@ -918,8 +1199,8 @@
* $page is missing at 404 page ([#388](https://github.com/vuejs/vuepress/issues/388)) ([cefc8c3](https://github.com/vuejs/vuepress/commit/cefc8c3))
* avoid the searchbox exceeded out of screen in narrow screen ([#254](https://github.com/vuejs/vuepress/issues/254)) ([8f04081](https://github.com/vuejs/vuepress/commit/8f04081))
* code looks not good at small sreen (close: [#350](https://github.com/vuejs/vuepress/issues/350)) ([6514c8f](https://github.com/vuejs/vuepress/commit/6514c8f))
* code looks not good at small sreen (close: [#350](https://github.com/vuejs/vuepress/issues/350)) ([d0ef06f](https://github.com/vuejs/vuepress/commit/d0ef06f))
* code looks not good at small screen (close: [#350](https://github.com/vuejs/vuepress/issues/350)) ([6514c8f](https://github.com/vuejs/vuepress/commit/6514c8f))
* code looks not good at small screen (close: [#350](https://github.com/vuejs/vuepress/issues/350)) ([d0ef06f](https://github.com/vuejs/vuepress/commit/d0ef06f))
* dropdown overlap due to word wrapping (close: [#359](https://github.com/vuejs/vuepress/issues/359)) ([#360](https://github.com/vuejs/vuepress/issues/360)) ([c65a8b7](https://github.com/vuejs/vuepress/commit/c65a8b7))
* duplicate slash when docs dir is not set ([#361](https://github.com/vuejs/vuepress/issues/361)) ([0c59ed5](https://github.com/vuejs/vuepress/commit/0c59ed5))
* emoji doesn't work in toc (close: [#417](https://github.com/vuejs/vuepress/issues/417)) ([#418](https://github.com/vuejs/vuepress/issues/418)) ([1b9012e](https://github.com/vuejs/vuepress/commit/1b9012e))
@@ -1032,7 +1313,7 @@
* fix emoji not showing on sidebars ([#206](https://github.com/vuejs/vuepress/issues/206)) ([bc2c83a](https://github.com/vuejs/vuepress/commit/bc2c83a))
* fix Sidebar link active logic ([#215](https://github.com/vuejs/vuepress/issues/215)) ([9c93d8f](https://github.com/vuejs/vuepress/commit/9c93d8f))
* Fix the style of repo link. ([f55fa00](https://github.com/vuejs/vuepress/commit/f55fa00))
* fix title inferrence regression (close [#208](https://github.com/vuejs/vuepress/issues/208)) ([52c20cf](https://github.com/vuejs/vuepress/commit/52c20cf))
* fix title inference regression (close [#208](https://github.com/vuejs/vuepress/issues/208)) ([52c20cf](https://github.com/vuejs/vuepress/commit/52c20cf))
* renames index.js to enhanceApp.js ([#226](https://github.com/vuejs/vuepress/issues/226)) ([0170449](https://github.com/vuejs/vuepress/commit/0170449))
* siteTitle vs pageTitle ([cd9b788](https://github.com/vuejs/vuepress/commit/cd9b788))
@@ -1048,7 +1329,7 @@
* support excerpt extraction with `<!-- more -->` (close [#174](https://github.com/vuejs/vuepress/issues/174)) ([fa404dc](https://github.com/vuejs/vuepress/commit/fa404dc))
* support for TOML front matter ([#141](https://github.com/vuejs/vuepress/issues/141)) ([#164](https://github.com/vuejs/vuepress/issues/164)) ([70620ba](https://github.com/vuejs/vuepress/commit/70620ba))
* support toml config ([#138](https://github.com/vuejs/vuepress/issues/138)) ([d136e22](https://github.com/vuejs/vuepress/commit/d136e22))
* theme index enhancment support ([#154](https://github.com/vuejs/vuepress/issues/154)) ([d026801](https://github.com/vuejs/vuepress/commit/d026801))
* theme index enhancement support ([#154](https://github.com/vuejs/vuepress/issues/154)) ([d026801](https://github.com/vuejs/vuepress/commit/d026801))
@@ -1094,7 +1375,7 @@
* handle headers that start with numbers (fix [#121](https://github.com/vuejs/vuepress/issues/121)) ([ad83169](https://github.com/vuejs/vuepress/commit/ad83169))
* make search locale-scoped (close [#128](https://github.com/vuejs/vuepress/issues/128)) ([846eb59](https://github.com/vuejs/vuepress/commit/846eb59))
* **nav:** unepxected error when themeConfig.nav isn't given. (close: [#125](https://github.com/vuejs/vuepress/issues/125)) ([#127](https://github.com/vuejs/vuepress/issues/127)) ([f052472](https://github.com/vuejs/vuepress/commit/f052472))
* **nav:** unexpected error when themeConfig.nav isn't given. (close: [#125](https://github.com/vuejs/vuepress/issues/125)) ([#127](https://github.com/vuejs/vuepress/issues/127)) ([f052472](https://github.com/vuejs/vuepress/commit/f052472))
* service worker path ([51c6eb2](https://github.com/vuejs/vuepress/commit/51c6eb2))
* use correct host in tip after the server has started ([#130](https://github.com/vuejs/vuepress/issues/130)) ([fd447ae](https://github.com/vuejs/vuepress/commit/fd447ae))
* use header's slug as it is if possible ([#119](https://github.com/vuejs/vuepress/issues/119)) ([5f7e199](https://github.com/vuejs/vuepress/commit/5f7e199))
@@ -1175,7 +1456,7 @@
### Bug Fixes
* always transpile lib directory ([#73](https://github.com/vuejs/vuepress/issues/73)) ([56e0392](https://github.com/vuejs/vuepress/commit/56e0392))
* avoid html-webpack-plugin requiring incomaptible webpack internals ([4816bef](https://github.com/vuejs/vuepress/commit/4816bef))
* avoid html-webpack-plugin requiring incompatible webpack internals ([4816bef](https://github.com/vuejs/vuepress/commit/4816bef))
* prioritize own deps + avoid serving wrong index.html (fix [#69](https://github.com/vuejs/vuepress/issues/69)) ([781e37a](https://github.com/vuejs/vuepress/commit/781e37a))
* redirect */index.html to */ (close [#83](https://github.com/vuejs/vuepress/issues/83)) ([52e04c4](https://github.com/vuejs/vuepress/commit/52e04c4))
* remove override import when ejecting (close [#56](https://github.com/vuejs/vuepress/issues/56)) ([2d811ed](https://github.com/vuejs/vuepress/commit/2d811ed))
+76
View File
@@ -0,0 +1,76 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at vuepresscore@gmail.com. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq
+85 -13
View File
@@ -5,43 +5,115 @@
</p>
<p align="center">
<a href="https://npmcharts.com/compare/vuepress?minimal=true"><img src="https://img.shields.io/npm/dm/vuepress.svg" alt="Downloads"></a>
<a href="https://npmcharts.com/compare/vuepress?minimal=true"><a href="https://opencollective.com/vuepress" alt="Financial Contributors on Open Collective"><img src="https://opencollective.com/vuepress/all/badge.svg?label=financial+contributors" /></a> <img src="https://img.shields.io/npm/dm/vuepress.svg" alt="Downloads"></a>
<a href="https://www.npmjs.com/package/vuepress"><img src="https://img.shields.io/npm/v/vuepress.svg" alt="Version"></a>
<a href="https://www.npmjs.com/package/vuepress"><img src="https://badgen.net/npm/v/vuepress/next" alt="npm next version"></a>
<a href="https://www.npmjs.com/package/vuepress"><img src="https://img.shields.io/npm/l/vuepress.svg" alt="License"></a>
<a href="https://github.com/vuejs/vuepress/blob/master/LICENSE"><img src="https://img.shields.io/npm/l/vuepress.svg" alt="License"></a>
<a href="https://discordapp.com/invite/HBherRA"><img src="https://img.shields.io/badge/Discord-join%20chat-738bd7.svg" alt="vuepress channel on Discord"></a>
</p>
> This is the branch for `VuePress 1.x` and docs are available at https://v1.vuepress.vuejs.org.
<h2 align="center">Supporting VuePress</h2>
## Status: alpha
Maintaining and developing new features to VuePress takes a considerable amount of time (if you know that Gastby's team is a company), and I am currently exploring the possibility of working on VuePress fulltime.
Certain combinations of plugins may not work properly, and things may change or break until we reach `beta` phase. Do not use in production yet unless you are adventurous.
- [Sponsor ULIVZ on Patreon](https://www.patreon.com/ulivz)
- [Sponsor Vue.js](https://vuejs.org/support-vuejs)
For 0.x, it's moved to [0.x branch](https://github.com/vuejs/vuepress/tree/0.x) and still maintained, the website is: https://vuepress.vuejs.org.
> "VuePress is much more than that." —— ULIVZ
## Install
```bash
yarn add vuepress -D # Install 0.x
yarn add vuepress@next -D # Install 1.x.
yarn add vuepress -D
```
## Showcase
Check out [Awesome Vuepress](https://github.com/ulivz/awesome-vuepress) to find awesome things related to VuePress 1.x .
- [Awesome Vuepress](https://github.com/ulivz/awesome-vuepress)
- [vuepress.gallery](https://vuepress.gallery) (by [@vicbergquist](https://twitter.com/vicbergquist))
## Development
## Documentation
> Please make sure your version of Node.js is greater than 8.
This is the branch for `VuePress 1.x` and docs are available at https://v1.vuepress.vuejs.org.
``` bash
## Contribution
Want to contribute? Check our [issues for beginners](https://github.com/vuejs/vuepress/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)!
```bash
yarn bootstrap # Install and link dependencies for this lerna repo
yarn dev # serves VuePress' own docs with itself
yarn test # make sure your code change pass the test
```
If you don't have a local checkout, you can also open [VuePress in Gitpod](https://gitpod.io/#https://github.com/vuejs/vuepress/blob/master/packages/docs/docs/README.md), a free online IDE for GitHub.
If you intend to make `"substantial"` changes to VuePress or its documentation, please checkout [VuePress RFCs](./rfcs/README.md).
If you have a VuePress-related project/component/tool, add it with a pull request to [this curated list](https://github.com/ulivz/awesome-vuepress)!
## Contributors
### Core Team
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
<!-- prettier-ignore-start -->
<!-- markdownlint-disable -->
<table>
<tr>
<td align="center"><a href="http://evanyou.me"><img src="https://avatars1.githubusercontent.com/u/499550?v=4" width="100px;" alt="Evan You"/><br /><sub><b>Evan You</b></sub></a><br /><a href="https://github.com/vuejs/vuepress/commits?author=yyx990803" title="Code">💻</a></td>
<td align="center"><a href="http://ulivz.com"><img src="https://avatars1.githubusercontent.com/u/23133919?v=4" width="100px;" alt="ULIVZ"/><br /><sub><b>ULIVZ</b></sub></a><br /><a href="https://github.com/vuejs/vuepress/commits?author=ulivz" title="Code">💻</a> <a href="https://github.com/vuejs/vuepress/commits?author=ulivz" title="Documentation">📖</a></td>
<td align="center"><a href="http://www.jenlooper.com"><img src="https://avatars2.githubusercontent.com/u/1450004?v=4" width="100px;" alt="Jen Looper"/><br /><sub><b>Jen Looper</b></sub></a><br /><a href="https://github.com/vuejs/vuepress/commits?author=jlooper" title="Documentation">📖</a> <a href="#design-jlooper" title="Design">🎨</a></td>
<td align="center"><a href="https://frontstuff.io/"><img src="https://avatars0.githubusercontent.com/u/5370675?v=4" width="100px;" alt="Sarah Dayan"/><br /><sub><b>Sarah Dayan</b></sub></a><br /><a href="https://github.com/vuejs/vuepress/commits?author=sarahdayan" title="Code">💻</a> <a href="https://github.com/vuejs/vuepress/commits?author=sarahdayan" title="Documentation">📖</a></td>
<td align="center"><a href="https://www.franck-abgrall.me/"><img src="https://avatars3.githubusercontent.com/u/9840435?v=4" width="100px;" alt="Franck Abgrall"/><br /><sub><b>Franck Abgrall</b></sub></a><br /><a href="https://github.com/vuejs/vuepress/commits?author=kefranabg" title="Code">💻</a> <a href="#question-kefranabg" title="Answering Questions">💬</a></td>
<td align="center"><a href="https://sobolevn.me"><img src="https://avatars1.githubusercontent.com/u/4660275?v=4" width="100px;" alt="Nikita Sobolev"/><br /><sub><b>Nikita Sobolev</b></sub></a><br /><a href="https://github.com/vuejs/vuepress/commits?author=sobolevn" title="Code">💻</a> <a href="https://github.com/vuejs/vuepress/commits?author=sobolevn" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/f3ltron"><img src="https://avatars1.githubusercontent.com/u/11556276?v=4" width="100px;" alt="Giraud Florent"/><br /><sub><b>Giraud Florent</b></sub></a><br /><a href="https://github.com/vuejs/vuepress/commits?author=f3ltron" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://twitter.com/CodesOfRa"><img src="https://avatars0.githubusercontent.com/u/945186?v=4" width="100px;" alt="Ramona"/><br /><sub><b>Ramona</b></sub></a><br /><a href="https://github.com/vuejs/vuepress/commits?author=CodesOfRa" title="Code">💻</a></td>
<td align="center"><a href="https://fatihacet.com"><img src="https://avatars3.githubusercontent.com/u/712419?v=4" width="100px;" alt="Fatih Acet"/><br /><sub><b>Fatih Acet</b></sub></a><br /><a href="https://github.com/vuejs/vuepress/commits?author=fatihacet" title="Code">💻</a></td>
<td align="center"><a href="https://rakowski.dev"><img src="https://avatars2.githubusercontent.com/u/15185752?v=4" width="100px;" alt="Filip Rakowski"/><br /><sub><b>Filip Rakowski</b></sub></a><br /><a href="https://github.com/vuejs/vuepress/commits?author=filrak" title="Code">💻</a></td>
<td align="center"><a href="http://farcaller.net/"><img src="https://avatars2.githubusercontent.com/u/693?v=4" width="100px;" alt="Vladimir Pouzanov"/><br /><sub><b>Vladimir Pouzanov</b></sub></a><br /><a href="https://github.com/vuejs/vuepress/commits?author=farcaller" title="Code">💻</a></td>
<td align="center"><a href="https://twitter.com/vicbergquist"><img src="https://avatars0.githubusercontent.com/u/25737281?v=4" width="100px;" alt="Victoria Bergquist"/><br /><sub><b>Victoria Bergquist</b></sub></a><br /><a href="https://github.com/vuejs/vuepress/commits?author=vicbergquist" title="Code">💻</a> <a href="#design-vicbergquist" title="Design">🎨</a></td>
<td align="center"><a href="https://billychin.netlify.com/"><img src="https://avatars0.githubusercontent.com/u/38957202?v=4" width="100px;" alt="Billyyyyy3320"/><br /><sub><b>Billyyyyy3320</b></sub></a><br /><a href="https://github.com/vuejs/vuepress/commits?author=newsbielt703" title="Code">💻</a></td>
<td align="center"><a href="http://www.bencodezen.io"><img src="https://avatars0.githubusercontent.com/u/4836334?v=4" width="100px;" alt="Ben Hong"/><br /><sub><b>Ben Hong</b></sub></a><br /><a href="https://github.com/vuejs/vuepress/commits?author=bencodezen" title="Code">💻</a> <a href="#blog-bencodezen" title="Blogposts">📝</a></td>
</tr>
<tr>
<td align="center"><a href="http://www.github.com/vinayakkulkarni"><img src="https://avatars2.githubusercontent.com/u/19776877?v=4" width="100px;" alt="Vinayak Kulkarni"/><br /><sub><b>Vinayak Kulkarni</b></sub></a><br /><a href="https://github.com/Ulivz/VuePress/commits?author=vinayakkulkarni" title="Code">💻</a></td>
</tr>
</table>
<!-- markdownlint-enable -->
<!-- prettier-ignore-end -->
<!-- ALL-CONTRIBUTORS-LIST:END -->
### Code Contributors
This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)].
<a href="https://github.com/vuejs/vuepress/graphs/contributors"><img src="https://opencollective.com/vuepress/contributors.svg?width=890&button=false" /></a>
### Financial Contributors
Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/vuepress/contribute)]
#### Individuals
<a href="https://opencollective.com/vuepress"><img src="https://opencollective.com/vuepress/individuals.svg?width=890"></a>
#### Organizations
Support this project with your organization. Your logo will show up here with a link to your website. [[Contribute](https://opencollective.com/vuepress/contribute)]
<a href="https://opencollective.com/vuepress/organization/0/website"><img src="https://opencollective.com/vuepress/organization/0/avatar.svg"></a>
<a href="https://opencollective.com/vuepress/organization/1/website"><img src="https://opencollective.com/vuepress/organization/1/avatar.svg"></a>
<a href="https://opencollective.com/vuepress/organization/2/website"><img src="https://opencollective.com/vuepress/organization/2/avatar.svg"></a>
<a href="https://opencollective.com/vuepress/organization/3/website"><img src="https://opencollective.com/vuepress/organization/3/avatar.svg"></a>
<a href="https://opencollective.com/vuepress/organization/4/website"><img src="https://opencollective.com/vuepress/organization/4/avatar.svg"></a>
<a href="https://opencollective.com/vuepress/organization/5/website"><img src="https://opencollective.com/vuepress/organization/5/avatar.svg"></a>
<a href="https://opencollective.com/vuepress/organization/6/website"><img src="https://opencollective.com/vuepress/organization/6/avatar.svg"></a>
<a href="https://opencollective.com/vuepress/organization/7/website"><img src="https://opencollective.com/vuepress/organization/7/avatar.svg"></a>
<a href="https://opencollective.com/vuepress/organization/8/website"><img src="https://opencollective.com/vuepress/organization/8/avatar.svg"></a>
<a href="https://opencollective.com/vuepress/organization/9/website"><img src="https://opencollective.com/vuepress/organization/9/avatar.svg"></a>
## License
[MIT](https://github.com/vuejs/vuepress/blob/master/LICENSE)
@@ -0,0 +1,2 @@
module.exports = {}
// This file is just for jest.
@@ -0,0 +1,4 @@
{
"name": "vuepress-theme-without-index",
"main": "Layout.vue"
}
+1359
View File
File diff suppressed because one or more lines are too long
+46
View File
@@ -0,0 +1,46 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M2.73587 7.90886L19.3984 5.43536C19.8439 5.36933 20.3115 5.63888 20.4443 6.03926L27.3378 26.8118C27.5068 27.3216 27.2442 27.8237 26.7485 27.9312L8.08892 31.9766C7.53676 32.0964 6.99003 31.7433 6.87004 31.1904L2.01592 8.81459C1.92304 8.38637 2.24605 7.98155 2.73587 7.90886Z" fill="url(#paint0_linear)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M2.8401 7.68541L19.5027 5.21191C19.9481 5.14588 20.4158 5.41543 20.5486 5.81581L27.442 26.5884C27.6113 27.0982 27.3484 27.6003 26.8527 27.7077L8.19315 31.7532C7.64099 31.8729 7.09425 31.5199 6.97426 30.967L2.11991 8.59139C2.02702 8.16292 2.35003 7.75834 2.8401 7.68541Z" fill="url(#paint1_linear)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.4202 5.19932L20.9615 2.77387C21.405 2.70882 21.8749 2.98059 22.0124 3.38196L29.0032 23.7904C29.1712 24.2809 28.9172 24.7624 28.434 24.8636L10.3229 28.6681C9.78926 28.7802 9.24967 28.4422 9.11958 27.9152L3.73179 6.09987C3.62633 5.67313 3.93531 5.27053 4.4202 5.19932Z" fill="url(#paint2_linear)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.52418 4.97612L21.0655 2.55066C21.509 2.48562 21.9789 2.75738 22.1163 3.15875L29.1071 23.5672C29.2752 24.0577 29.0212 24.5392 28.538 24.6404L10.4269 28.4449C9.89324 28.557 9.35364 28.219 9.22355 27.692L3.83601 5.87667C3.73056 5.44968 4.03928 5.04708 4.52418 4.97612Z" fill="url(#paint3_linear)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.19741 2.58045L22.5968 0.23236C23.0371 0.169284 23.5062 0.438834 23.6462 0.83552L30.7136 20.8674C30.8824 21.3459 30.6345 21.8138 30.158 21.9111L12.3295 25.5545C11.805 25.6617 11.2693 25.3318 11.1348 24.8193L5.52698 3.46548C5.41611 3.04489 5.71719 2.64919 6.19741 2.58045Z" fill="url(#paint4_linear)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.30139 2.35699L22.7008 0.00915408C23.1411 -0.0539215 23.6102 0.215628 23.7502 0.612315L30.8176 20.6442C30.9864 21.1227 30.7385 21.5906 30.262 21.6879L12.4335 25.3313C11.909 25.4385 11.3733 25.1086 11.2388 24.5961L5.63096 3.24252C5.52033 2.82168 5.82117 2.42574 6.30139 2.35699Z" fill="url(#paint5_linear)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.1566 15.5455L22.3814 14.2066C22.4842 14.1876 22.585 14.2482 22.6163 14.3478L23.829 18.2018C23.8659 18.3193 23.7925 18.443 23.6713 18.4667L16.3429 19.8935C16.2372 19.9139 16.1338 19.8501 16.1044 19.7466L14.9952 15.8044C14.9624 15.6876 15.0371 15.5676 15.1566 15.5455ZM14.5327 15.8884L15.6242 19.8627C15.709 20.1709 16.0167 20.3619 16.3304 20.301L23.7469 18.8592C24.1064 18.7892 24.3242 18.4213 24.2126 18.0727L22.9883 14.2453C22.8949 13.9535 22.6005 13.7759 22.2989 13.8296L15.0149 15.1246C14.6611 15.1877 14.4374 15.5415 14.5327 15.8884ZM15.9739 16.2122L16.7374 18.8927L17.5579 18.7059L17.1193 17.1625L18.2394 18.0397L18.7844 16.805L19.2311 18.3728L20.0272 18.2023L19.2555 15.6113L18.4106 15.7574L17.8989 16.8944L16.7941 16.0577L15.9739 16.2122ZM21.9185 16.5727L21.5095 15.1978L20.6594 15.3432L21.0906 16.7304L20.3268 16.8782L21.8963 17.9253L22.6921 16.4224L21.9185 16.5727Z" fill="#BCC0CF"/>
<path d="M23.8107 18.3664L23.7245 18.5462L23.6974 18.6029C23.6678 18.6473 23.621 18.6793 23.5668 18.6892L16.2392 20.1182C16.1333 20.1379 16.0298 20.0739 16.0002 19.9704L14.8915 16.0282C14.8791 15.9813 14.8816 15.9321 14.9013 15.8902C14.9038 15.8852 14.9062 15.8803 14.9087 15.8754L14.9161 15.8606L14.9999 15.6807C14.9851 15.7177 14.9826 15.7621 14.9949 15.8039L15.0245 15.9099L16.1037 19.7462C16.1333 19.8496 16.2367 19.9137 16.3427 19.894L23.6678 18.4674H23.6703C23.7344 18.4551 23.7836 18.4157 23.8107 18.3664ZM24.1187 18.6226C24.1458 18.5191 24.1434 18.4058 24.1089 18.2949L22.8843 14.4685C22.7907 14.1778 22.4975 14.0004 22.1944 14.0521L14.9112 15.3481C14.751 15.3752 14.6155 15.4639 14.5293 15.5871C14.5785 15.358 14.7633 15.1683 15.0146 15.1239L22.3004 13.8304C22.601 13.7762 22.8966 13.9536 22.9878 14.2443L24.2124 18.0732C24.2764 18.2678 24.2345 18.4723 24.1187 18.6226ZM22.6921 16.4224L22.5714 16.6491L21.815 16.7969L21.406 15.422L20.721 15.5403L20.6594 15.3432L21.5095 15.1978L21.9185 16.5727L22.6921 16.4224ZM20.928 16.7624L21.0906 16.7304L20.9871 16.9546L20.5633 17.0359L20.3268 16.8782L20.928 16.7624ZM17.8949 16.893L17.7939 17.1172L16.6901 16.2819L16.0273 16.4051L15.9731 16.2129L16.7936 16.0577L17.8949 16.893ZM20.0262 18.2013L19.866 18.2358L19.1515 15.8335L18.3089 15.9813L18.4099 15.7571L19.255 15.6118L20.0262 18.2013ZM17.2297 17.5533L17.1188 17.1615L17.0153 17.3858L17.4022 18.7409L17.5574 18.7064L17.2297 17.5533Z" fill="#A2A6B3"/>
<path d="M12.8933 5.37674L10.7645 5.67241L18.3335 12.7093L21.0537 4.07581L19.0431 4.43061L17.4465 9.51608L12.8933 5.37674Z" fill="url(#paint6_linear)"/>
<path d="M12.8932 5.37674L14.8447 5.08107L16.5595 6.61854L17.21 4.72627L19.0431 4.4306L17.4465 9.51607L12.8932 5.37674Z" fill="url(#paint7_linear)"/>
<defs>
<linearGradient id="paint0_linear" x1="14.6945" y1="31.9997" x2="14.6945" y2="5.42521" gradientUnits="userSpaceOnUse">
<stop stop-color="#1D2130"/>
<stop offset="1" stop-color="#3E445A"/>
</linearGradient>
<linearGradient id="paint1_linear" x1="14.7986" y1="31.7764" x2="14.7986" y2="5.20194" gradientUnits="userSpaceOnUse">
<stop stop-color="#262B3F"/>
<stop offset="1" stop-color="#656E91"/>
</linearGradient>
<linearGradient id="paint2_linear" x1="16.383" y1="28.6898" x2="16.383" y2="2.76414" gradientUnits="userSpaceOnUse">
<stop stop-color="#267550"/>
<stop offset="1" stop-color="#79B881"/>
</linearGradient>
<linearGradient id="paint3_linear" x1="16.4871" y1="28.4665" x2="16.4871" y2="2.54086" gradientUnits="userSpaceOnUse">
<stop stop-color="#279264"/>
<stop offset="1" stop-color="#A4F3AA"/>
</linearGradient>
<linearGradient id="paint4_linear" x1="18.1351" y1="25.575" x2="18.1351" y2="0.223269" gradientUnits="userSpaceOnUse">
<stop offset="0.59" stop-color="#CECFD0"/>
<stop offset="1" stop-color="#DFDFDF"/>
</linearGradient>
<linearGradient id="paint5_linear" x1="18.2392" y1="25.3517" x2="18.2392" y2="-1.20886e-05" gradientUnits="userSpaceOnUse">
<stop stop-color="#EAEEF0"/>
<stop offset="0.41" stop-color="white"/>
</linearGradient>
<linearGradient id="paint6_linear" x1="13.2661" y1="-3.77058" x2="23.9637" y2="31.2197" gradientUnits="userSpaceOnUse">
<stop offset="0.22" stop-color="#73CB8D"/>
<stop offset="0.4" stop-color="#2F9869"/>
</linearGradient>
<linearGradient id="paint7_linear" x1="11.8979" y1="-6.91736" x2="28.6463" y2="41.7235" gradientUnits="userSpaceOnUse">
<stop offset="0.25" stop-color="#586080"/>
<stop offset="0.35" stop-color="#2C3247"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 6.5 KiB

+3
View File
@@ -3,6 +3,9 @@ module.exports = {
'test': {
'presets': [
['@babel/preset-env', { 'targets': { 'node': 'current' }}]
],
'plugins': [
'@babel/plugin-syntax-dynamic-import'
]
}
}
+1 -2
View File
@@ -1,6 +1,5 @@
{
"lerna": "2.5.1",
"npmClient": "yarn",
"useWorkspaces": true,
"version": "1.0.0-alpha.40"
"version": "1.0.4"
}
+29 -30
View File
@@ -3,51 +3,50 @@
"workspaces": [
"packages/@vuepress/*",
"packages/vuepress",
"packages/docs",
"packages/blog"
"packages/docs"
],
"description": "Minimalistic doc generator with Vue component based layout system",
"scripts": {
"bootstrap": "lerna bootstrap && yarn tsc",
"precommit": "lint-staged",
"bootstrap": "yarn && yarn tsc",
"clean": "lerna clean && rm -rf node_modules",
"packages:list": "lerna ls -l",
"packages:diff": "lerna diff",
"packages:changed": "lerna changed",
"boot": "node scripts/bootstrap.js",
"dev": "yarn tsc && yarn workspace docs dev",
"build": "yarn tsc && yarn workspace docs build",
"remote-version": "node scripts/remote-version.js",
"dev": "yarn tsc && yarn dev:docs",
"dev:docs": "yarn workspace docs dev",
"build": "yarn tsc && yarn build:docs",
"build:docs": "yarn workspace docs build",
"view-info": "yarn tsc && yarn workspace docs view-info",
"show-help": "yarn workspace docs show-help",
"dev:blog": "yarn tsc && yarn workspace blog dev",
"build:blog": "yarn tsc && yarn workspace blog build",
"register-vuepress": "lerna exec --scope vuepress -- yarn link",
"unregister-vuepress": "lerna exec --scope vuepress -- yarn unlink",
"lint": "eslint packages --fix --ext .js,.vue",
"release": "yarn --pure-lockfile && yarn tsc && node scripts/release.js",
"release": "yarn --pure-lockfile && yarn tsc && node scripts/release.js",
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 2",
"test": "node scripts/test.js",
"tsc": "lerna run tsc"
},
"gitHooks": {
"pre-commit": "lint-staged"
},
"lint-staged": {
"*.{js,vue}": [
"eslint --fix",
"git add"
]
"tsc": "yarn workspace @vuepress/shared-utils tsc"
},
"devDependencies": {
"conventional-changelog-cli": "^1.3.22",
"@types/escape-html": "^0.0.20",
"@types/fs-extra": "^5.0.4",
"@types/globby": "^9.1.0",
"@types/hash-sum": "^1.0.0",
"@types/lru-cache": "^4.1.1",
"@types/node": "^10.12.12",
"@types/semver": "^6.0.0",
"conventional-changelog-cli": "^2.0.21",
"eslint": "^4.19.1",
"eslint-plugin-jest": "^21.15.1",
"eslint-plugin-vue-libs": "^3.0.0",
"lerna": "^3.6.0",
"lint-staged": "^7.0.4",
"minimist": "^1.2.0",
"yorkie": "^1.0.3",
"husky": "^3.0.2",
"inquirer": "^6.2.0",
"lerna": "3.13.4",
"lint-staged": "^8.1.5",
"minimist": "^1.2.0",
"typescript": "^3.2.2",
"@types/node": "^10.12.12",
"@types/lru-cache": "^4.1.1",
"@types/fs-extra": "^5.0.4",
"@types/semver": "^5.5.0",
"@types/hash-sum": "^1.0.0",
"@types/globby": "^8.0.0",
"@types/escape-html": "^0.0.20"
"all-contributors-cli": "^6.8.1"
}
}
@@ -1,18 +0,0 @@
import { isPageExists } from '../util'
export default {
props: {
pageKey: String,
slotKey: {
type: String,
default: 'default'
}
},
render (h) {
const pageKey = this.pageKey || this.$parent.$page.key
if (isPageExists(pageKey)) {
return h(pageKey)
}
return h('')
}
}
@@ -1,19 +0,0 @@
<template>
<component :is="layout"/>
</template>
<script>
export default {
computed: {
layout () {
if (this.$page.path) {
if (this.$vuepress.isLayoutExists(this.$page.frontmatter.layout)) {
return this.$page.frontmatter.layout
}
return 'Layout'
}
return 'NotFound'
}
}
}
</script>
-191
View File
@@ -1,191 +0,0 @@
'use strict'
module.exports = async function build (sourceDir, cliOptions = {}) {
process.env.NODE_ENV = 'production'
const webpack = require('webpack')
const readline = require('readline')
const escape = require('escape-html')
const { chalk, fs, path, logger, env, performance } = require('@vuepress/shared-utils')
const prepare = require('./prepare/index')
const createClientConfig = require('./webpack/createClientConfig')
const createServerConfig = require('./webpack/createServerConfig')
const { createBundleRenderer } = require('vue-server-renderer')
const { normalizeHeadTag, applyUserWebpackConfig } = require('./util/index')
logger.wait('Extracting site metadata...')
const ctx = await prepare(sourceDir, cliOptions, true /* isProd */)
const { outDir, cwd } = ctx
if (cwd === outDir) {
return console.error(logger.error(chalk.red('Unexpected option: outDir cannot be set to the current working directory.\n'), false))
}
await fs.emptyDir(outDir)
logger.debug('Dist directory: ' + chalk.gray(outDir))
let clientConfig = createClientConfig(ctx, cliOptions).toConfig()
let serverConfig = createServerConfig(ctx, cliOptions).toConfig()
// apply user config...
const userConfig = ctx.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: ctx.siteConfig.shouldPrefetch || (() => true),
template: await fs.readFile(ctx.ssrTemplate, 'utf-8')
})
// pre-render head tags from user config
const userHeadTags = (ctx.siteConfig.head || [])
.map(renderHeadTag)
.join('\n ')
// if the user does not have a custom 404.md, generate the theme's default
if (!ctx.pages.some(p => p.path === '/404.html')) {
ctx.addPage({ path: '/404.html' })
}
// render pages
logger.wait('Rendering static HTML...')
const pagePaths = []
for (const page of ctx.pages) {
pagePaths.push(await renderPage(page))
}
readline.clearLine(process.stdout, 0)
readline.cursorTo(process.stdout, 0)
await ctx.pluginAPI.options.generated.apply(pagePaths)
// DONE.
const relativeDir = path.relative(cwd, outDir)
logger.success(`Generated static files in ${chalk.cyan(relativeDir)}.`)
const { duration } = performance.stop()
logger.developer(`It took a total of ${chalk.cyan(`${duration}ms`)} to run the ${chalk.cyan('vuepress build')}.`)
console.log()
// --- 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 (env.isDebug && 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 ? `</${tagName}>` : ``}`
}
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)
return filePath
}
function renderPageMeta (meta) {
if (!meta) return ''
return meta.map(m => {
let res = `<meta`
Object.keys(m).forEach(key => {
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)
}
}
@@ -58,8 +58,12 @@ Vue.prototype.$withBase = function (path) {
}
export function createApp (isServer) {
const routerBase = typeof window !== 'undefined' && window.__VUEPRESS_ROUTER_BASE__
? window.__VUEPRESS_ROUTER_BASE__
: (siteData.routerBase || siteData.base)
const router = new Router({
base: siteData.base,
base: routerBase,
mode: 'history',
fallback: false,
routes,
@@ -4,7 +4,7 @@ import { createApp } from './app'
const { app, router } = createApp(false /* isServer */)
window.__VUEPRESS_VERSION__ = {
window.__VUEPRESS__ = {
version: VUEPRESS_VERSION,
hash: LAST_COMMIT_HASH
}
@@ -0,0 +1,29 @@
import Vue from 'vue'
import { setGlobalInfo, getPageAsyncComponent } from '@app/util'
export default {
props: {
pageKey: String,
slotKey: {
type: String,
default: 'default'
}
},
render (h) {
const pageKey = this.pageKey || this.$parent.$page.key
setGlobalInfo('pageKey', pageKey)
/**
* This is for use cases that render `<Content />`
* with dynamic pageKey from current $page.
*/
if (!Vue.component(pageKey)) {
Vue.component(pageKey, getPageAsyncComponent(pageKey))
}
if (Vue.component(pageKey)) {
return h(pageKey)
}
return h('')
}
}
@@ -8,8 +8,7 @@ export default {
return h('div',
{
class: [
'content',
props.slotKey
`content__${props.slotKey}`
]
},
slots()[props.slotKey]
@@ -0,0 +1,32 @@
<template>
<component :is="layout"/>
</template>
<script>
import Vue from 'vue'
import { setGlobalInfo } from '@app/util'
export default {
methods: {
getLayout () {
if (this.$page.path) {
const layout = this.$page.frontmatter.layout
if (layout && (this.$vuepress.getLayoutAsyncComponent(layout)
|| this.$vuepress.getVueComponent(layout))) {
return layout
}
return 'Layout'
}
return 'NotFound'
}
},
computed: {
layout () {
const layout = this.getLayout()
setGlobalInfo('layout', layout)
return Vue.component(layout)
}
}
}
</script>
@@ -5,11 +5,12 @@
</svg>
</template>
<style lang="stylus">
.icon.outbound
color #aaa
display inline-block
vertical-align middle
position relative
top -1px
<style>
.icon.outbound {
color: #aaa;
display: inline-block;
vertical-align: middle;
position: relative;
top: -1px;
}
</style>
@@ -1,8 +1,8 @@
/* global VUEPRESS_TEMP_PATH */
import Vue from 'vue'
import GLobalVue from 'vue'
export default function dataMixin (I18n, siteData) {
export default function dataMixin (I18n, siteData, Vue = GLobalVue) {
prepare(siteData)
Vue.$vuepress.$set('siteData', siteData)
@@ -1,22 +1,18 @@
import Store from './Store'
import {
isPageExists,
isPageLoaded,
getPageAsyncComponent,
isLayoutExists,
isLayoutLoaded,
getLayoutAsyncComponent
getLayoutAsyncComponent,
getAsyncComponent,
getVueComponent
} from '../util'
class VuePress extends Store {}
Object.assign(VuePress.prototype, {
isPageExists,
isPageLoaded,
getPageAsyncComponent,
isLayoutExists,
isLayoutLoaded,
getLayoutAsyncComponent
getLayoutAsyncComponent,
getAsyncComponent,
getVueComponent
})
export default {
@@ -2,36 +2,89 @@ import Vue from 'vue'
import layoutComponents from '@internal/layout-components'
import pageComponents from '@internal/page-components'
/**
* Create a cached version of a pure function.
*/
function cached (fn) {
const cache = Object.create(null)
// eslint-disable-next-line func-names
return function cachedFn (str) {
const hit = cache[str]
// eslint-disable-next-line no-return-assign
return hit || (cache[str] = fn(str))
}
}
/**
* Camelize a hyphen-delimited string.
*/
const camelizeRE = /-(\w)/g
const camelize = cached(str => {
return str.replace(camelizeRE, (_, c) => c ? c.toUpperCase() : '')
})
/**
* Hyphenate a camelCase string.
*/
const hyphenateRE = /\B([A-Z])/g
const hyphenate = cached(str => {
return str.replace(hyphenateRE, '-$1').toLowerCase()
})
/**
* Capitalize a string.
*/
const capitalize = cached(str => {
return str.charAt(0).toUpperCase() + str.slice(1)
})
/**
* This method was for securely getting Vue component when components
* are named in different style.
*
* e.g. a component named `a-b` can be also getted by `AB`, It's the
* same the other way round
*
* @param {function} getter a function of getting component by name
* @param {string} name component's name
* @returns {Component|AsyncComponent}
*/
export function getComponent (getter, name) {
if (!name) return
if (getter(name)) return getter(name)
const isKebabCase = name.includes('-')
if (isKebabCase) return getter(capitalize(camelize(name)))
return getter(capitalize(name)) || getter(hyphenate(name))
}
const asyncComponents = Object.assign({}, layoutComponents, pageComponents)
export function isPageExists (pageKey) {
return Boolean(pageComponents[pageKey])
}
export function isPageLoaded (pageKey) {
return Boolean(Vue.component(pageKey))
}
const asyncComponentsGetter = name => asyncComponents[name]
const pageComponentsGetter = layout => pageComponents[layout]
const layoutComponentsGetter = layout => layoutComponents[layout]
const globalComponentsGetter = name => Vue.component(name)
export function getPageAsyncComponent (pageKey) {
return pageComponents[pageKey]
return getComponent(pageComponentsGetter, pageKey)
}
export function isLayoutExists (layout) {
return Boolean(layoutComponents[layout])
export function getLayoutAsyncComponent (layout) {
return getComponent(layoutComponentsGetter, layout)
}
export function isLayoutLoaded (layout) {
return Boolean(Vue.component(layout))
export function getAsyncComponent (name) {
return getComponent(asyncComponentsGetter, name)
}
export function getLayoutAsyncComponent (pageKey) {
return layoutComponents[pageKey]
export function getVueComponent (name) {
return getComponent(globalComponentsGetter, name)
}
export function ensureAsyncComponentsLoaded (...names) {
return Promise.all(names.filter(v => v).map(async (name) => {
if (!Vue.component(name) && asyncComponents[name]) {
const comp = await asyncComponents[name]()
if (!getVueComponent(name) && getAsyncComponent(name)) {
const comp = await getAsyncComponent(name)()
Vue.component(name, comp.default)
}
}))
@@ -129,3 +182,16 @@ export function normalizeConfig (component, rawConfig) {
}
return rawConfig
}
/**
* Set global info in `window.__VUEPRESS__` for debugging.
*
* @param {string}key
* @param {any} value
*/
export function setGlobalInfo (key, value) {
if (typeof window === 'undefined' || !window.__VUEPRESS__) {
return
}
window.__VUEPRESS__[key] = value
}
-207
View File
@@ -1,207 +0,0 @@
'use strict'
module.exports = async (sourceDir, cliOptions = {}, ctx) => {
const { server, host, port } = await prepareServer(sourceDir, cliOptions, ctx)
server.listen(port, host, err => {
if (err) {
console.log(err)
}
})
}
module.exports.prepare = prepareServer
async function prepareServer (sourceDir, cliOptions = {}, context) {
const WebpackDevServer = require('webpack-dev-server')
const { path } = require('@vuepress/shared-utils')
const webpack = require('webpack')
const chokidar = require('chokidar')
const prepare = require('./prepare/index')
const { chalk, fs, logger } = require('@vuepress/shared-utils')
const HeadPlugin = require('./webpack/HeadPlugin')
const DevLogPlugin = require('./webpack/DevLogPlugin')
const createClientConfig = require('./webpack/createClientConfig')
const { applyUserWebpackConfig } = require('./util/index')
const { frontmatterEmitter } = require('@vuepress/markdown-loader')
const ctx = context || await prepare(sourceDir, cliOptions, false /* isProd */)
// setup watchers to update options and dynamically generated files
const update = (reason) => {
console.log(`Reload due to ${reason}`)
ctx.pluginAPI.options.updated.syncApply()
prepare(sourceDir, cliOptions, false /* isProd */).catch(err => {
console.error(logger.error(chalk.red(err.stack), false))
})
}
// Curry update handler by update type
const spawnUpdate = updateType => file => {
const target = path.join(sourceDir, file)
// Bust cache.
delete require.cache[target]
update(`${chalk.red(updateType)} ${chalk.cyan(file)}`)
}
// watch add/remove of files
const pagesWatcher = chokidar.watch([
'**/*.md',
'.vuepress/components/**/*.vue'
], {
cwd: sourceDir,
ignored: ['.vuepress/**/*.md', 'node_modules'],
ignoreInitial: true
})
pagesWatcher.on('add', spawnUpdate('add'))
pagesWatcher.on('unlink', spawnUpdate('unlink'))
pagesWatcher.on('addDir', spawnUpdate('addDir'))
pagesWatcher.on('unlinkDir', spawnUpdate('unlinkDir'))
const watchFiles = [
'.vuepress/config.js',
'.vuepress/config.yml',
'.vuepress/config.toml'
].concat(
(
ctx.siteConfig.extraWatchFiles || []
).map(file => normalizeWatchFilePath(file, ctx.sourceDir))
)
logger.debug('watchFiles', watchFiles)
// watch config file
const configWatcher = chokidar.watch(watchFiles, {
cwd: sourceDir,
ignoreInitial: true
})
configWatcher.on('change', spawnUpdate('change'))
// also listen for frontmatter changes from markdown files
frontmatterEmitter.on('update', () => update('frontmatter or headers change'))
// resolve webpack config
let config = createClientConfig(ctx)
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: ctx.devTemplate
}])
config
.plugin('site-data')
.use(HeadPlugin, [{
tags: ctx.siteConfig.head || []
}])
const port = await resolvePort(cliOptions.port || ctx.siteConfig.port)
const { host, displayHost } = await resolveHost(cliOptions.host || ctx.siteConfig.host)
// debug in a running dev process.
process.stdin
&& process.stdin.on('data', chunk => {
const parsed = chunk.toString('utf-8').trim()
if (parsed === '*') {
console.log(Object.keys(ctx))
}
if (ctx[parsed]) {
console.log(ctx[parsed])
}
})
config
.plugin('vuepress-log')
.use(DevLogPlugin, [{
port,
displayHost,
publicPath: ctx.base
}])
config = config.toConfig()
const userConfig = ctx.siteConfig.configureWebpack
if (userConfig) {
config = applyUserWebpackConfig(userConfig, config, false /* isServer */)
}
const contentBase = path.resolve(sourceDir, '.vuepress/public')
const serverConfig = Object.assign({
disableHostCheck: true,
compress: true,
clientLogLevel: 'error',
hot: true,
quiet: true,
headers: {
'access-control-allow-origin': '*'
},
open: cliOptions.open,
publicPath: ctx.base,
watchOptions: {
ignored: [
/node_modules/,
`!${ctx.tempPath}/**`
]
},
historyApiFallback: {
disableDotRule: true,
rewrites: [
{ from: /./, to: path.posix.join(ctx.base, 'index.html') }
]
},
overlay: false,
host,
contentBase,
before (app, server) {
if (fs.existsSync(contentBase)) {
app.use(ctx.base, require('express').static(contentBase))
}
ctx.pluginAPI.options.beforeDevServer.syncApply(app, server)
},
after (app, server) {
ctx.pluginAPI.options.afterDevServer.syncApply(app, server)
}
}, ctx.siteConfig.devServer || {})
WebpackDevServer.addDevServerEntrypoints(config, serverConfig)
const compiler = webpack(config)
const server = new WebpackDevServer(compiler, serverConfig)
return {
server,
host,
port,
ctx
}
}
function resolveHost (host) {
const defaultHost = 'localhost'
host = host || defaultHost
const displayHost = host === defaultHost
? 'localhost'
: host
return {
displayHost,
host
}
}
async function resolvePort (port) {
const portfinder = require('portfinder')
portfinder.basePort = parseInt(port) || 8080
port = await portfinder.getPortPromise()
return port
}
function normalizeWatchFilePath (filepath, baseDir) {
const { isAbsolute, relative } = require('path')
if (isAbsolute(filepath)) {
return relative(baseDir, filepath)
}
return filepath
}
+25 -2
View File
@@ -1,5 +1,28 @@
'use strict'
exports.dev = require('./dev')
exports.build = require('./build')
const App = require('./node/App')
const { version } = require('../package')
const { logger } = require('@vuepress/shared-utils')
function createApp (options) {
logger.wait('Extracting site metadata...')
return new App(options)
}
async function dev (options) {
const app = createApp(options)
await app.process()
return app.dev()
}
async function build (options) {
const app = createApp(options)
await app.process()
return app.build()
}
exports.version = version
exports.createApp = createApp
exports.dev = dev
exports.build = build
exports.eject = require('./eject')
@@ -16,43 +16,41 @@ const {
const Page = require('./Page')
const ClientComputedMixin = require('./ClientComputedMixin')
const PluginAPI = require('../plugin-api/index')
const PluginAPI = require('./plugin-api')
const DevProcess = require('./dev')
const BuildProcess = require('./build')
const createTemp = require('./createTemp')
/**
* Expose AppContext.
* Expose VuePressApp.
*/
module.exports = class AppContext {
static getInstance (...args) {
if (!AppContext._instance) {
AppContext._instance = new AppContext(...args)
}
return AppContext._instance
}
module.exports = class App {
/**
* Instantiate the app context with a new API
*
* @param {string} sourceDir
* @param {{
* isProd: boolean,
* plugins: pluginsConfig,
* theme: themeNameConfig
* temp: string
* }} options
*/
constructor (sourceDir, cliOptions = {}, isProd) {
logger.debug('sourceDir', sourceDir)
this.sourceDir = sourceDir
this.cliOptions = cliOptions
this.isProd = isProd
constructor (options = {}) {
this.options = options
this.sourceDir = this.options.sourceDir || path.join(__dirname, 'docs.fallback')
logger.debug('sourceDir', this.sourceDir)
if (!fs.existsSync(this.sourceDir)) {
logger.warn(`Source directory doesn't exist: ${chalk.yellow(this.sourceDir)}`)
}
const { tempPath, writeTemp } = createTemp(cliOptions.temp)
const { tempPath, writeTemp } = createTemp(options.temp)
this.tempPath = tempPath
this.writeTemp = writeTemp
this.vuepressDir = path.resolve(sourceDir, '.vuepress')
this.vuepressDir = path.resolve(this.sourceDir, '.vuepress')
this.libDir = path.join(__dirname, '../')
}
/**
@@ -63,9 +61,14 @@ module.exports = class AppContext {
*/
resolveConfigAndInitialize () {
this.siteConfig = loadConfig(this.vuepressDir)
if (isFunction(this.siteConfig)) {
this.siteConfig = this.siteConfig(this)
if (this.options.siteConfig) {
this.siteConfig = this.options.siteConfig
} else {
let siteConfig = loadConfig(this.vuepressDir)
if (isFunction(siteConfig)) {
siteConfig = siteConfig(this)
}
this.siteConfig = siteConfig
}
// TODO custom cwd.
@@ -74,7 +77,7 @@ module.exports = class AppContext {
this.base = this.siteConfig.base || '/'
this.themeConfig = this.siteConfig.themeConfig || {}
const rawOutDir = this.cliOptions.dest || this.siteConfig.dest
const rawOutDir = this.options.dest || this.siteConfig.dest
this.outDir = rawOutDir
? require('path').resolve(this.cwd, rawOutDir)
: require('path').resolve(this.sourceDir, '.vuepress/dist')
@@ -84,7 +87,8 @@ module.exports = class AppContext {
}
/**
* Load pages, load plugins, apply plugins / plugin options, etc.
* A asynchronous method used to prepare the context of the current app. which
* contains loading pages and plugins, apply plugins, etc.
*
* @returns {Promise<void>}
* @api private
@@ -92,7 +96,6 @@ module.exports = class AppContext {
async process () {
this.resolveConfigAndInitialize()
this.resolveCacheLoaderOptions()
this.normalizeHeadTagUrls()
this.themeAPI = loadTheme(this)
this.resolveTemplates()
@@ -105,18 +108,18 @@ module.exports = class AppContext {
this.markdown = createMarkdown(this)
await this.resolvePages()
await this.pluginAPI.options.additionalPages.apply(this)
await this.pluginAPI.applyAsyncOption('additionalPages', this)
await Promise.all(
this.pluginAPI.options.additionalPages.appliedValues.map(async (options) => {
this.pluginAPI.getOption('additionalPages').appliedValues.map(async (options) => {
await this.addPage(options)
})
)
await this.pluginAPI.options.ready.apply()
await this.pluginAPI.applyAsyncOption('ready')
await Promise.all([
this.pluginAPI.options.clientDynamicModules.apply(this),
this.pluginAPI.options.enhanceAppFiles.apply(this),
this.pluginAPI.options.globalUIComponents.apply(this)
this.pluginAPI.applyAsyncOption('clientDynamicModules', this),
this.pluginAPI.applyAsyncOption('enhanceAppFiles', this),
this.pluginAPI.applyAsyncOption('globalUIComponents', this)
])
}
@@ -138,17 +141,27 @@ module.exports = class AppContext {
this.pluginAPI
// internl core plugins
.use(require('../internal-plugins/siteData'))
.use(require('../internal-plugins/routes'))
.use(require('../internal-plugins/rootMixins'))
.use(require('../internal-plugins/enhanceApp'))
.use(require('../internal-plugins/palette'))
.use(require('../internal-plugins/style'))
.use(require('../internal-plugins/layoutComponents'))
.use(require('../internal-plugins/pageComponents'))
.use(require('../internal-plugins/transformModule'))
.use(require('../internal-plugins/dataBlock'))
.use(require('../internal-plugins/frontmatterBlock'))
.use(require('./internal-plugins/siteData'))
.use(require('./internal-plugins/routes'))
.use(require('./internal-plugins/rootMixins'))
.use(require('./internal-plugins/enhanceApp'))
.use(require('./internal-plugins/palette'))
.use(require('./internal-plugins/style'))
.use(require('./internal-plugins/layoutComponents'))
.use(require('./internal-plugins/pageComponents'))
.use(require('./internal-plugins/transformModule'))
.use(require('./internal-plugins/dataBlock'))
.use(require('./internal-plugins/frontmatterBlock'))
.use('container', {
type: 'slot',
before: info => `<template slot="${info}">`,
after: '</template>'
})
.use('container', {
type: 'v-pre',
before: '<div v-pre>',
after: '</div>'
})
.use('@vuepress/last-updated', !!shouldUseLastUpdated)
.use('@vuepress/register-components', {
componentsDir: [
@@ -166,7 +179,7 @@ module.exports = class AppContext {
*/
applyUserPlugins () {
this.pluginAPI.useByPluginsConfig(this.cliOptions.plugins)
this.pluginAPI.useByPluginsConfig(this.options.plugins)
if (this.themeAPI.existsParentTheme) {
this.pluginAPI.use(this.themeAPI.parentTheme.entry)
}
@@ -205,7 +218,7 @@ module.exports = class AppContext {
*/
resolveCacheLoaderOptions () {
Object.assign(this, (getCacheLoaderOptions(this.siteConfig, this.cliOptions, this.cwd, this.isProd)))
Object.assign(this, (getCacheLoaderOptions(this.siteConfig, this.options, this.cwd, this.isProd)))
}
/**
@@ -225,7 +238,7 @@ module.exports = class AppContext {
this.devTemplate = this.resolveCommonAgreementFilePath(
'devTemplate',
{
defaultValue: path.resolve(__dirname, '../app/index.dev.html'),
defaultValue: this.getLibFilePath('client/index.dev.html'),
siteAgreement: 'templates/dev.html',
themeAgreement: 'templates/dev.html'
}
@@ -234,7 +247,7 @@ module.exports = class AppContext {
this.ssrTemplate = this.resolveCommonAgreementFilePath(
'ssrTemplate',
{
defaultValue: path.resolve(__dirname, '../app/index.ssr.html'),
defaultValue: this.getLibFilePath('client/index.ssr.html'),
siteAgreement: 'templates/ssr.html',
themeAgreement: 'templates/ssr.html'
}
@@ -255,7 +268,7 @@ module.exports = class AppContext {
this.globalLayout = this.resolveCommonAgreementFilePath(
'globalLayout',
{
defaultValue: path.resolve(__dirname, `../app/components/GlobalLayout.vue`),
defaultValue: this.getLibFilePath('client/components/GlobalLayout.vue'),
siteAgreement: `components/GlobalLayout.vue`,
themeAgreement: `layouts/GlobalLayout.vue`
}
@@ -333,9 +346,16 @@ module.exports = class AppContext {
await page.process({
markdown: this.markdown,
computed: new this.ClientComputedMixinConstructor(),
enhancers: this.pluginAPI.options.extendPageData.items
enhancers: this.pluginAPI.getOption('extendPageData').items
})
this.pages.push(page)
const index = this.pages.findIndex(({ path }) => path === page.path)
if (index >= 0) {
// Override a page if corresponding path already exists
logger.warn(`Override existing page ${chalk.yellow(page.path)}.`)
this.pages.splice(index, 1, page)
} else {
this.pages.push(page)
}
}
/**
@@ -365,7 +385,7 @@ module.exports = class AppContext {
return current
}
if (this.themeAPI.existsParentTheme) {
const parent = path.resolve(this.themeAPI.theme.path, filepath)
const parent = path.resolve(this.themeAPI.parentTheme.path, filepath)
if (fs.existsSync(parent)) {
return parent
}
@@ -415,45 +435,62 @@ module.exports = class AppContext {
locales
}
}
}
/**
* Create a dynamic temp utility context that allow to lanuch
* multiple apps with isolated context at the same time.
* @param tempPath
* @returns {{
* writeTemp: (function(file: string, content: string): string),
* tempPath: string
* }}
*/
/**
* Get file path in core lib
*
* @param relative
* @returns {string}
* @api public
*/
function createTemp (tempPath) {
if (!tempPath) {
tempPath = path.resolve(__dirname, '../../.temp')
} else {
tempPath = path.resolve(tempPath)
getLibFilePath (relative) {
return path.join(this.libDir, relative)
}
if (!fs.existsSync(tempPath)) {
fs.ensureDirSync(tempPath)
} else {
fs.emptyDirSync(tempPath)
}
/**
* Launch a dev process with current app context.
*
* @returns {Promise<App>}
* @api public
*/
logger.debug(`Temp directory: ${chalk.gray(tempPath)}`)
const tempCache = new Map()
async function writeTemp (file, content) {
const destPath = path.join(tempPath, file)
await fs.ensureDir(path.parse(destPath).dir)
// cache write to avoid hitting the dist if it didn't change
const cached = tempCache.get(file)
if (cached !== content) {
await fs.writeFile(destPath, content)
tempCache.set(file, content)
async dev () {
this.isProd = false
this.devProcess = new DevProcess(this)
await this.devProcess.process()
const error = await new Promise(resolve => {
try {
this.devProcess
.on('fileChanged', ({ type, target }) => {
console.log(`Reload due to ${chalk.red(type)} ${chalk.cyan(path.relative(this.sourceDir, target))}`)
this.process()
})
.createServer()
.listen(resolve)
} catch (err) {
resolve(err)
}
})
if (error) {
throw error
}
return destPath
return this
}
return { writeTemp, tempPath }
/**
* Launch a build process with current app context
*
* @returns {Promise<App>}
* @api public
*/
async build () {
this.isProd = true
this.buildProcess = new BuildProcess(this)
await this.buildProcess.process()
await this.buildProcess.render()
return this
}
}
@@ -12,12 +12,12 @@ const {
/**
* Get cache directory and cache identifier via config.
* @param {object} siteConfig
* @param {object} cliOptions
* @param {object} options
*/
exports.getCacheLoaderOptions = function (siteConfig, cliOptions, cwd, isProd) {
exports.getCacheLoaderOptions = function (siteConfig, options, cwd, isProd) {
const defaultCacheDirectory = path.resolve(__dirname, '../../node_modules/.cache/vuepress')
let cache = cliOptions.cache || siteConfig.cache || defaultCacheDirectory
let cache = options.cache || siteConfig.cache || defaultCacheDirectory
if (isBoolean(cache)) {
if (cache === true) {
@@ -4,7 +4,7 @@
* Module dependencies.
*/
const { inferDate, DATE_RE } = require('../util/index')
const { inferDate, DATE_RE } = require('./util/index')
const {
fs,
path,
@@ -37,7 +37,7 @@ module.exports = class Page {
*/
constructor ({
path,
path: _path,
meta,
title,
content,
@@ -58,12 +58,16 @@ module.exports = class Page {
if (relative) {
this.regularPath = encodeURI(fileToPath(relative))
} else if (path) {
this.regularPath = encodeURI(path)
} else if (_path) {
this.regularPath = encodeURI(_path)
} else if (permalink) {
this.regularPath = encodeURI(permalink)
}
if (filePath) {
this.relativePath = path.relative(context.sourceDir, filePath).replace(/\\/g, '/')
}
this.key = 'v-' + hash(`${this._filePath}${this.regularPath}`)
// Using regularPath first, would be override by permalink later.
this.path = this.regularPath
@@ -85,11 +89,7 @@ module.exports = class Page {
enhancers = [],
preRender = {}
}) {
// relative path
let relPath
if (this._filePath) {
relPath = path.relative(this._context.sourceDir, this._filePath)
logger.developer(`static_route`, chalk.cyan(this.path))
this._content = await fs.readFile(this._filePath, 'utf-8')
} else if (this._content) {
@@ -124,7 +124,7 @@ module.exports = class Page {
if (excerpt) {
const { html } = markdown.render(excerpt, {
frontmatter: this.frontmatter,
relPath
relativePath: this.relativePath
})
this.excerpt = html
}
@@ -146,6 +146,17 @@ module.exports = class Page {
this.buildPermalink()
}
/**
* name of page's parent directory.
*
* @returns {string}
* @api public
*/
get dirname () {
return path.basename(path.dirname(this._filePath || this.regularPath))
}
/**
* file name of page's source markdown file, or the last cut of regularPath.
*
@@ -165,7 +176,19 @@ module.exports = class Page {
*/
get slug () {
return slugify(this.strippedFilename)
const strippedFilename = this.strippedFilename
if (/^(index|readme)$/i.test(strippedFilename)) {
const strippedFilename = this.stripFilename(
path.basename(path.dirname(this._filePath || this.regularPath))
)
if (strippedFilename) {
return slugify(strippedFilename)
}
}
return slugify(strippedFilename)
}
/**
@@ -179,8 +202,7 @@ module.exports = class Page {
*/
get strippedFilename () {
const match = this.filename.match(DATE_RE)
return match ? match[3] : this.filename
return this.stripFilename(this.filename)
}
/**
@@ -191,7 +213,23 @@ module.exports = class Page {
*/
get date () {
return inferDate(this.frontmatter, this.filename)
return inferDate(this.frontmatter, this.filename, this.dirname)
}
/**
* stripped file name.
*
* If filename was yyyy-MM-dd-[title], the date prefix will be stripped.
* If filename was yyyy-MM-[title], the date prefix will be stripped.
*
* @param {string} fileName
* @returns {string}
* @private
*/
stripFilename (fileName) {
const match = fileName.match(DATE_RE)
return match ? match[3] : fileName
}
/**
@@ -1,4 +1,4 @@
const AsyncOption = require('../../lib/plugin-api/abstract/AsyncOption')
const AsyncOption = require('../../plugin-api/abstract/AsyncOption')
describe('AsyncOption', () => {
test('parallelApply', async () => {
@@ -1,4 +1,4 @@
import Option from '../../lib/plugin-api/abstract/Option'
import Option from '../../plugin-api/abstract/Option'
describe('Option', () => {
test('key', () => {
@@ -2,8 +2,8 @@ jest.mock('vuepress-plugin-a')
jest.mock('vuepress-plugin-b')
jest.mock('@org/vuepress-plugin-a')
import PluginAPI from '../../lib/plugin-api/index'
import { PLUGIN_OPTION_MAP } from '../../lib/plugin-api/constants'
import PluginAPI from '../../plugin-api/index'
import { PLUGIN_OPTION_MAP } from '../../plugin-api/constants'
describe('Plugin', () => {
test('registerOption', () => {
@@ -1,31 +1,31 @@
import { flattenPlugin } from '../../lib/plugin-api/util'
import { flattenPlugin } from '../../plugin-api/util'
describe('flattenPlugin', () => {
test('should hydrate plugin correctly', () => {
const plugin = { name: 'a', shortcut: 'a', module: { enhanceAppFiles: 'file' }}
const plugin = { name: 'a', shortcut: 'a', entry: { enhanceAppFiles: 'file' }}
const hydratedPlugin = flattenPlugin(plugin, {}, {})
expect(hydratedPlugin.name).toBe('a')
expect(hydratedPlugin.shortcut).toBe('a')
expect(hydratedPlugin.shortcut).toBe(null)
expect(hydratedPlugin.enabled).toBe(true)
expect(hydratedPlugin.enhanceAppFiles).toBe('file')
})
test('should set \'enabled\' to false when \'pluginOptions\' is set to false.', () => {
const plugin = { name: 'a', shortcut: 'a', module: {}}
const plugin = { name: 'a', shortcut: 'a', entry: {}}
const hydratedPlugin = flattenPlugin(plugin, false, {})
expect(hydratedPlugin.name).toBe('a')
expect(hydratedPlugin.shortcut).toBe('a')
expect(hydratedPlugin.shortcut).toBe(null)
expect(hydratedPlugin.enabled).toBe(false)
})
test('should flatten functional plugin correctly.', () => {
const config = jest.fn(() => ({ enhanceAppFiles: 'file' }))
const plugin = { name: 'a', shortcut: 'a', module: config }
const plugin = { name: 'a', shortcut: 'a', entry: config }
const pluginOptions = {}
const pluginContext = {}
const hydratedPlugin = flattenPlugin(plugin, pluginOptions, pluginContext)
expect(hydratedPlugin.name).toBe('a')
expect(hydratedPlugin.shortcut).toBe('a')
expect(hydratedPlugin.shortcut).toBe(null)
expect(hydratedPlugin.enabled).toBe(true)
expect(hydratedPlugin.enhanceAppFiles).toBe('file')
expect(config.mock.calls).toHaveLength(1)
@@ -35,7 +35,7 @@ describe('flattenPlugin', () => {
test('should flatten functional plugin correctly - options defaults to \'{}\'.', () => {
const config = jest.fn(() => ({ enhanceAppFiles: 'file' }))
const plugin = { name: 'a', shortcut: 'a', module: config }
const plugin = { name: 'a', shortcut: 'a', entry: config }
const pluginOptions = undefined
const pluginContext = {}
flattenPlugin(plugin, pluginOptions, pluginContext)
@@ -1,5 +1,5 @@
const { fs, path } = require('@vuepress/shared-utils')
const prepare = require('../../lib/prepare')
const App = require('../../App')
const docsBaseDir = path.resolve(__dirname, 'fixtures')
const docsModeNames = fs.readdirSync(docsBaseDir)
@@ -9,12 +9,17 @@ const docsModes = docsModeNames.map(name => {
return { name, docsPath, docsTempPath }
})
describe('prepare', () => {
describe('App', () => {
test('should not throw error', async () => {
await Promise.all(docsModes.map(async ({ name, docsPath, docsTempPath }) => {
await fs.ensureDir(docsTempPath)
const context = await prepare(docsPath, { theme: '@vuepress/default', temp: docsTempPath })
expect(context.sourceDir).toBe(docsPath)
const app = new App({
sourceDir: docsPath,
theme: '@vuepress/default',
emp: docsTempPath
})
await app.process()
expect(app.sourceDir).toBe(docsPath)
}))
})
})
@@ -1,19 +1,28 @@
const Page = require('../../lib/prepare/Page')
const Page = require('../../Page')
const App = require('../../App')
const {
getComputed,
getMarkdown,
getDocument,
readFile
} = require('./util')
describe('Page', () => {
let app
let computed
beforeAll(async () => {
app = new App()
await app.process()
computed = new app.ClientComputedMixinConstructor()
})
test('pure route', async () => {
const page = new Page({ path: '/' })
const page = new Page({ path: '/' }, app)
expect(page.path).toBe('/')
expect(page.regularPath).toBe('/')
const computed = getComputed()
await page.process({ computed })
expect(page.path).toBe('/')
@@ -22,7 +31,7 @@ describe('Page', () => {
test('pure route - encodeURI', async () => {
const path = '/尤/'
const page = new Page({ path })
const page = new Page({ path }, app)
expect(page.path).toBe(encodeURI(path))
expect(page.regularPath).toBe(encodeURI(path))
@@ -33,7 +42,7 @@ describe('Page', () => {
const page = new Page({
path: '/',
frontmatter
})
}, app)
expect(page.frontmatter).toBe(frontmatter)
})
@@ -42,15 +51,16 @@ describe('Page', () => {
const page = new Page({
path: '/',
frontmatter
})
}, app)
expect(page.frontmatter.title).toBe('alpha')
const computed = getComputed()
const enhancers = [
{
name: 'plugin-a',
value: page => { page.frontmatter.title = 'beta' }
value: page => {
page.frontmatter.title = 'beta'
}
}
]
await page.process({ computed, enhancers })
@@ -60,14 +70,13 @@ describe('Page', () => {
test('markdown page - pointing to a markdown file', async () => {
const { relative, filePath } = getDocument('README.md')
const page = new Page({ filePath, relative })
const page = new Page({ filePath, relative }, app)
expect(page._filePath).toBe(filePath)
expect(page.regularPath).toBe('/')
expect(page.path).toBe('/')
expect(page.frontmatter).toEqual({})
const computed = getComputed()
const markdown = getMarkdown()
await page.process({ computed, markdown })
@@ -79,14 +88,13 @@ describe('Page', () => {
test('markdown page - pointing to a markdown file with frontmatter', async () => {
const { relative, filePath } = getDocument('alpha.md')
const page = new Page({ filePath, relative })
const page = new Page({ filePath, relative }, app)
expect(page._filePath).toBe(filePath)
expect(page.regularPath).toBe('/alpha.html')
expect(page.path).toBe('/alpha.html')
expect(page.frontmatter).toEqual({})
const computed = getComputed()
const markdown = getMarkdown()
await page.process({ computed, markdown })
@@ -0,0 +1,5 @@
module.exports = {
title: 'Hello VuePress',
description: '# Hello, VuePress!',
theme: 'vuepress-theme-without-index'
}
@@ -0,0 +1 @@
# Hello, VuePress!
@@ -1,15 +1,5 @@
const { fs, path } = require('@vuepress/shared-utils')
const AppContext = require('../../lib/prepare/AppContext')
const createMarkdown = require('../../../markdown/index')
function getAppContext () {
return new AppContext('.')
}
function getComputed () {
const context = getAppContext()
return new context.ClientComputedMixinConstructor()
}
const createMarkdown = require('../../../../../markdown/index')
const docsBaseDir = path.resolve(__dirname, 'fixtures/docs')
@@ -25,8 +15,6 @@ const getMarkdown = createMarkdown
const readFile = async filePath => await fs.readFile(filePath, 'utf-8')
module.exports = {
getAppContext,
getComputed,
getMarkdown,
getDocument,
readFile
@@ -1,7 +1,7 @@
jest.mock('vuepress-theme-parent')
jest.mock('vuepress-theme-child')
import ThemeAPI from '../../lib/theme-api'
import ThemeAPI from '../../theme-api'
import { resolve } from 'path'
const theme = {
@@ -0,0 +1,271 @@
'use strict'
const EventEmitter = require('events').EventEmitter
const webpack = require('webpack')
const readline = require('readline')
const escape = require('escape-html')
const { chalk, fs, path, logger, env, performance } = require('@vuepress/shared-utils')
const createClientConfig = require('../webpack/createClientConfig')
const createServerConfig = require('../webpack/createServerConfig')
const { createBundleRenderer } = require('vue-server-renderer')
const { normalizeHeadTag, applyUserWebpackConfig } = require('../util/index')
/**
* Expose Build Process Class.
*/
module.exports = class Build extends EventEmitter {
constructor (context) {
super()
process.env.NODE_ENV = 'production'
this.context = context
this.outDir = this.context.outDir
}
/**
* Doing somthing before render pages, e.g. validate and empty output directory,
* prepare webpack config.
*
* @returns {Promise<void>}
* @api public
*/
async process () {
if (this.context.cwd === this.outDir) {
throw new Error('Unexpected option: "outDir" cannot be set to the current working directory')
}
this.context.resolveCacheLoaderOptions()
await fs.emptyDir(this.outDir)
logger.debug('Dist directory: ' + chalk.gray(this.outDir))
this.prepareWebpackConfig()
}
/**
* Compile and render pages.
*
* @returns {Promise<void>}
* @api public
*/
async render () {
// compile!
const stats = await compile([this.clientConfig, this.serverConfig])
const serverBundle = require(path.resolve(this.outDir, 'manifest/server.json'))
const clientManifest = require(path.resolve(this.outDir, 'manifest/client.json'))
// remove manifests after loading them.
await fs.remove(path.resolve(this.outDir, 'manifest'))
// ref: https://github.com/vuejs/vuepress/issues/1367
if (!this.clientConfig.devtool && (!this.clientConfig.plugins
|| !this.clientConfig.plugins.some(p =>
p instanceof webpack.SourceMapDevToolPlugin
|| p instanceof webpack.EvalSourceMapDevToolPlugin
))) {
await workaroundEmptyStyleChunk(stats, this.outDir)
}
// create server renderer using built manifests
this.renderer = createBundleRenderer(serverBundle, {
clientManifest,
runInNewContext: false,
inject: false,
shouldPrefetch: this.context.siteConfig.shouldPrefetch || (() => true),
template: await fs.readFile(this.context.ssrTemplate, 'utf-8')
})
// pre-render head tags from user config
this.userHeadTags = (this.context.siteConfig.head || [])
.map(renderHeadTag)
.join('\n ')
// if the user does not have a custom 404.md, generate the theme's default
if (!this.context.pages.some(p => p.path === '/404.html')) {
this.context.addPage({ path: '/404.html' })
}
// render pages
logger.wait('Rendering static HTML...')
const pagePaths = []
for (const page of this.context.pages) {
pagePaths.push(await this.renderPage(page))
}
readline.clearLine(process.stdout, 0)
readline.cursorTo(process.stdout, 0)
await this.context.pluginAPI.applyAsyncOption('generated', pagePaths)
// DONE.
const relativeDir = path.relative(this.context.cwd, this.outDir)
logger.success(`Generated static files in ${chalk.cyan(relativeDir)}.`)
const { duration } = performance.stop()
logger.developer(`It took a total of ${chalk.cyan(`${duration}ms`)} to run the ${chalk.cyan('vuepress build')}.`)
console.log()
}
/**
* Prepare webpack config under build.
*
* @api private
*/
prepareWebpackConfig () {
this.clientConfig = createClientConfig(this.context).toConfig()
this.serverConfig = createServerConfig(this.context).toConfig()
const userConfig = this.context.siteConfig.configureWebpack
if (userConfig) {
this.clientConfig = applyUserWebpackConfig(userConfig, this.clientConfig, false)
this.serverConfig = applyUserWebpackConfig(userConfig, this.serverConfig, true)
}
}
/**
* Render page
*
* @param {Page} page
* @returns {Promise<string>}
* @api private
*/
async renderPage (page) {
const pagePath = decodeURIComponent(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: page.path,
userHeadTags: this.userHeadTags,
pageMeta,
title: 'VuePress',
lang: 'en',
description: ''
}
let html
try {
html = await this.renderer.renderToString(context)
} catch (e) {
console.error(logger.error(chalk.red(`Error rendering ${pagePath}:`), false))
throw e
}
const filename = pagePath.replace(/\/$/, '/index.html').replace(/^\//, '')
const filePath = path.resolve(this.outDir, filename)
await fs.ensureDir(path.dirname(filePath))
await fs.writeFile(filePath, html)
return filePath
}
}
/**
* Compile a webpack application and return stats json.
*
* @param {Object} config
* @returns {Promise<Object>}
*/
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 (env.isDebug && stats.hasWarnings()) {
stats.toJson().warnings.forEach(warning => {
console.warn(warning)
})
}
resolve(stats.toJson({ modules: false }))
})
})
}
/**
* Render head tag
*
* @param {Object} tag
* @returns {string}
*/
function renderHeadTag (tag) {
const { tagName, attributes, innerHTML, closeTag } = normalizeHeadTag(tag)
return `<${tagName}${renderAttrs(attributes)}>${innerHTML}${closeTag ? `</${tagName}>` : ``}`
}
/**
* Render html attributes
*
* @param {Object} attrs
* @returns {string}
*/
function renderAttrs (attrs = {}) {
const keys = Object.keys(attrs)
if (keys.length) {
return ' ' + keys.map(name => `${name}="${escape(attrs[name])}"`).join(' ')
} else {
return ''
}
}
/**
* Render meta tags
*
* @param {Array} meta
* @returns {Array<string>}
*/
function renderPageMeta (meta) {
if (!meta) return ''
return meta.map(m => {
let res = `<meta`
Object.keys(m).forEach(key => {
res += ` ${key}="${escape(m[key])}"`
})
return res + `>`
}).join('')
}
/**
* 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
*
* @param {Object} stats
* @param {String} outDir
* @returns {Promise<void>}
*/
async function workaroundEmptyStyleChunk (stats, outDir) {
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)
}
@@ -16,12 +16,12 @@ module.exports = function (ctx) {
const beforeInstantiate = config => {
chainMarkdown && chainMarkdown(config)
ctx.pluginAPI.options.chainMarkdown.syncApply(config)
ctx.pluginAPI.applySyncOption('chainMarkdown', config)
}
const afterInstantiate = md => {
extendMarkdown && extendMarkdown(md)
ctx.pluginAPI.options.extendMarkdown.syncApply(md)
ctx.pluginAPI.applySyncOption('extendMarkdown', md)
}
return createMarkdown(
@@ -0,0 +1,42 @@
const { fs, path, chalk, logger } = require('@vuepress/shared-utils')
/**
* Create a dynamic temp utility context that allow to lanuch
* multiple apps with isolated context at the same time.
* @param tempPath
* @returns {{
* writeTemp: (function(file: string, content: string): string),
* tempPath: string
* }}
*/
module.exports = function createTemp (tempPath) {
if (!tempPath) {
tempPath = path.resolve(__dirname, '../../.temp')
} else {
tempPath = path.resolve(tempPath)
}
if (!fs.existsSync(tempPath)) {
fs.ensureDirSync(tempPath)
} else {
fs.emptyDirSync(tempPath)
}
logger.debug(`Temp directory: ${chalk.gray(tempPath)}`)
const tempCache = new Map()
async function writeTemp (file, content) {
const destPath = path.join(tempPath, file)
await fs.ensureDir(path.parse(destPath).dir)
// cache write to avoid hitting the dist if it didn't change
const cached = tempCache.get(file)
if (cached !== content) {
await fs.writeFile(destPath, content)
tempCache.set(file, content)
}
return destPath
}
return { writeTemp, tempPath }
}
@@ -0,0 +1,308 @@
'use strict'
/**
* Module dependencies.
*/
const EventEmitter = require('events').EventEmitter
const WebpackDevServer = require('webpack-dev-server')
const { frontmatterEmitter } = require('@vuepress/markdown-loader')
const webpack = require('webpack')
const chokidar = require('chokidar')
const { path, fs, logger } = require('@vuepress/shared-utils')
const HeadPlugin = require('../webpack/HeadPlugin')
const DevLogPlugin = require('../webpack/DevLogPlugin')
const createClientConfig = require('../webpack/createClientConfig')
const { applyUserWebpackConfig } = require('../util/index')
module.exports = class DevProcess extends EventEmitter {
constructor (context) {
super()
this.context = context
}
/**
* Prepare essential data for launch dev server.
*/
async process () {
this.context.resolveCacheLoaderOptions()
this.watchSourceFiles()
this.watchUserConfig()
this.watchFrontmatter()
this.setupDebugTip()
await this.resolvePort()
await this.resolveHost()
this.prepareWebpackConfig()
return this
}
/**
* Hande file's update, need to re-prepare app context.
*
* @param {string} type
* @param {string} target
*/
handleUpdate (type, target) {
logger.debug(type, target)
if (!path.isAbsolute(target)) {
target = path.join(this.context.sourceDir, target)
}
if (target.endsWith('.js')) {
// Bust cache.
delete require.cache[target]
}
this.emit('fileChanged', {
type,
target
})
}
/**
* Watch user's source document files.
*/
watchSourceFiles () {
// watch add/remove of files
this.pagesWatcher = chokidar.watch([
'**/*.md',
'.vuepress/components/**/*.vue'
], {
cwd: this.context.sourceDir,
ignored: ['.vuepress/**/*.md', 'node_modules'],
ignoreInitial: true
})
this.pagesWatcher.on('add', target => this.handleUpdate('add', target))
this.pagesWatcher.on('unlink', target => this.handleUpdate('unlink', target))
this.pagesWatcher.on('addDir', target => this.handleUpdate('addDir', target))
this.pagesWatcher.on('unlinkDir', target => this.handleUpdate('unlinkDir', target))
}
/**
* Watch user's config files and extra files.
*/
watchUserConfig () {
this.watchFiles = [
'.vuepress/config.js',
'.vuepress/config.yml',
'.vuepress/config.toml'
].concat(
(
this.context.siteConfig.extraWatchFiles || []
).map(file => normalizeWatchFilePath(file, this.context.sourceDir))
)
logger.debug('watchFiles', this.watchFiles)
this.configWatcher = chokidar.watch(this.watchFiles, {
cwd: this.context.sourceDir,
ignoreInitial: true
})
this.configWatcher.on('change', target => this.handleUpdate('change', target))
}
/**
* Also listen for frontmatter changes from markdown files
*/
watchFrontmatter () {
frontmatterEmitter.on('update', target => this.handleUpdate('frontmatter', target))
}
/**
* Resolve used port
*/
async resolvePort () {
this.port = await resolvePort(this.context.options.port || this.context.siteConfig.port)
}
/**
* Resolve used host
*/
async resolveHost () {
const { host, displayHost } = await resolveHost(this.context.options.host || this.context.siteConfig.host)
this.host = host
this.displayHost = displayHost
}
/**
* Set up a shortcut to debug context under dev.
*/
setupDebugTip () {
// debug in a running dev process.
process.stdin
&& process.stdin.on('data', chunk => {
const parsed = chunk.toString('utf-8').trim()
if (parsed === '*') {
console.log(Object.keys(this.context))
}
if (this.context[parsed]) {
console.log(this.context[parsed])
}
})
}
/**
* Prepare webpack for dev process.
*/
prepareWebpackConfig () {
// resolve webpack config
let config = createClientConfig(this.context)
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: this.context.devTemplate
}])
config
.plugin('site-data')
.use(HeadPlugin, [{
tags: this.context.siteConfig.head || []
}])
config
.plugin('vuepress-log')
.use(DevLogPlugin, [{
port: this.port,
displayHost: this.displayHost,
publicPath: this.context.base,
clearScreen: this.context.options.clearScreen
}])
config = config.toConfig()
const userConfig = this.context.siteConfig.configureWebpack
if (userConfig) {
config = applyUserWebpackConfig(userConfig, config, false /* isServer */)
}
this.webpackConfig = config
}
/**
* Create dev server
* @returns {module.DevProcess}
*/
createServer () {
const contentBase = path.resolve(this.context.sourceDir, '.vuepress/public')
const serverConfig = Object.assign({
disableHostCheck: true,
compress: true,
clientLogLevel: 'error',
hot: true,
quiet: true,
headers: {
'access-control-allow-origin': '*'
},
open: this.context.options.open,
publicPath: this.context.base,
watchOptions: {
ignored: [
/node_modules/,
`!${this.context.tempPath}/**`
]
},
historyApiFallback: {
disableDotRule: true,
rewrites: [
{ from: /./, to: path.posix.join(this.context.base, 'index.html') }
]
},
overlay: false,
host: this.host,
contentBase,
before: (app, server) => {
if (fs.existsSync(contentBase)) {
app.use(this.context.base, require('express').static(contentBase))
}
this.context.pluginAPI.applySyncOption('beforeDevServer', app, server)
},
after: (app, server) => {
this.context.pluginAPI.applySyncOption('afterDevServer', app, server)
}
}, this.context.siteConfig.devServer || {})
WebpackDevServer.addDevServerEntrypoints(this.webpackConfig, serverConfig)
const compiler = webpack(this.webpackConfig)
this.server = new WebpackDevServer(compiler, serverConfig)
return this
}
/**
* delegate listen call.
*
* @param callback handler when connection is ready.
* @returns {module.DevProcess}
*/
listen (callback) {
this.server.listen(this.port, this.host, (err) => {
if (typeof callback === 'function') {
callback(err)
}
})
return this
}
}
/**
* Resolve host.
*
* @param {string} host user's host
* @returns {{displayHost: string, host: string}}
*/
function resolveHost (host) {
const defaultHost = '0.0.0.0'
host = host || defaultHost
const displayHost = host === defaultHost
? 'localhost'
: host
return {
displayHost,
host
}
}
/**
* Resolve port.
*
* @param {number} port user's port
* @returns {Promise<number>}
*/
async function resolvePort (port) {
const portfinder = require('portfinder')
portfinder.basePort = parseInt(port) || 8080
port = await portfinder.getPortPromise()
return port
}
/**
* Normalize file path and always return relative path,
*
* @param {string} filepath user's path
* @param {string} baseDir source directory
* @returns {string}
*/
function normalizeWatchFilePath (filepath, baseDir) {
const { isAbsolute, relative } = require('path')
if (isAbsolute(filepath)) {
return relative(baseDir, filepath)
}
return filepath
}
@@ -0,0 +1,3 @@
# VuePress
> `Prompts`: You are running VuePress without setting sourceDir!
@@ -18,7 +18,7 @@ module.exports = function (source, map) {
&& parsed.data
&& JSON.stringify(cached.data) !== JSON.stringify(parsed.data)
) {
frontmatterEmitter.emit('update')
frontmatterEmitter.emit('update', file)
}
cache.set(file, parsed)
@@ -8,7 +8,7 @@ module.exports = (options, ctx) => ({
async ready () {
// 1. enable config.styl globally.
const configFile = path.resolve(__dirname, '../../app/style/config.styl')
const configFile = ctx.getLibFilePath('client/style/config.styl')
if (!ctx.siteConfig.stylus) {
ctx.siteConfig.stylus = {
import: [configFile]
@@ -31,15 +31,22 @@ module.exports = (options, ctx) => ({
? `@import(${JSON.stringify(userPalette.replace(/[\\]+/g, '/'))})`
: ''
const nullComment = '// null'
// user's palette can override theme's palette.
let paletteContent = themePaletteContent + userPaletteContent
let paletteContent = '// Theme\'s Palette\n'
+ (themePaletteContent || nullComment)
+ '\n\n// User\'s Palette\n'
+ (userPaletteContent || nullComment)
if (ctx.themeAPI.existsParentTheme) {
const parentThemePalette = path.resolve(ctx.themeAPI.parentTheme.path, 'styles/palette.styl')
const parentThemePaletteContent = fs.existsSync(parentThemePalette)
? `@import(${JSON.stringify(parentThemePalette.replace(/[\\]+/g, '/'))})`
: ''
paletteContent = parentThemePaletteContent + paletteContent
paletteContent = '// Parent Theme\'s Palette\n'
+ (parentThemePaletteContent || nullComment)
+ '\n\n' + paletteContent
}
await writeTemp('palette.styl', paletteContent)
@@ -1,4 +1,3 @@
const { path } = require('@vuepress/shared-utils')
const { codegen: { pathsToModuleCode }} = require('@vuepress/shared-utils')
module.exports = (options, context, api) => ({
@@ -7,7 +6,7 @@ module.exports = (options, context, api) => ({
// @internal/root-mixins
async clientDynamicModules () {
const builtInRootMixins = [
path.resolve(__dirname, '../app/root-mixins/updateMeta.js')
context.getLibFilePath('client/root-mixins/updateMeta.js')
]
const rootMixins = [
@@ -30,14 +30,23 @@ module.exports = (options, ctx) => ({
? `@import(${JSON.stringify(userStyle.replace(/[\\]+/g, '/'))})`
: ''
let styleContent = themeStyleContent + userStyleContent
const nullComment = '// null'
// user's styles can override theme's styles.
let styleContent = '// Theme\'s Styles\n'
+ (themeStyleContent || nullComment)
+ '\n\n// User\'s Styles\n'
+ (userStyleContent || nullComment)
if (themeAPI.existsParentTheme) {
const parentThemeStyle = path.resolve(themeAPI.parentTheme.path, 'styles/index.styl')
const parentThemeStyleContent = fs.existsSync(parentThemeStyle)
? `@import(${JSON.stringify(parentThemeStyle.replace(/[\\]+/g, '/'))})`
: ''
styleContent = parentThemeStyleContent + styleContent
styleContent = '// Parent Theme\'s Styles\n'
+ (parentThemeStyleContent || nullComment)
+ '\n\n' + styleContent
}
await writeTemp('style.styl', styleContent)
@@ -11,7 +11,7 @@ module.exports = (options, ctx) => ({
async clientDynamicModules () {
const files = [
path.resolve(__dirname, '../prepare/ClientComputedMixin.js')
path.resolve(__dirname, '../ClientComputedMixin.js')
]
const modules = await Promise.all(files.map(async file => {
@@ -11,7 +11,7 @@ const {
datatypes: { isString },
logger, chalk
} = require('@vuepress/shared-utils')
const ThemeAPI = require('../theme-api')
const ThemeAPI = require('./theme-api')
/**
* Resolve theme.
@@ -32,36 +32,36 @@ const ThemeAPI = require('../theme-api')
module.exports = function loadTheme (ctx) {
const themeResolver = getThemeResolver()
const theme = resolveTheme(ctx, themeResolver)
if (!theme.path) {
throw new Error(`[vuepress] You must specify a theme, or create a local custom theme. \n For more details, refer to https://vuepress.vuejs.org/guide/custom-themes.html#custom-themes. \n`)
throw new Error(
'[vuepress] You must specify a theme, or create a local custom theme. \n'
+ 'For more details, refer to https://vuepress.vuejs.org/guide/custom-themes.html#custom-themes. \n'
)
}
logger.tip(`Apply theme ${chalk.gray(theme.name)}`)
let applyTip = `Apply theme ${chalk.magenta(theme.name)}`
theme.entry.name = '@vuepress/internal-theme-entry-file'
let parentTheme = {}
if (theme.entry.extend) {
parentTheme = resolveTheme(ctx, themeResolver, true, theme.entry.extend)
parentTheme.entry.name = '@vuepress/internal-parent-theme-entry-file'
applyTip += chalk.gray(` (extends ${chalk.magenta(parentTheme.name)})`)
}
logger.tip(applyTip + ' ...')
logger.debug('theme', theme.name, theme.path)
logger.debug('parentTheme', parentTheme.name, parentTheme.path)
return new ThemeAPI(theme, parentTheme, ctx)
}
function normalizeThemePath (resolved) {
const { entry, name, fromDep } = resolved
const { entry, fromDep } = resolved
if (fromDep) {
const pkgPath = require.resolve(name)
let packageRootDir = parse(pkgPath).dir
// For those cases that "main" field was set to non-index file
// e.g. `layouts/Layout.vue`
while (!fs.existsSync(`${packageRootDir}/package.json`)) {
packageRootDir = resolve(packageRootDir, '..')
}
return packageRootDir
return parse(require.resolve(entry)).dir
} else if (entry.endsWith('.js') || entry.endsWith('.vue')) {
return parse(entry).dir
} else {
@@ -70,16 +70,18 @@ function normalizeThemePath (resolved) {
}
function resolveTheme (ctx, resolver, ignoreLocal, theme) {
const { siteConfig, cliOptions, sourceDir, vuepressDir, pluginAPI } = ctx
const { siteConfig, options, sourceDir, vuepressDir, pluginAPI } = ctx
const localThemePath = resolve(vuepressDir, 'theme')
theme = theme || siteConfig.theme || cliOptions.theme
theme = theme || siteConfig.theme || options.theme
let path
let name
let shortcut
let entry = {}
// 1. From local
/**
* 1. From `.vuepress/theme` directory.
*/
if (!ignoreLocal
&& !fs.existsSync(theme)
&& fs.existsSync(localThemePath)
@@ -89,15 +91,31 @@ function resolveTheme (ctx, resolver, ignoreLocal, theme) {
name = shortcut = 'local'
logger.tip(`Apply local theme at ${chalk.gray(path)}...`)
// 2. From dep
/**
* 2. From deps or custom local path.
* - vuepress-plugin-foo
* - /path/to/a-theme/index.js
*/
} else if (isString(theme)) {
/**
* To let theme resolver get the correct theme name.
*/
if (theme.endsWith('/index.js')) {
theme = theme.replace(/\/index\.js$/, '')
}
const resolved = resolver.resolve(theme, sourceDir)
if (resolved.entry === null) {
throw new Error(`Cannot resolve theme: ${theme}.`)
}
path = normalizeThemePath(resolved)
name = resolved.name
shortcut = resolved.shortcut
/**
* 3. fallback
*/
} else {
return {}
}
@@ -5,12 +5,12 @@
*/
const instantiateOption = require('./override/instantiateOption')
const { flattenPlugin, normalizePluginsConfig } = require('./util')
const { flattenPlugin } = require('./util')
const { PLUGIN_OPTION_MAP } = require('./constants')
const {
moduleResolver: { getPluginResolver },
datatypes: { assertTypes, isPlainObject },
logger, chalk
logger, chalk, normalizeConfig
} = require('@vuepress/shared-utils')
/**
@@ -22,6 +22,7 @@ module.exports = class PluginAPI {
this.options = {}
this._pluginContext = context
this._pluginQueue = []
this._loggedPlugins = []
this._initialized = false
this._pluginResolver = getPluginResolver()
this.initializeOptions(PLUGIN_OPTION_MAP)
@@ -88,6 +89,7 @@ module.exports = class PluginAPI {
plugin = this.normalizePlugin(pluginRaw, pluginOptions)
} catch (e) {
logger.warn(e.message)
return this
}
}
@@ -134,7 +136,7 @@ module.exports = class PluginAPI {
*/
useByPluginsConfig (pluginsConfig) {
pluginsConfig = normalizePluginsConfig(pluginsConfig)
pluginsConfig = normalizeConfig(pluginsConfig)
pluginsConfig.forEach(([pluginRaw, pluginOptions]) => {
this.use(pluginRaw, pluginOptions)
})
@@ -213,8 +215,11 @@ module.exports = class PluginAPI {
beforeDevServer,
afterDevServer
}) {
const isInternalPlugin = pluginName.startsWith('@vuepress/internal-')
logger[isInternalPlugin ? 'debug' : 'tip'](pluginLog(pluginName, shortcut))
if (!this._loggedPlugins.includes(pluginName)) {
const isInternalPlugin = pluginName.startsWith('@vuepress/internal-')
logger[isInternalPlugin ? 'debug' : 'tip'](pluginLog(pluginName, shortcut))
this._loggedPlugins.push(pluginName)
}
this
.registerOption(PLUGIN_OPTION_MAP.READY.key, ready, pluginName)
@@ -237,6 +242,50 @@ module.exports = class PluginAPI {
.registerOption(PLUGIN_OPTION_MAP.BEFORE_DEV_SERVER.key, beforeDevServer, pluginName)
.registerOption(PLUGIN_OPTION_MAP.AFTER_DEV_SERVER.key, afterDevServer, pluginName)
}
/**
* Apply synchronous option.
*
* @param {string} name
* @param {Array<any>} args
* @returns {void}
* @api public
*/
applySyncOption (name, ...args) {
logger.debug('applySyncOption: ' + name)
this.getOption(name).apply(...args)
return this
}
/**
* Apply asynchronous option.
*
* @param {string} name
* @param {Array<any>} args
* @returns {Promise<void>}
* @api public
*/
async applyAsyncOption (name, ...args) {
logger.debug('applyAsyncOption: ' + name)
await this.getOption(name).apply(...args)
}
/**
* Get exisiting option
*
* @param name
* @returns {Option}
* @api public
*/
getOption (name) {
if (!this.options[name]) {
throw new Error(`Unknown option ${name}`)
}
return this.options[name]
}
}
function pluginLog (name, shortcut) {

Some files were not shown because too many files have changed in this diff Show More