├── .editorconfig
├── .gitattributes
├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── PULL_REQUEST_TEMPLATE.md
├── issue_label_bot.yaml
└── workflows
│ └── ci.yml
├── .gitignore
├── .npmrc
├── .prettierrc
├── .vscode
├── launch.json
└── settings.json
├── CONTRIBUTING.md
├── KNOWN_ISSUES.md
├── LICENSE
├── README.md
├── circle.yml
├── examples
├── extract-css
│ ├── README.md
│ ├── css
│ │ └── global.css
│ ├── package.json
│ ├── pages
│ │ ├── about.vue
│ │ └── index.vue
│ ├── saber-browser.js
│ └── saber-config.js
└── vue-composition-api
│ ├── README.md
│ ├── package.json
│ ├── pages
│ └── index.vue
│ └── saber-browser.js
├── lerna.json
├── other-packages
├── eslint-config-saber
│ ├── CHANGELOG.md
│ ├── index.js
│ └── package.json
└── extract-sfc-blocks
│ ├── CHANGELOG.md
│ ├── index.js
│ └── package.json
├── package.json
├── packages
├── create-blog
│ ├── CHANGELOG.md
│ ├── LICENSE
│ ├── cli.js
│ └── package.json
├── create-site
│ ├── CHANGELOG.md
│ ├── LICENSE
│ ├── cli.js
│ ├── package.json
│ └── template
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── pages
│ │ ├── _posts
│ │ │ ├── my-example-post.md
│ │ │ ├── super-long-article.md
│ │ │ └── super-short-article.md
│ │ ├── about.md
│ │ └── index.md
│ │ ├── public
│ │ └── favicon.ico
│ │ └── saber-config.yml
├── saber-highlight-css
│ ├── CHANGELOG.md
│ ├── LICENSE
│ ├── default.css
│ └── package.json
├── saber-highlighter-prism
│ ├── CHANGELOG.md
│ ├── LICENSE
│ ├── README.md
│ ├── index.js
│ ├── loadLanguages.js
│ └── package.json
├── saber-log
│ ├── CHANGELOG.md
│ ├── LICENSE
│ ├── package.json
│ ├── rollup.config.js
│ ├── src
│ │ ├── colors.ts
│ │ └── index.ts
│ └── tsconfig.json
├── saber-markdown
│ ├── CHANGELOG.md
│ ├── LICENSE
│ ├── README.md
│ ├── babel-plugin-vue-features.js
│ ├── package.json
│ ├── rollup.config.js
│ └── src
│ │ └── index.js
├── saber-plugin-code-copy
│ ├── CHANGELOG.md
│ ├── LICENSE
│ ├── README.md
│ ├── lib
│ │ ├── index.js
│ │ └── saber-browser.js
│ └── package.json
├── saber-plugin-feed
│ ├── CHANGELOG.md
│ ├── LICENSE
│ ├── README.md
│ ├── example
│ │ ├── pages
│ │ │ └── _posts
│ │ │ │ ├── bye-world.md
│ │ │ │ └── hello-world.md
│ │ └── saber-config.yml
│ ├── lib
│ │ ├── index.js
│ │ ├── saber-browser.js
│ │ └── utils.js
│ └── package.json
├── saber-plugin-git-modification-time
│ ├── CHANGELOG.md
│ ├── LICENSE
│ ├── README.md
│ ├── lib
│ │ └── index.js
│ └── package.json
├── saber-plugin-google-analytics
│ ├── CHANGELOG.md
│ ├── LICENSE
│ ├── README.md
│ ├── index.js
│ ├── package.json
│ └── saber-browser.js
├── saber-plugin-image
│ ├── CHANGELOG.md
│ ├── LICENSE
│ ├── README.md
│ ├── lib
│ │ ├── index.js
│ │ ├── saber-browser.js
│ │ └── styles.module.css
│ └── package.json
├── saber-plugin-meta-redirect
│ ├── CHANGELOG.md
│ ├── LICENSE
│ ├── README.md
│ ├── example
│ │ ├── pages
│ │ │ ├── about.md
│ │ │ └── index.md
│ │ ├── saber-config.yml
│ │ └── saber-node.js
│ ├── lib
│ │ └── index.js
│ └── package.json
├── saber-plugin-netlify-redirect
│ ├── CHANGELOG.md
│ ├── LICENSE
│ ├── README.md
│ ├── example
│ │ ├── pages
│ │ │ ├── about.md
│ │ │ └── index.md
│ │ ├── public
│ │ │ └── _redirects
│ │ ├── saber-config.yml
│ │ └── saber-node.js
│ ├── lib
│ │ └── index.js
│ └── package.json
├── saber-plugin-prismjs
│ ├── CHANGELOG.md
│ ├── LICENSE
│ ├── README.md
│ ├── index.js
│ └── package.json
├── saber-plugin-pwa
│ ├── CHANGELOG.md
│ ├── LICENSE
│ ├── README.md
│ ├── lib
│ │ ├── create-element.js
│ │ ├── generate-manifest.js
│ │ ├── get-app-config.js
│ │ ├── index.js
│ │ ├── noop-sw-middleware.js
│ │ └── saber-browser.js
│ └── package.json
├── saber-plugin-query-posts
│ ├── CHANGELOG.md
│ ├── LICENSE
│ ├── README.md
│ ├── lib
│ │ ├── index.js
│ │ └── utils.js
│ └── package.json
├── saber-plugin-search
│ ├── CHANGELOG.md
│ ├── LICENSE
│ ├── README.md
│ ├── lib
│ │ ├── index.js
│ │ └── saber-browser.js
│ └── package.json
├── saber-plugin-transformer-html
│ ├── CHANGELOG.md
│ ├── LICENSE
│ ├── README.md
│ ├── example
│ │ ├── pages
│ │ │ └── index.html
│ │ └── saber-config.yml
│ ├── lib
│ │ └── index.js
│ └── package.json
├── saber-plugin-transformer-pug
│ ├── CHANGELOG.md
│ ├── LICENSE
│ ├── README.md
│ ├── example
│ │ ├── pages
│ │ │ ├── about.vue
│ │ │ └── index.pug
│ │ └── saber-config.yml
│ ├── lib
│ │ ├── index.js
│ │ └── pug-plain-loader.js
│ └── package.json
├── saber-utils
│ ├── CHANGELOG.md
│ ├── LICENSE
│ ├── build.js
│ ├── index.d.ts
│ ├── index.js
│ ├── package.json
│ └── src
│ │ ├── fs.js
│ │ ├── glob.js
│ │ └── isAbsoluteUrl.js
└── saber
│ ├── .eslintignore
│ ├── .eslintrc.js
│ ├── CHANGELOG.md
│ ├── LICENSE
│ ├── babel.js
│ ├── declarations.d.ts
│ ├── example
│ ├── pages
│ │ ├── _bar
│ │ │ └── hehe.md
│ │ ├── _foo.vue
│ │ ├── _posts
│ │ │ ├── bar.md
│ │ │ └── foo.md
│ │ ├── about.md
│ │ ├── hehe.css
│ │ ├── hehe.module.css
│ │ ├── hehe.vue
│ │ ├── index.md
│ │ └── wow.js
│ ├── public
│ │ └── test.txt
│ ├── saber-browser.js
│ ├── saber-config.json
│ ├── saber-node.js
│ └── src
│ │ └── sum.js
│ ├── jest.config.js
│ ├── package.json
│ ├── src
│ ├── BrowserApi.ts
│ ├── Compiler.ts
│ ├── Pages.ts
│ ├── Transformers.ts
│ ├── WebpackUtils.ts
│ ├── babel
│ │ └── preset.js
│ ├── cli-commands
│ │ ├── build.js
│ │ ├── dev.js
│ │ ├── eject-theme.js
│ │ ├── index.js
│ │ ├── serve.js
│ │ └── utils.js
│ ├── cli.js
│ ├── config-chain
│ │ ├── Options.js
│ │ ├── Plugin.js
│ │ └── index.js
│ ├── hooks.ts
│ ├── index.ts
│ ├── markdown
│ │ ├── __test__
│ │ │ ├── __snapshots__
│ │ │ │ └── highlight-plugin.test.js.snap
│ │ │ ├── create-env.js
│ │ │ ├── excerpt-plugin.test.js
│ │ │ ├── headings-plugin.test.js
│ │ │ └── highlight-plugin.test.js
│ │ ├── escape-interpolations-plugin.js
│ │ ├── excerpt-plugin.js
│ │ ├── headings-plugin.js
│ │ ├── highlight-plugin.js
│ │ ├── hoist-tags-plugin.js
│ │ └── task-list-plugin.js
│ ├── plugins
│ │ ├── config-css.js
│ │ ├── config-font.js
│ │ ├── config-image.js
│ │ ├── config-other-loaders.js
│ │ ├── emit-runtime-polyfills.js
│ │ ├── emit-saber-variables.js
│ │ ├── extend-browser-api.js
│ │ ├── extend-node-api.js
│ │ ├── index.js
│ │ ├── layouts.ts
│ │ ├── source-pages.ts
│ │ ├── transformer-components.js
│ │ ├── transformer-default.js
│ │ ├── transformer-markdown.js
│ │ └── watch-config.ts
│ ├── utils
│ │ ├── __test__
│ │ │ ├── getPermalink.test.js
│ │ │ └── resolvePackage.test.js
│ │ ├── assetsAttribute.ts
│ │ ├── configLoader.js
│ │ ├── getFileNames.js
│ │ ├── getPageType.js
│ │ ├── getPermalink.js
│ │ ├── inspectWebpack.js
│ │ ├── parseAttributes.js
│ │ ├── parseFrontmatter.js
│ │ ├── prettyBytes.js
│ │ ├── resolvePackage.js
│ │ ├── serveDir.js
│ │ ├── toml.min.js
│ │ ├── validateConfig.ts
│ │ └── yaml.min.js
│ ├── vue-renderer
│ │ ├── get-initial-document-data.js
│ │ ├── get-initial-document.js
│ │ ├── index.js
│ │ ├── page-prop-loader.js
│ │ ├── render-html.js
│ │ ├── saber-page-loader.js
│ │ ├── template-plugins
│ │ │ ├── __test__
│ │ │ │ └── link.test.js
│ │ │ ├── index.js
│ │ │ └── link.js
│ │ ├── transform-template-loader.js
│ │ └── utils.js
│ └── webpack
│ │ ├── PrintStatusPlugin.js
│ │ ├── babel-loader.js
│ │ ├── toml-loader.js
│ │ ├── webpack.config.js
│ │ └── yaml-loader.js
│ ├── tsconfig.json
│ └── vue-app
│ ├── 404.vue
│ ├── components
│ ├── ClientOnly.js
│ ├── LayoutManager.vue
│ └── SaberLink.js
│ ├── create-app.js
│ ├── dev-client.js
│ ├── entry-client.js
│ ├── entry-server.js
│ ├── helpers
│ ├── inject-config.js
│ ├── path.js
│ ├── scroll-handler.js
│ └── set-transition.js
│ ├── polyfills.js
│ ├── router.js
│ ├── theme
│ └── layouts
│ │ └── default.vue
│ ├── utils
│ └── is-absolute-url.js
│ └── vendor
│ └── promise.js
├── scripts
└── release-pr.js
├── tsconfig.base.json
├── website
├── images
│ ├── gh-pages-setting.png
│ ├── logo-square.jpeg
│ ├── logo.png
│ └── simple-index-md-page.png
├── package.json
├── pages
│ ├── _posts
│ │ ├── html-and-pug-page
│ │ │ ├── 1.webp
│ │ │ ├── 2.webp
│ │ │ ├── 3.gif
│ │ │ ├── 4.webp
│ │ │ └── index.md
│ │ ├── integrate-netlify-forms
│ │ │ └── index.md
│ │ └── saber
│ │ │ ├── index.md
│ │ │ └── size-compare.png
│ ├── blog
│ │ └── index.md
│ ├── docs
│ │ ├── browser-apis.md
│ │ ├── components.md
│ │ ├── css-modules.md
│ │ ├── css-preprocessors.md
│ │ ├── deployment.md
│ │ ├── i18n.md
│ │ ├── images-fonts-and-files.md
│ │ ├── index.md
│ │ ├── installation.md
│ │ ├── layouts.md
│ │ ├── manipulating-head.md
│ │ ├── markdown-features.md
│ │ ├── node-apis.md
│ │ ├── page-interface.md
│ │ ├── page-transition.md
│ │ ├── pages.md
│ │ ├── permalinks.md
│ │ ├── plugin-api.md
│ │ ├── postcss.md
│ │ ├── project-structure.md
│ │ ├── routing.md
│ │ ├── saber-config.md
│ │ ├── saber-instance.md
│ │ ├── static-folder.md
│ │ ├── themes.md
│ │ ├── using-vue-in-markdown.md
│ │ └── working-with-webpack.md
│ ├── index.vue
│ ├── themes
│ │ ├── _themes.yml
│ │ ├── index.vue
│ │ └── previews
│ │ │ ├── saber-theme-minima.png
│ │ │ └── saber-theme-tailsaw.png
│ └── tutorial
│ │ ├── images
│ │ ├── about.png
│ │ ├── first-page.png
│ │ ├── hello-world.png
│ │ ├── navbar-dumb.gif
│ │ ├── not-found.png
│ │ ├── post-list.png
│ │ ├── prev-next-post.gif
│ │ └── vue-devtools-page-prop.png
│ │ └── tutorial.md
├── postcss.config.js
├── saber-config.js
├── saber-node.js
├── src
│ ├── components
│ │ ├── Header.vue
│ │ ├── Logo.vue
│ │ ├── PostList.vue
│ │ ├── PostMeta.vue
│ │ ├── Search.vue
│ │ ├── Sidebar.vue
│ │ ├── SiteNav.vue
│ │ ├── SiteSearch.vue
│ │ ├── Toc.vue
│ │ ├── Vercel.vue
│ │ └── Wrap.vue
│ ├── css
│ │ ├── button.css
│ │ ├── global.css
│ │ ├── page.css
│ │ └── prism.css
│ ├── layouts
│ │ ├── blog.vue
│ │ ├── docs.vue
│ │ ├── index.vue
│ │ ├── post.vue
│ │ └── tutorial.vue
│ ├── mixins
│ │ └── doc.js
│ └── saber-browser.js
├── static
│ ├── _redirects
│ ├── img
│ │ ├── icons
│ │ │ ├── icon_128x128.png
│ │ │ ├── icon_16x16.png
│ │ │ ├── icon_256x256.png
│ │ │ ├── icon_32x32.png
│ │ │ └── icon_512x512.png
│ │ └── vercel-logo.svg
│ └── manifest.json
└── yarn.lock
└── yarn.lock
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = space
5 | indent_size = 2
6 | end_of_line = lf
7 | charset = utf-8
8 | trim_trailing_whitespace = true
9 | insert_final_newline = true
10 |
11 | [*.md]
12 | trim_trailing_whitespace = false
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto eol=lf
2 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | patreon: egoist
4 | issuehunt: saberland
5 | ko_fi: support_egoist
6 | open_collective: saber
7 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | ---
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | ## Bug report
15 |
16 | #### Steps to reproduce
17 |
18 |
19 |
20 |
21 |
22 | #### What is expected?
23 |
24 | #### What is actually happening?
25 |
26 | #### Other relevant information
27 |
28 | - Result of running `saber -v`:
29 | - Browser version (optional):
30 | - Is Saber a global or local install?
31 | - Which package manager did you use for the install?
32 | - Does this issue occur when all plugins are disabled?
33 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 |
5 | ---
6 |
7 |
8 |
9 |
10 | ## Feature request
11 |
12 |
13 |
14 |
15 |
16 | #### What problem does this feature solve?
17 |
18 | #### What does the proposed API look like?
19 |
20 | #### How should this be implemented in your opinion?
21 |
22 | #### Are you willing to work on this yourself?
23 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | **Summary**
7 |
8 |
9 |
10 |
11 | **What kind of change does this PR introduce?** (check at least one)
12 |
13 | - [ ] Bugfix
14 | - [ ] Feature
15 | - [ ] Code style update
16 | - [ ] Refactor
17 | - [ ] Docs
18 | - [ ] Build-related changes
19 | - [ ] Other, please describe:
20 |
21 | If changing the UI/CSS related code, please provide the **before/after** screenshot:
22 |
23 | **Does this PR introduce a breaking change?** (check one)
24 |
25 | - [ ] Yes
26 | - [ ] No
27 |
28 | If yes, please describe the impact and migration path for existing applications:
29 |
30 | **The PR fulfills these requirements:**
31 |
32 | - [ ] Briefly describing what this PR does in the title in [Angular Commit Message Conventions](https://git.io/fNGDG), e.g. `fix: rebuild when a page is added`
33 |
34 | To avoid wasting your time, it's best to open a **feature request issue** first and wait for approval before working on it.
35 |
36 | **Other information:**
37 |
--------------------------------------------------------------------------------
/.github/issue_label_bot.yaml:
--------------------------------------------------------------------------------
1 | label-alias:
2 | bug: 'type: bug'
3 | feature_request: 'type: feature'
4 | question: 'question'
5 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: Node CI
2 |
3 | on:
4 | pull_request:
5 | push:
6 | branches:
7 | - master
8 | paths-ignore:
9 | - '**.md'
10 |
11 | jobs:
12 | test:
13 | name: Test on ${{ matrix.os }}
14 | runs-on: ${{ matrix.os }}
15 |
16 | strategy:
17 | matrix:
18 | os: [ubuntu-latest, windows-latest]
19 |
20 | steps:
21 | - uses: actions/checkout@v2.1.0
22 |
23 | - name: Use Node.js 12.x
24 | uses: actions/setup-node@v1.4.1
25 | with:
26 | node-version: 12.x
27 |
28 | - name: Get yarn cache directory path
29 | id: yarn-cache-dir-path
30 | run: echo "::set-output name=dir::$(yarn cache dir)"
31 |
32 | - uses: actions/cache@v1
33 | id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
34 | with:
35 | path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
36 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
37 | restore-keys: |
38 | ${{ runner.os }}-yarn-
39 |
40 | - name: Install dependencies
41 | run: yarn
42 |
43 | - name: Test
44 | run: yarn test
45 | env:
46 | CI: true
47 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .saber
3 | dist
4 | public
5 | .DS_Store
6 | examples/*/yarn.lock
7 | examples/*/package-lock.json
8 | packages/*/types
9 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | registry=https://registry.npmjs.org
2 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "semi": false,
3 | "singleQuote": true,
4 | "bracketSpacing": true
5 | }
6 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "type": "node",
9 | "request": "launch",
10 | "name": "Debug Saber with website",
11 | "program": "${workspaceFolder}/packages/saber/lib/cli.js",
12 | "cwd": "${workspaceFolder}/website/"
13 | }
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "eslint.validate": ["javascript", "javascriptreact", "typescript"],
3 | "eslint.enable": true,
4 | "eslint.workingDirectories": [
5 | {
6 | "directory": "packages/saber",
7 | "changeProcessCWD": true
8 | }
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to Saber
2 |
3 | ## Set up Saber locally
4 |
5 | Fork this project, then:
6 |
7 | ```bash
8 | git clone git@github.com:$USER/saber.git
9 | cd saber
10 |
11 | # Install dependencies
12 | yarn
13 |
14 | # Link the `saber` binary if you want to run it globally
15 | cd packages/saber
16 | yarn link
17 |
18 | # If you wanna run the website locally
19 | cd website
20 | yarn dev # which uses the global `saber` command
21 | ```
22 |
23 | Visual Studio Code users can take advantage of the debug configuration shipping with Saber, which allows testing Saber with the documentation website. If you want to debug with a different project, set `cwd` under `.vscode/launch.json` to your Saber project's working directory.
24 |
25 | ## Pull Requests
26 |
27 | Before starting to work on an issue, first ensure there are no open PRs for it already, then comment that you intend to work on it, to prevent others from wasting their time doing the same work.
28 |
29 | If the issue you're working on is funded by IssueHunt, you also need to submit the PR URL to IssueHunt after submitting your PR on GitHub.
30 |
31 | ## Publish a new version
32 |
33 | If you have write access to this project:
34 |
35 | ```bash
36 | # In root directory
37 | # Analyze git history, create git tag and update upstream
38 | yarn lerna version --conventional-commits
39 | # Publish to npm
40 | yarn lerna publish from-git
41 | ```
42 |
--------------------------------------------------------------------------------
/KNOWN_ISSUES.md:
--------------------------------------------------------------------------------
1 | # Known Issues
2 |
3 | ## Inline Critical CSS
4 |
5 | If you're using a `.js` file as page component, the CSS imported there won't be inlined in the initial HTML sent by the server.
6 |
7 | Inlining Critical CSS only works with `
14 |
--------------------------------------------------------------------------------
/examples/extract-css/pages/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
--------------------------------------------------------------------------------
/examples/extract-css/saber-browser.js:
--------------------------------------------------------------------------------
1 | import './css/global.css'
2 |
--------------------------------------------------------------------------------
/examples/extract-css/saber-config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | build: {
3 | extractCSS: true
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/examples/vue-composition-api/README.md:
--------------------------------------------------------------------------------
1 | # vue-composition-api
2 |
3 | With `@vue/composition-api` you can enjoy the upcoming Vue 3.x feature in Vue 2.x right away.
4 |
5 | ## How to run
6 |
7 | ```bash
8 | git clone https://github.com/saberland/saber.git --single-branch
9 | cd saber/examples/vue-composition-api
10 | yarn
11 | yarn dev
12 | ```
13 |
--------------------------------------------------------------------------------
/examples/vue-composition-api/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "name": "vue-composition-api",
4 | "description": "How to use the module @vue/composition-api with Saber",
5 | "scripts": {
6 | "dev": "saber",
7 | "build": "saber build"
8 | },
9 | "dependencies": {
10 | "saber": "^0.9.1",
11 | "@vue/composition-api": "^0.3.2"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/examples/vue-composition-api/pages/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{ state.count }} * 2 = {{ doubleCount }}
4 |
5 |
6 |
7 |
29 |
--------------------------------------------------------------------------------
/examples/vue-composition-api/saber-browser.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import VueCompositionApi from '@vue/composition-api'
3 |
4 | Vue.use(VueCompositionApi)
5 |
--------------------------------------------------------------------------------
/lerna.json:
--------------------------------------------------------------------------------
1 | {
2 | "packages": ["packages/*", "other-packages/*"],
3 | "version": "independent",
4 | "npmClient": "yarn"
5 | }
6 |
--------------------------------------------------------------------------------
/other-packages/eslint-config-saber/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 | All notable changes to this project will be documented in this file.
4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5 |
6 | ## [0.0.2](https://github.com/saberland/saber/compare/eslint-config-saber@0.0.1...eslint-config-saber@0.0.2) (2019-12-02)
7 |
8 | **Note:** Version bump only for package eslint-config-saber
9 |
10 | ## 0.0.1 (2019-11-03)
11 |
12 | **Note:** Version bump only for package eslint-config-saber
13 |
--------------------------------------------------------------------------------
/other-packages/eslint-config-saber/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | parser: '@typescript-eslint/parser',
3 | parserOptions: {
4 | warnOnUnsupportedTypeScriptVersion: false,
5 | sourceType: 'module',
6 | jsx: false,
7 | project: 'tsconfig.json'
8 | },
9 | extends: [
10 | 'eslint:recommended',
11 | 'plugin:@typescript-eslint/eslint-recommended',
12 | 'plugin:@typescript-eslint/recommended',
13 | 'plugin:@typescript-eslint/recommended-requiring-type-checking',
14 | 'plugin:prettier/recommended'
15 | ],
16 | env: {
17 | node: true,
18 | jest: true,
19 | es2017: true
20 | },
21 | rules: {
22 | // Enable this rule when all files are migated to TS
23 | '@typescript-eslint/no-var-requires': 'off',
24 | '@typescript-eslint/camelcase': 'off',
25 | // Enable this rule later, it explodes the terminal
26 | '@typescript-eslint/explicit-function-return-type': 'off',
27 | '@typescript-eslint/member-delimiter-style': [
28 | 'error',
29 | {
30 | multiline: {
31 | delimiter: 'none',
32 | requireLast: false
33 | },
34 | singleline: {
35 | delimiter: 'semi',
36 | requireLast: false
37 | }
38 | }
39 | ],
40 | '@typescript-eslint/prefer-includes': 'off'
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/other-packages/eslint-config-saber/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "eslint-config-saber",
3 | "version": "0.0.2",
4 | "description": "ESLint config for core Saber repos",
5 | "man": "index.js",
6 | "files": [
7 | "index.js"
8 | ],
9 | "license": "MIT",
10 | "dependencies": {
11 | "@typescript-eslint/eslint-plugin": "^2.6.0",
12 | "@typescript-eslint/parser": "^2.6.0",
13 | "eslint-config-prettier": "6.5.0",
14 | "eslint-plugin-prettier": "3.1.1"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/other-packages/extract-sfc-blocks/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 | All notable changes to this project will be documented in this file.
4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5 |
6 | ## [0.0.2](https://github.com/egoist/saber/compare/extract-sfc-blocks@0.0.1...extract-sfc-blocks@0.0.2) (2019-05-02)
7 |
8 | ### Bug Fixes
9 |
10 | - **deps:** update dependency stringify-attributes to v2 ([#125](https://github.com/egoist/saber/issues/125)) ([0465485](https://github.com/egoist/saber/commit/0465485))
11 |
12 | ## 0.0.1 (2019-04-27)
13 |
14 | ### Features
15 |
16 | - add saber-plugin-transformer-pug ([edc7891](https://github.com/egoist/saber/commit/edc7891))
17 |
--------------------------------------------------------------------------------
/other-packages/extract-sfc-blocks/index.js:
--------------------------------------------------------------------------------
1 | const posthtml = require('posthtml')
2 | const stringifyAttrs = require('stringify-attributes')
3 |
4 | function stringifyNode(node) {
5 | if (typeof node === 'string') {
6 | return node
7 | }
8 |
9 | const content = node.content || []
10 | return `<${node.tag}${stringifyAttrs(node.attrs || {})}>${content
11 | .map(n => stringifyNode(n))
12 | .join('')}${node.tag}>`
13 | }
14 |
15 | module.exports = input => {
16 | const blocks = []
17 | const { html } = posthtml([
18 | tree =>
19 | tree.walk(node => {
20 | if (node.tag === 'script' || node.tag === 'style') {
21 | blocks.push(stringifyNode(node))
22 | return
23 | }
24 |
25 | return node
26 | })
27 | ]).process(input, {
28 | sync: true,
29 | recognizeSelfClosing: true
30 | })
31 |
32 | return {
33 | html,
34 | blocks
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/other-packages/extract-sfc-blocks/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "extract-sfc-blocks",
3 | "version": "0.0.2",
4 | "description": "Extract Vue SFC blocks from HTML",
5 | "files": [
6 | "index.js"
7 | ],
8 | "main": "index.js",
9 | "license": "MIT",
10 | "dependencies": {
11 | "posthtml": "^0.11.3",
12 | "stringify-attributes": "^2.0.0"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "name": "saber",
4 | "scripts": {
5 | "test": "npm run lint && lerna run test",
6 | "lint": "lerna run lint",
7 | "commit": "git-cz",
8 | "prepare": "lerna run prepublishOnly"
9 | },
10 | "repository": {
11 | "url": "saberland/saber",
12 | "type": "git"
13 | },
14 | "author": "egoist<0x142857@gmail.com>",
15 | "license": "MIT",
16 | "devDependencies": {
17 | "commitizen": "4.0.3",
18 | "conf": "6.2.0",
19 | "cz-conventional-changelog": "3.0.2",
20 | "enquirer": "^2.3.1",
21 | "eslint": "^6.6.0",
22 | "husky": "3.0.9",
23 | "jest": "^24.9.0",
24 | "lerna": "3.18.3",
25 | "lint-staged": "8.1.6",
26 | "prettier": "1.18.2",
27 | "typedoc": "^0.15.0",
28 | "typescript": "^3.8.0-dev.20191031"
29 | },
30 | "resolutions": {
31 | "typedoc/typescript": "^3.8.0-dev.20191031"
32 | },
33 | "husky": {
34 | "hooks": {
35 | "pre-commit": "lint-staged"
36 | }
37 | },
38 | "lint-staged": {
39 | "linters": {
40 | "*.{json,md}": [
41 | "prettier --write",
42 | "git add"
43 | ]
44 | },
45 | "ignore": [
46 | "**/dist/**",
47 | "**/vendor/**",
48 | "*.min.js",
49 | "**/vue-renderer/app/**",
50 | "**/example/**",
51 | "website/**"
52 | ]
53 | },
54 | "workspaces": [
55 | "packages/*",
56 | "other-packages/*",
57 | "website"
58 | ],
59 | "config": {
60 | "commitizen": {
61 | "path": "cz-conventional-changelog"
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/packages/create-blog/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) EGOIST <0x142857@gmail.com> (https://github.com/egoist)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/create-blog/cli.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | require('create-site/cli')
3 |
--------------------------------------------------------------------------------
/packages/create-blog/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "create-blog",
3 | "version": "0.2.9",
4 | "bin": "cli.js",
5 | "files": [
6 | "cli.js"
7 | ],
8 | "dependencies": {
9 | "create-site": "^0.4.0"
10 | },
11 | "license": "MIT",
12 | "author": {
13 | "name": "EGOIST",
14 | "email": "0x142857@gmail.com"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/packages/create-site/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) EGOIST <0x142857@gmail.com> (https://github.com/egoist)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/create-site/cli.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | const path = require('path')
3 | const { spawnSync } = require('child_process')
4 | const { promisify } = require('util')
5 | const colors = require('kleur')
6 |
7 | const args = process.argv.slice(2)
8 |
9 | if (
10 | args.length === 0 ||
11 | ['-h', '--help'].some(helpFlag => args.includes(helpFlag))
12 | ) {
13 | console.log(
14 | `create-site v${require('./package').version}
15 |
16 | Usage: create-site
17 | `.trim()
18 | )
19 | process.exit(1)
20 | }
21 |
22 | if (args.some(arg => arg.startsWith('-'))) {
23 | console.log(`Invalid flag ${args.join('')}`)
24 | process.exit(1)
25 | }
26 |
27 | if (parseInt(process.versions.node, 10) < 8) {
28 | console.log(
29 | `Node.js ${process.versions.node} isn't supported, you need Node.js 8 or above.`
30 | )
31 | process.exit(1)
32 | }
33 |
34 | const dir = path.resolve(args[0])
35 |
36 | console.log(`Creating a new site...`)
37 |
38 | const { ncp } = require('ncp')
39 |
40 | let hasYarn = false
41 | try {
42 | spawnSync('yarn', ['--version'])
43 | hasYarn = true
44 | } catch (error) {}
45 |
46 | promisify(ncp)(path.join(__dirname, 'template'), dir)
47 | .then(() => {
48 | console.log(
49 | colors.green(`Successfully created at ${colors.underline(dir)}`)
50 | )
51 | console.log(colors.bold(`To start dev server, run:`))
52 | console.log(colors.cyan(`$ cd ${path.relative(process.cwd(), dir)}`))
53 | if (hasYarn) {
54 | console.log(colors.cyan(`$ yarn`))
55 | console.log(colors.cyan(`$ yarn dev`))
56 | } else {
57 | console.log(colors.cyan(`$ npm install`))
58 | console.log(colors.cyan(`$ npm run dev`))
59 | }
60 |
61 | console.log(colors.dim(`For more details, please check out the README.md`))
62 | })
63 | .catch(console.error)
64 |
--------------------------------------------------------------------------------
/packages/create-site/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "create-site",
3 | "version": "0.4.0",
4 | "bin": "cli.js",
5 | "files": [
6 | "cli.js",
7 | "template"
8 | ],
9 | "license": "MIT",
10 | "dependencies": {
11 | "kleur": "3.0.3",
12 | "ncp": "2.0.0"
13 | },
14 | "author": {
15 | "name": "EGOIST",
16 | "email": "0x142857@gmail.com"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/packages/create-site/template/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | *.log
3 | .saber
4 | public
5 |
--------------------------------------------------------------------------------
/packages/create-site/template/README.md:
--------------------------------------------------------------------------------
1 | # my-saber-blog
2 |
3 | ## Scripts
4 |
5 | ### `npm run dev`
6 |
7 | Run dev server at `http://localhost:3000`
8 |
9 | ### `npm run build`
10 |
11 | Build your website to `public` folder which you can deploy to GitHub Pages, Netlify or wherever you want.
12 |
--------------------------------------------------------------------------------
/packages/create-site/template/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "scripts": {
4 | "dev": "saber",
5 | "build": "saber build"
6 | },
7 | "devDependencies": {
8 | "saber": "latest",
9 | "saber-theme-minima": "latest",
10 | "saber-plugin-feed": "latest",
11 | "saber-plugin-query-posts": "latest"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/packages/create-site/template/pages/_posts/my-example-post.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: My Example Post
3 | date: 2016-05-20
4 | layout: post
5 | ---
6 |
7 | Eos eu docendi tractatos sapientem, brute option menandri in vix, quando vivendo accommodare te ius. Nec melius fastidii constituam id, viderer theophrastus ad sit, hinc semper periculis cum id. Noluisse postulant assentior est in, no choro sadipscing repudiandae vix. Vis in euismod delenit dignissim. Ex quod nostrum sit, suas decore animal id ius, nobis solet detracto quo te.
8 |
9 | No laudem altera adolescens has, volumus lucilius eum no. Eam ei nulla audiam efficiantur. Suas affert per no, ei tale nibh sea. Sea ne magna harum, in denique scriptorem sea, cetero alienum tibique ei eos. Labores persequeris referrentur eos ei.
10 |
--------------------------------------------------------------------------------
/packages/create-site/template/pages/_posts/super-short-article.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: 'Some articles are just so short that we have to make the footer stick'
3 | date: 2016-05-19
4 | layout: post
5 | ---
6 |
7 | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
8 |
--------------------------------------------------------------------------------
/packages/create-site/template/pages/about.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: About
3 | layout: page
4 | ---
5 |
6 | This is the Saber port of the base Jekyll theme. Check out the [GitHub project](https://github.com/egoist/saber-theme-minima) for detailed usages.
7 |
8 | You can find out more info about customizing your theme, as well as basic Saber usage documentation at https://saber.land
9 |
--------------------------------------------------------------------------------
/packages/create-site/template/pages/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | # Fallback to `default` layout if `index` is not found
3 | layout: index
4 | # Inject post list as `page.posts` (by saber-plugin-query-posts)
5 | injectAllPosts: true
6 | ---
7 |
--------------------------------------------------------------------------------
/packages/create-site/template/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saberland/saber/e63d22041a9397bec38468caadec6bd7b923d8b6/packages/create-site/template/public/favicon.ico
--------------------------------------------------------------------------------
/packages/create-site/template/saber-config.yml:
--------------------------------------------------------------------------------
1 | theme: minima
2 |
3 | siteConfig:
4 | url: https://example.com
5 | author: Author of This Site
6 | email: author@your-domain.com
7 | description: Write an awesome description for your new site here. You can edit this line in saber-config.yml. It will appear in your document head meta (for Google search results) site description.
8 |
9 | themeConfig:
10 | nav:
11 | - text: Home
12 | link: /
13 | - text: About
14 | link: /about.html
15 | social:
16 | twitter: saber_land
17 | github: egoist
18 | rss: true
19 |
20 | plugins:
21 | - resolve: saber-plugin-query-posts
22 | - resolve: saber-plugin-feed
23 | options:
24 | atomFeed: true
25 |
--------------------------------------------------------------------------------
/packages/saber-highlight-css/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) EGOIST <0x142857@gmail.com> (https://github.com/egoist)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/saber-highlight-css/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "saber-highlight-css",
3 | "version": "0.1.2",
4 | "description": "Default CSS for code highlighting in Saber",
5 | "files": [
6 | "*.css"
7 | ],
8 | "license": "MIT",
9 | "peerDependencies": {
10 | "saber": ">=0.7.0"
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/packages/saber-highlighter-prism/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) EGOIST <0x142857@gmail.com> (https://github.com/egoist)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/saber-highlighter-prism/README.md:
--------------------------------------------------------------------------------
1 | Don't use this directly, you should use [saber-plugin-prismjs](../saber-plugin-prismjs) instead.
2 |
--------------------------------------------------------------------------------
/packages/saber-highlighter-prism/index.js:
--------------------------------------------------------------------------------
1 | const Prism = require('prismjs')
2 | const { log } = require('saber-log')
3 | const loadLanguages = require('./loadLanguages')
4 |
5 | const languageAlias = {
6 | vue: 'html',
7 | sh: 'bash',
8 | styl: 'stylus'
9 | }
10 |
11 | module.exports = (code, lang) => {
12 | if (!lang) return Prism.highlight(code, {})
13 |
14 | lang = lang.toLowerCase()
15 |
16 | if (lang in languageAlias) {
17 | lang = languageAlias[lang]
18 | }
19 |
20 | if (!Prism.languages[lang]) {
21 | try {
22 | loadLanguages(lang)
23 | } catch (error) {
24 | log.warn(error.message)
25 | return Prism.highlight(code, {})
26 | }
27 | }
28 |
29 | const grammer = Prism.languages[lang]
30 |
31 | return Prism.highlight(code, grammer, lang)
32 | }
33 |
--------------------------------------------------------------------------------
/packages/saber-highlighter-prism/loadLanguages.js:
--------------------------------------------------------------------------------
1 | const Prism = require(`prismjs`)
2 | const prismComponents = require(`prismjs/components`)
3 |
4 | // Get the real name of a language given it or an alias
5 | const getBaseLanguageName = (nameOrAlias, components = prismComponents) => {
6 | if (components.languages[nameOrAlias]) {
7 | return nameOrAlias
8 | }
9 |
10 | return Object.keys(components.languages).find(language => {
11 | const { alias } = components.languages[language]
12 | if (!alias) return false
13 | if (Array.isArray(alias)) {
14 | return alias.includes(nameOrAlias)
15 | }
16 |
17 | return alias === nameOrAlias
18 | })
19 | }
20 |
21 | module.exports = function loadPrismLanguage(language) {
22 | const baseLanguage = getBaseLanguageName(language)
23 |
24 | if (!baseLanguage) {
25 | throw new Error(`Prism doesn't support language '${language}'.`)
26 | }
27 |
28 | if (Prism.languages[baseLanguage]) {
29 | // Don't load already loaded language
30 | return
31 | }
32 |
33 | const languageData = prismComponents.languages[baseLanguage]
34 |
35 | if (languageData.option === `default`) {
36 | // Default language has already been loaded by Prism
37 | return
38 | }
39 |
40 | if (languageData.require) {
41 | // Load the required language first
42 | if (Array.isArray(languageData.require)) {
43 | languageData.require.forEach(loadPrismLanguage)
44 | } else {
45 | loadPrismLanguage(languageData.require)
46 | }
47 | }
48 |
49 | require(`prismjs/components/prism-${baseLanguage}.js`)
50 | }
51 |
52 | /* Exposed for unit testing */
53 | module.exports.getBaseLanguageName = getBaseLanguageName
54 |
--------------------------------------------------------------------------------
/packages/saber-highlighter-prism/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "saber-highlighter-prism",
3 | "version": "0.3.4",
4 | "license": "MIT",
5 | "files": [
6 | "index.js",
7 | "loadLanguages.js"
8 | ],
9 | "main": "index.js",
10 | "dependencies": {
11 | "saber-log": "^0.3.1"
12 | },
13 | "devDependencies": {
14 | "prismjs": "1.17.1"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/packages/saber-log/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 | All notable changes to this project will be documented in this file.
4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5 |
6 | ## [0.3.1](https://github.com/saberland/saber/compare/saber-log@0.3.0...saber-log@0.3.1) (2019-11-03)
7 |
8 | **Note:** Version bump only for package saber-log
9 |
10 | # [0.3.0](https://github.com/saberland/saber/compare/saber-log@0.2.1...saber-log@0.3.0) (2019-09-11)
11 |
12 | ### Features
13 |
14 | - **cli:** tweak logger style ([d90025d](https://github.com/saberland/saber/commit/d90025d))
15 |
16 | ## [0.2.1](https://github.com/saberland/saber/compare/saber-log@0.2.0...saber-log@0.2.1) (2019-08-22)
17 |
18 | ### Bug Fixes
19 |
20 | - **pkg:** add missing LICENSE in leaf packages ([9f73603](https://github.com/saberland/saber/commit/9f73603))
21 |
22 | # [0.2.0](https://github.com/egoist/saber/compare/saber-log@0.1.1...saber-log@0.2.0) (2019-04-09)
23 |
24 | ### Bug Fixes
25 |
26 | - **logger:** set log level properly ([251125d](https://github.com/egoist/saber/commit/251125d))
27 |
28 | ### Features
29 |
30 | - output fewer logs ([d3a68ea](https://github.com/egoist/saber/commit/d3a68ea))
31 |
32 | ## [0.1.1](https://github.com/egoist/saber/compare/saber-log@0.1.0...saber-log@0.1.1) (2019-02-13)
33 |
34 | **Note:** Version bump only for package saber-log
35 |
36 | # 0.1.0 (2019-01-21)
37 |
38 | ### Features
39 |
40 | - support markdown highlighter ([5c8ec2e](https://github.com/egoist/saber/commit/5c8ec2e))
41 |
--------------------------------------------------------------------------------
/packages/saber-log/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) EGOIST <0x142857@gmail.com> (https://github.com/egoist)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/saber-log/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "saber-log",
3 | "version": "0.3.1",
4 | "main": "dist/index.js",
5 | "types": "dist/index.d.ts",
6 | "files": [
7 | "dist"
8 | ],
9 | "license": "MIT",
10 | "scripts": {
11 | "prepublishOnly": "yarn build",
12 | "build": "rollup -c"
13 | },
14 | "devDependencies": {
15 | "rollup": "^1.26.0",
16 | "rollup-plugin-typescript2": "^0.24.3"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/packages/saber-log/rollup.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | input: 'src/index.ts',
3 | output: {
4 | file: 'dist/index.js',
5 | format: 'cjs'
6 | },
7 | plugins: [
8 | require('rollup-plugin-typescript2')({
9 | tsconfigOverride: {
10 | compilerOptions: {
11 | declaration: true
12 | }
13 | }
14 | })
15 | ]
16 | }
17 |
--------------------------------------------------------------------------------
/packages/saber-log/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "include": ["src"],
4 | "compilerOptions": {
5 | "module": "esnext"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/packages/saber-markdown/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) EGOIST <0x142857@gmail.com> (https://github.com/egoist)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/saber-markdown/README.md:
--------------------------------------------------------------------------------
1 | # saber-markdown
2 |
3 | A custom build of markdown-it for Saber.
4 |
5 | ## Differences
6 |
7 | Implemented via a [babel plugin](./babel-plugin-vue-features.js).
8 |
9 | ### Support `@` in attribute names
10 |
11 | In:
12 |
13 | ```html
14 |
15 | ```
16 |
17 | Out:
18 |
19 | ```diff
20 | - <button @click="foo">
\n
21 | +
\n
22 | ```
23 |
24 | ### Support top-level components
25 |
26 | In:
27 |
28 | ```html
29 |
30 | ```
31 |
32 | Out:
33 |
34 | ```diff
35 | - hi
\n
36 | + hi
37 | ```
38 |
--------------------------------------------------------------------------------
/packages/saber-markdown/babel-plugin-vue-features.js:
--------------------------------------------------------------------------------
1 | module.exports = ({ types: t }) => {
2 | return {
3 | visitor: {
4 | StringLiteral(
5 | path,
6 | {
7 | file: {
8 | opts: { filename }
9 | }
10 | }
11 | ) {
12 | if (
13 | filename.endsWith('/html_re.js') &&
14 | path.node.value === '[a-zA-Z_:][a-zA-Z0-9:._-]*'
15 | ) {
16 | console.log('support @ in attribute names!')
17 | path.node.value = '[a-zA-Z@_:][a-zA-Z0-9:._-]*'
18 | }
19 | },
20 | ArrayExpression(path) {
21 | if (
22 | path.parent.type === 'VariableDeclarator' &&
23 | path.parent.id.name === 'HTML_SEQUENCES'
24 | ) {
25 | // Using Vue components at top-level
26 | // Previously `` is transformed to `
`
27 | // Now it's just ``
28 | console.log('support top-level components')
29 | path.node.elements = [
30 | ...path.node.elements.slice(0, -2),
31 | // PascalCase Components
32 | t.arrayExpression([
33 | t.regExpLiteral('^<[A-Z]'),
34 | t.regExpLiteral('>'),
35 | t.booleanLiteral(true)
36 | ]),
37 | // custom elements with hyphens
38 | t.arrayExpression([
39 | t.regExpLiteral('^<\\w+\\-'),
40 | t.regExpLiteral('>'),
41 | t.booleanLiteral(true)
42 | ]),
43 | ...path.node.elements.slice(-2)
44 | ]
45 | }
46 | }
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/packages/saber-markdown/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "saber-markdown",
3 | "version": "0.1.6",
4 | "files": [
5 | "dist"
6 | ],
7 | "main": "dist/index.js",
8 | "scripts": {
9 | "build": "rollup -c",
10 | "prepublishOnly": "npm run build"
11 | },
12 | "license": "MIT",
13 | "devDependencies": {
14 | "@babel/core": "7.6.4",
15 | "builtin-modules": "3.1.0",
16 | "markdown-it": "10.0.0",
17 | "rollup": "1.22.0",
18 | "rollup-plugin-babel": "4.3.3",
19 | "rollup-plugin-commonjs": "10.1.0",
20 | "rollup-plugin-json": "4.0.0",
21 | "rollup-plugin-node-resolve": "5.2.0",
22 | "rollup-plugin-terser": "5.1.2"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/packages/saber-markdown/rollup.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | input: './src/index.js',
3 | output: {
4 | file: './dist/index.js',
5 | format: 'cjs'
6 | },
7 | plugins: [
8 | require('rollup-plugin-commonjs')(),
9 | require('rollup-plugin-node-resolve')(),
10 | require('rollup-plugin-json')(),
11 | require('rollup-plugin-babel')({
12 | plugins: [require('./babel-plugin-vue-features')]
13 | }),
14 | require('rollup-plugin-terser').terser()
15 | ],
16 | external: require('builtin-modules')
17 | }
18 |
--------------------------------------------------------------------------------
/packages/saber-markdown/src/index.js:
--------------------------------------------------------------------------------
1 | import Markdown from 'markdown-it/lib'
2 | import Token from 'markdown-it/lib/token'
3 |
4 | Markdown.Token = Token
5 |
6 | export default Markdown
7 |
--------------------------------------------------------------------------------
/packages/saber-plugin-code-copy/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 | All notable changes to this project will be documented in this file.
4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5 |
6 | ## [0.3.2](https://github.com/saberland/saber/compare/saber-plugin-code-copy@0.3.1...saber-plugin-code-copy@0.3.2) (2019-11-10)
7 |
8 | ### Bug Fixes
9 |
10 | - **code-copy:** copy button positioning ([#557](https://github.com/saberland/saber/issues/557)) ([175ba24](https://github.com/saberland/saber/commit/175ba24))
11 |
12 | ## [0.3.1](https://github.com/saberland/saber/compare/saber-plugin-code-copy@0.3.0...saber-plugin-code-copy@0.3.1) (2019-11-03)
13 |
14 | **Note:** Version bump only for package saber-plugin-code-copy
15 |
16 | # 0.3.0 (2019-09-29)
17 |
18 | ### Bug Fixes
19 |
20 | - **plugin-code-copy:** correct package name ([ee7d0ce](https://github.com/saberland/saber/commit/ee7d0ce))
21 |
22 | ### Features
23 |
24 | - add code-copy plugin ([#450](https://github.com/saberland/saber/issues/450)) ([007636c](https://github.com/saberland/saber/commit/007636c))
25 |
26 | # 0.2.0 (2019-09-25)
27 |
28 | ### Features
29 |
30 | - add code-copy plugin ([#450](https://github.com/saberland/saber/issues/450)) ([007636c](https://github.com/saberland/saber/commit/007636c))
31 |
--------------------------------------------------------------------------------
/packages/saber-plugin-code-copy/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) EGOIST <0x142857@gmail.com> (https://github.com/egoist)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/saber-plugin-code-copy/README.md:
--------------------------------------------------------------------------------
1 | # saber-plugin-code-copy
2 |
3 | Copy code to clipboard.
4 |
5 | ## Install
6 |
7 | ```bash
8 | yarn add saber-plugin-code-copy
9 | ```
10 |
11 | ## Usage
12 |
13 | In your `saber-config.yml`:
14 |
15 | ```yml
16 | plugins:
17 | - resolve: saber-plugin-code-copy
18 | ```
19 |
20 | ## Options
21 |
22 | ### statusAttribute
23 |
24 | - Type: `string`
25 | - Default: `title`
26 |
27 | By default the `title` attribute of the button is set to `Copy`, when code is copied we update it to `Copied`, you can specify another attribute name if you want, e.g. `aria-label`.
28 |
29 | ### buttonStyle
30 |
31 | - Type: `object`
32 | - Default: `undefined`
33 |
34 | Assign custom style to the _Copy_ button, e.g.:
35 |
36 | ```js
37 | {
38 | backgroundColor: 'red'
39 | }
40 | ```
41 |
42 | ## License
43 |
44 | MIT.
45 |
--------------------------------------------------------------------------------
/packages/saber-plugin-code-copy/lib/index.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 |
3 | const ID = 'code-copy'
4 |
5 | exports.name = ID
6 |
7 | exports.apply = (api, options) => {
8 | api.browserApi.add(path.join(__dirname, 'saber-browser.js'))
9 | api.hooks.chainWebpack.tap(ID, config => {
10 | config.plugin('constants').tap(([constants]) => [
11 | Object.assign(constants, {
12 | __CODE_COPY_OPTIONS__: JSON.stringify(options)
13 | })
14 | ])
15 | })
16 | }
17 |
--------------------------------------------------------------------------------
/packages/saber-plugin-code-copy/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "saber-plugin-code-copy",
3 | "version": "0.3.2",
4 | "description": "Allow to copy code into clipboard",
5 | "license": "MIT",
6 | "main": "lib/index.js",
7 | "files": [
8 | "lib"
9 | ],
10 | "peerDependencies": {
11 | "saber": ">=0.7.0"
12 | },
13 | "dependencies": {
14 | "modern-copy": "^1.0.3"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/packages/saber-plugin-feed/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) EGOIST <0x142857@gmail.com> (https://github.com/egoist)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/saber-plugin-feed/example/pages/_posts/bye-world.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: bye world
3 | date: 2018-03-12
4 | layout: post
5 | ---
6 |
7 | well ok __fine__
8 |
--------------------------------------------------------------------------------
/packages/saber-plugin-feed/example/pages/_posts/hello-world.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: hello world
3 | date: 2018-02-12
4 | layout: post
5 | ---
6 |
7 | wtf is __this__ {{ 1 + 1 }}
8 |
--------------------------------------------------------------------------------
/packages/saber-plugin-feed/example/saber-config.yml:
--------------------------------------------------------------------------------
1 | plugins:
2 | - resolve: ../
3 | options:
4 | atomFeed: true
5 | rss2Feed: true
6 | jsonFeed: blog/feed.json
7 |
8 | siteConfig:
9 | url: https://example.com
10 | author: Your Name
11 |
--------------------------------------------------------------------------------
/packages/saber-plugin-feed/lib/saber-browser.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | import Vue from 'vue'
3 | import { jsonFeedPath, atomFeedPath, rss2FeedPath } from 'saber/variables'
4 |
5 | const getPermalink = (localePath, feedPath) => {
6 | return `${localePath === '/' ? '' : localePath}/${feedPath.replace(
7 | /^\.?\//,
8 | ''
9 | )}`
10 | }
11 |
12 | Vue.mixin({
13 | computed: {
14 | $feed() {
15 | const allFeeds = this.$allFeeds
16 | const permalink = allFeeds.atom || allFeeds.rss2 || allFeeds.json
17 | const type = allFeeds.atom ? 'atom' : allFeeds.rss2 ? 'rss2' : 'json'
18 | return {
19 | permalink,
20 | type
21 | }
22 | },
23 | $allFeeds() {
24 | return {
25 | atom: atomFeedPath && getPermalink(this.$localePath, atomFeedPath),
26 | rss2: rss2FeedPath && getPermalink(this.$localePath, rss2FeedPath),
27 | json: jsonFeedPath && getPermalink(this.$localePath, jsonFeedPath)
28 | }
29 | }
30 | }
31 | })
32 |
--------------------------------------------------------------------------------
/packages/saber-plugin-feed/lib/utils.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 | const { URL } = require('url')
3 |
4 | /**
5 | * Get feed path
6 | * @param {string|boolean|undefined} feedPath
7 | * @param {string} defaultPath
8 | */
9 | exports.getFeedPath = (feedPath, defaultPath) => {
10 | if (feedPath === true) {
11 | return defaultPath
12 | }
13 |
14 | if (typeof feedPath === 'string') {
15 | return feedPath
16 | }
17 |
18 | return null
19 | }
20 |
21 | /**
22 | * @param {string} base
23 | * @param {string} pathname
24 | * @returns {string}
25 | */
26 | exports.resolveURL = (base, pathname) => {
27 | return new URL(pathname, base).href
28 | }
29 |
--------------------------------------------------------------------------------
/packages/saber-plugin-feed/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "saber-plugin-feed",
3 | "version": "0.4.3",
4 | "description": "Generate Atom, RSS2, or JSON feed for your Saber app",
5 | "license": "MIT",
6 | "main": "lib/index.js",
7 | "scripts": {
8 | "generate:example": "saber generate example"
9 | },
10 | "files": [
11 | "lib"
12 | ],
13 | "dependencies": {
14 | "feed": "^4.0.0"
15 | },
16 | "peerDependencies": {
17 | "saber": ">=0.7.0"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/packages/saber-plugin-git-modification-time/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) EGOIST <0x142857@gmail.com> (https://github.com/egoist)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/saber-plugin-git-modification-time/README.md:
--------------------------------------------------------------------------------
1 | # saber-plugin-git-modification-time
2 |
3 | > Use the author time of the last commit as `page.updatedAt`
4 |
5 | Why? See [#9785](https://github.com/gatsbyjs/gatsby/issues/9785).
6 |
7 | **tl;dr** `page.updatedAt` defaults to `file.mtime` which will change on platforms like Netlify. We restore the value to author time of the last commit instead.
8 |
9 | ## Install
10 |
11 | ```bash
12 | yarn add saber-plugin-git-modification-time
13 | ```
14 |
15 | ## Usage
16 |
17 | In your `saber-config.js`:
18 |
19 | ```js
20 | module.exports = {
21 | plugins: ['saber-plugin-git-modification-time']
22 | }
23 | ```
24 |
25 | ## License
26 |
27 | MIT.
28 |
--------------------------------------------------------------------------------
/packages/saber-plugin-git-modification-time/lib/index.js:
--------------------------------------------------------------------------------
1 | const execa = require('execa')
2 |
3 | const ID = 'git-modification-time'
4 |
5 | exports.name = ID
6 |
7 | exports.apply = api => {
8 | api.hooks.onCreatePage.tapPromise(ID, async page => {
9 | if (page.internal.absolute) {
10 | const { stdout } = await execa('git', [
11 | 'log',
12 | '-1',
13 | '--pretty=format:%aI',
14 | '--',
15 | page.internal.absolute
16 | ])
17 | if (stdout) {
18 | page.updatedAt = new Date(stdout)
19 | }
20 | }
21 | })
22 | }
23 |
--------------------------------------------------------------------------------
/packages/saber-plugin-git-modification-time/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "saber-plugin-git-modification-time",
3 | "version": "0.1.3",
4 | "description": "Use the author time of the last commit as page.updatedAt",
5 | "license": "MIT",
6 | "main": "lib/index.js",
7 | "files": [
8 | "lib"
9 | ],
10 | "dependencies": {
11 | "execa": "^3.2.0"
12 | },
13 | "peerDependencies": {
14 | "saber": ">=0.7.0"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/packages/saber-plugin-google-analytics/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) EGOIST <0x142857@gmail.com> (https://github.com/egoist)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/saber-plugin-google-analytics/README.md:
--------------------------------------------------------------------------------
1 | # saber-plugin-google-analytics
2 |
3 | Add Google Analytics to your Saber sites.
4 |
5 | ## Install
6 |
7 | ```bash
8 | yarn add saber-plugin-google-analytics
9 | ```
10 |
11 | ## Usage
12 |
13 | In your `saber-config.yml`:
14 |
15 | ```yml
16 | plugins:
17 | - resolve: saber-plugin-google-analytics
18 | options:
19 | trackId: UA-XXX-XX # Google Analytics Track ID
20 | ```
21 |
22 | ## Plugin Options
23 |
24 | ### trackId
25 |
26 | - Type: `string`
27 | - Required: `true`
28 |
29 | Google Analytics Track ID.
30 |
31 | ### anonymizeIp
32 |
33 | - Type: `boolean`
34 | - Default: `false`
35 |
36 | To anonymize the IP address for all hits sent from a single tracker, set the `anonymizeIp` option to `true`.
37 |
38 | ## License
39 |
40 | MIT.
41 |
--------------------------------------------------------------------------------
/packages/saber-plugin-google-analytics/index.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 |
3 | const ID = 'google-analytics'
4 |
5 | exports.name = ID
6 |
7 | exports.apply = (api, { trackId = false, anonymizeIp = false } = {}) => {
8 | api.hooks.chainWebpack.tap(ID, config => {
9 | config.plugin('constants').tap(([options]) => [
10 | Object.assign(options, {
11 | __GA_TRACK_ID__: JSON.stringify(trackId),
12 | __GA_ANONYMIZE_IP: JSON.stringify(anonymizeIp)
13 | })
14 | ])
15 | })
16 |
17 | api.browserApi.add(path.join(__dirname, 'saber-browser.js'))
18 | }
19 |
--------------------------------------------------------------------------------
/packages/saber-plugin-google-analytics/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "saber-plugin-google-analytics",
3 | "version": "0.2.1",
4 | "main": "index.js",
5 | "files": [
6 | "index.js",
7 | "saber-browser.js"
8 | ],
9 | "peerDependencies": {
10 | "saber": ">=0.7.0"
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/packages/saber-plugin-google-analytics/saber-browser.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | // Google analytics integration for Vue.js renderer
3 | export default function({ router }) {
4 | if (
5 | process.browser &&
6 | process.env.NODE_ENV === 'production' &&
7 | __GA_TRACK_ID__
8 | ) {
9 | function doNotTrackEnabled() {
10 | const dntNumber = parseInt(
11 | navigator.msDoNotTrack || // Internet Explorer 9 and 10 vendor prefix
12 | window.doNotTrack || // IE 11 uses window.doNotTrack
13 | navigator.doNotTrack, // W3C
14 | 10
15 | )
16 |
17 | return dntNumber === 1
18 | }
19 |
20 | if (doNotTrackEnabled()) {
21 | // Respect doNotTrack setting
22 | return
23 | }
24 |
25 | // prettier-ignore
26 | ;(function(i, s, o, g, r, a, m) {
27 | i.GoogleAnalyticsObject = r
28 | i[r] = i[r] || function () {
29 | (i[r].q = i[r].q || []).push(arguments)
30 | }
31 | i[r].l = Number(new Date())
32 | a = s.createElement(o)
33 | m = s.getElementsByTagName(o)[0]
34 | a.async = 1
35 | a.src = g
36 | m.parentNode.insertBefore(a, m)
37 | })(window, document, 'script', 'https://www.google-analytics.com/analytics.js', 'ga')
38 |
39 | ga('create', __GA_TRACK_ID__, 'auto')
40 |
41 | if (__GA_ANONYMIZE_IP) {
42 | ga('set', 'anonymizeIp', true)
43 | }
44 |
45 | router.afterEach(to => {
46 | ga('set', 'page', to.fullPath)
47 | ga('send', 'pageview')
48 | })
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/packages/saber-plugin-image/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) EGOIST <0x142857@gmail.com> (https://github.com/egoist)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/saber-plugin-image/lib/styles.module.css:
--------------------------------------------------------------------------------
1 | .blendIn[data-src],
2 | .blendIn[data-srcset] {
3 | transition: filter 0.3s;
4 | filter: blur(5px);
5 | }
6 |
7 | .blendIn[data-lazy-loaded] {
8 | filter: none;
9 | }
10 |
--------------------------------------------------------------------------------
/packages/saber-plugin-image/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "saber-plugin-image",
3 | "version": "0.2.2",
4 | "main": "lib/index.js",
5 | "files": [
6 | "lib"
7 | ],
8 | "license": "MIT",
9 | "dependencies": {
10 | "jimp": "^0.8.0",
11 | "lozad": "^1.9.0",
12 | "responsive-loader": "^1.2.0"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/packages/saber-plugin-meta-redirect/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) EGOIST <0x142857@gmail.com> (https://github.com/egoist)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/saber-plugin-meta-redirect/README.md:
--------------------------------------------------------------------------------
1 | # saber-plugin-meta-redirect
2 |
3 | Generates ` ` redirect html files for redirecting on any static file host.
4 |
5 | ## Install
6 |
7 | ```bash
8 | yarn add saber-plugin-meta-redirect
9 | ```
10 |
11 | ## Usage
12 |
13 | In your `saber-config.yml`:
14 |
15 | ```yml
16 | plugins:
17 | - resolve: saber-plugin-meta-redirect
18 | ```
19 |
20 | In your `saber-node.js`:
21 |
22 | ```js
23 | exports.onCreatePages = function() {
24 | this.pages.createRedirect({
25 | fromPath: '/about',
26 | toPath: '/about-us'
27 | })
28 | }
29 | ```
30 |
31 | Check out the docs for [`pages.createRedirect`]().
32 |
33 | ## License
34 |
35 | MIT.
36 |
--------------------------------------------------------------------------------
/packages/saber-plugin-meta-redirect/example/pages/about.md:
--------------------------------------------------------------------------------
1 | This is about
2 |
--------------------------------------------------------------------------------
/packages/saber-plugin-meta-redirect/example/pages/index.md:
--------------------------------------------------------------------------------
1 | hello [about](./redirect-to-about)
2 |
--------------------------------------------------------------------------------
/packages/saber-plugin-meta-redirect/example/saber-config.yml:
--------------------------------------------------------------------------------
1 | plugins:
2 | - resolve: ../
3 |
4 | build:
5 | publicUrl: /blog/
6 |
--------------------------------------------------------------------------------
/packages/saber-plugin-meta-redirect/example/saber-node.js:
--------------------------------------------------------------------------------
1 | exports.onCreatePages = function () {
2 | this.pages.createRedirect({
3 | fromPath: '/redirect-to-about',
4 | toPath: '/about.html',
5 | redirectInBrowser: true
6 | })
7 | }
8 |
--------------------------------------------------------------------------------
/packages/saber-plugin-meta-redirect/lib/index.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const urlJoin = require('url-join')
3 |
4 | const ID = 'meta-redirect'
5 |
6 | exports.name = ID
7 |
8 | exports.apply = api => {
9 | api.hooks.afterGenerate.tapPromise(ID, async () => {
10 | const { log } = api
11 | const { fs } = api.utils
12 |
13 | const outDir = api.resolveOutDir()
14 |
15 | const getFileNameFromLink = link => {
16 | const filename = link.endsWith('.html')
17 | ? link
18 | : link.replace(/\/?$/, '/index.html')
19 | return path.join(outDir, filename)
20 | }
21 |
22 | const getPageContent = toPath => {
23 | return ` `
27 | }
28 |
29 | const writePage = async config => {
30 | const fileName = getFileNameFromLink(config.fromPath)
31 | log.info(`Generating ${path.relative(outDir, fileName)}`)
32 | await fs.outputFile(fileName, getPageContent(config.toPath), 'utf8')
33 | }
34 |
35 | const configs = [...api.pages.redirectRoutes.values()]
36 | await Promise.all(configs.map(config => writePage(config)))
37 | })
38 | }
39 |
--------------------------------------------------------------------------------
/packages/saber-plugin-meta-redirect/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "saber-plugin-meta-redirect",
3 | "version": "0.1.2",
4 | "description": "Generates meta redirect html files for redirecting on any static file host",
5 | "license": "MIT",
6 | "main": "lib/index.js",
7 | "scripts": {
8 | "dev:example": "saber example",
9 | "build:example": "saber build example"
10 | },
11 | "files": [
12 | "lib"
13 | ],
14 | "dependencies": {
15 | "url-join": "^4.0.0"
16 | },
17 | "peerDependencies": {
18 | "saber": ">=0.7.0"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/packages/saber-plugin-netlify-redirect/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) EGOIST <0x142857@gmail.com> (https://github.com/egoist)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/saber-plugin-netlify-redirect/README.md:
--------------------------------------------------------------------------------
1 | # saber-plugin-netlify-redirect
2 |
3 | Automatically generate or update `_redirects` file in your public folder for [Netlify](https://www.netlify.com/docs/redirects/).
4 |
5 | ## Install
6 |
7 | ```bash
8 | yarn add saber-plugin-netlify-redirect
9 | ```
10 |
11 | ## Usage
12 |
13 | In your `saber-config.yml`:
14 |
15 | ```yml
16 | plugins:
17 | - resolve: saber-plugin-netlify-redirect
18 | ```
19 |
20 | Then you can create redirects using the [`pages.createRedirect`]() API.
21 |
22 | Since Netlify automatically rewrites routes like `/about` to `/about.html`, this plugin will also create redirects for all `.html` routes so that `/about` will be redirected to `/about.html` when it does not exist.
23 |
24 | ## License
25 |
26 | MIT.
27 |
--------------------------------------------------------------------------------
/packages/saber-plugin-netlify-redirect/example/pages/about.md:
--------------------------------------------------------------------------------
1 | This is about
2 |
--------------------------------------------------------------------------------
/packages/saber-plugin-netlify-redirect/example/pages/index.md:
--------------------------------------------------------------------------------
1 | hello [about](./redirect-to-about)
2 |
--------------------------------------------------------------------------------
/packages/saber-plugin-netlify-redirect/example/public/_redirects:
--------------------------------------------------------------------------------
1 | /foo /bar 301
2 |
--------------------------------------------------------------------------------
/packages/saber-plugin-netlify-redirect/example/saber-config.yml:
--------------------------------------------------------------------------------
1 | plugins:
2 | - resolve: ../
3 |
4 | build:
5 | publicUrl: /blog/
6 |
--------------------------------------------------------------------------------
/packages/saber-plugin-netlify-redirect/example/saber-node.js:
--------------------------------------------------------------------------------
1 | exports.onCreatePages = function () {
2 | this.pages.createRedirect({
3 | fromPath: '/redirect-to-about',
4 | toPath: '/about.html',
5 | redirectInBrowser: true
6 | })
7 | }
8 |
--------------------------------------------------------------------------------
/packages/saber-plugin-netlify-redirect/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "saber-plugin-netlify-redirect",
3 | "version": "0.1.2",
4 | "description": "Automatically generate or update `_redirects` file in your public folder for Netlify.",
5 | "license": "MIT",
6 | "main": "lib/index.js",
7 | "scripts": {
8 | "dev:example": "saber example",
9 | "build:example": "saber build example"
10 | },
11 | "files": [
12 | "lib"
13 | ],
14 | "dependencies": {
15 | "url-join": "^4.0.0"
16 | },
17 | "peerDependencies": {
18 | "saber": ">=0.7.0"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/packages/saber-plugin-prismjs/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 | All notable changes to this project will be documented in this file.
4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5 |
6 | ## [0.2.4](https://github.com/saberland/saber/compare/saber-plugin-prismjs@0.2.3...saber-plugin-prismjs@0.2.4) (2019-11-03)
7 |
8 | **Note:** Version bump only for package saber-plugin-prismjs
9 |
10 | ## [0.2.3](https://github.com/saberland/saber/compare/saber-plugin-prismjs@0.2.2...saber-plugin-prismjs@0.2.3) (2019-09-11)
11 |
12 | **Note:** Version bump only for package saber-plugin-prismjs
13 |
14 | ## [0.2.2](https://github.com/saberland/saber/compare/saber-plugin-prismjs@0.2.1...saber-plugin-prismjs@0.2.2) (2019-08-22)
15 |
16 | ### Bug Fixes
17 |
18 | - **pkg:** add missing LICENSE in leaf packages ([9f73603](https://github.com/saberland/saber/commit/9f73603))
19 |
20 | ## [0.2.1](https://github.com/saberland/saber/compare/saber-plugin-prismjs@0.2.0...saber-plugin-prismjs@0.2.1) (2019-05-29)
21 |
22 | **Note:** Version bump only for package saber-plugin-prismjs
23 |
24 | # 0.2.0 (2019-05-08)
25 |
26 | ### Features
27 |
28 | - add saber-plugin-prismjs ([1bcf1e6](https://github.com/egoist/saber/commit/1bcf1e6))
29 |
--------------------------------------------------------------------------------
/packages/saber-plugin-prismjs/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) EGOIST <0x142857@gmail.com> (https://github.com/egoist)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/saber-plugin-prismjs/README.md:
--------------------------------------------------------------------------------
1 | # saber-plugin-prismjs
2 |
3 | ## How to use
4 |
5 | Install this package:
6 |
7 | ```bash
8 | yarn add prismjs saber-plugin-prismjs saber-highlight-css
9 | ```
10 |
11 | Note that saber-highlight-css is required for line highlighting to display properly.
12 |
13 | Use it in your `saber-config.yml`:
14 |
15 | ```yaml
16 | plugins:
17 | - resolve: saber-plugin-prismjs
18 | ```
19 |
20 | ### Include CSS
21 |
22 | In your `saber-browser.js`:
23 |
24 | ```js
25 | import 'prismjs/themes/prism.css'
26 | ```
27 |
28 | Optional: Include `saber-highlight-css` for line highlighting styles:
29 |
30 | ```bash
31 | yarn add saber-highlight-css
32 | ```
33 |
34 | ```js
35 | // saber-browser.js
36 | import 'prismjs/themes/prism.css'
37 | import 'saber-highlight-css/default.css'
38 | ```
39 |
40 | ## License
41 |
42 | MIT © EGOIST
43 |
--------------------------------------------------------------------------------
/packages/saber-plugin-prismjs/index.js:
--------------------------------------------------------------------------------
1 | const ID = 'prismjs'
2 |
3 | exports.name = ID
4 |
5 | exports.apply = api => {
6 | api.hooks.chainMarkdown.tap(ID, config => {
7 | config.options.highlight(require('saber-highlighter-prism'))
8 | })
9 | }
10 |
--------------------------------------------------------------------------------
/packages/saber-plugin-prismjs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "saber-plugin-prismjs",
3 | "version": "0.2.4",
4 | "license": "MIT",
5 | "files": [
6 | "index.js"
7 | ],
8 | "main": "index.js",
9 | "devDependencies": {
10 | "prismjs": "1.17.1"
11 | },
12 | "peerDependencies": {
13 | "prismjs": "^1.16.0"
14 | },
15 | "dependencies": {
16 | "saber-highlighter-prism": "^0.3.4"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/packages/saber-plugin-pwa/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) EGOIST <0x142857@gmail.com> (https://github.com/egoist)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/saber-plugin-pwa/lib/create-element.js:
--------------------------------------------------------------------------------
1 | const stringify = require('stringify-attributes')
2 |
3 | module.exports = (tagName, attrs, content) => {
4 | const attrString = stringify(attrs)
5 | return content
6 | ? `<${tagName}${attrString}>${content}${tagName}`
7 | : `<${tagName}${attrString} />`
8 | }
9 |
--------------------------------------------------------------------------------
/packages/saber-plugin-pwa/lib/generate-manifest.js:
--------------------------------------------------------------------------------
1 | module.exports = async (api, { name, themeColor, manifest }) => {
2 | const { log } = api
3 | const { fs } = api.utils
4 |
5 | const newManifest = JSON.stringify(
6 | Object.assign(
7 | {
8 | short_name: name,
9 | name,
10 | start_url: './',
11 | display: 'standalone',
12 | theme_color: themeColor,
13 | background_color: '#ffffff'
14 | },
15 | manifest
16 | ),
17 | null,
18 | 2
19 | )
20 |
21 | log.info(`Generating manifest.json`)
22 | await fs.outputFile(api.resolveOutDir('manifest.json'), newManifest, 'utf8')
23 | }
24 |
--------------------------------------------------------------------------------
/packages/saber-plugin-pwa/lib/get-app-config.js:
--------------------------------------------------------------------------------
1 | const defaults = {
2 | name: 'Saber PWA App',
3 | themeColor: '#ffffff',
4 | assetsVersion: ''
5 | }
6 |
7 | module.exports = (config = {}) => {
8 | return Object.assign({}, defaults, config, {
9 | name: config.name || defaults.name
10 | })
11 | }
12 |
--------------------------------------------------------------------------------
/packages/saber-plugin-pwa/lib/noop-sw-middleware.js:
--------------------------------------------------------------------------------
1 | const resetScript = `
2 | self.addEventListener('install', function(e) {
3 | self.skipWaiting()
4 | })
5 |
6 | self.addEventListener('activate', function(e) {
7 | self.registration
8 | .unregister()
9 | .then(function() {
10 | return self.clients.matchAll()
11 | })
12 | .then(function(clients) {
13 | clients.forEach(client => client.navigate(client.url))
14 | })
15 | })
16 | `
17 |
18 | module.exports = () => (req, res, next) => {
19 | if (req.path === '/service-worker.js') {
20 | res.setHeader('Content-Type', 'text/javascript')
21 | res.end(resetScript)
22 | } else {
23 | next()
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/packages/saber-plugin-pwa/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "saber-plugin-pwa",
3 | "version": "0.4.4",
4 | "description": "Add PWA support for your Saber app.",
5 | "license": "MIT",
6 | "main": "lib/index.js",
7 | "files": [
8 | "lib"
9 | ],
10 | "peerDependencies": {
11 | "saber": ">=0.7.0"
12 | },
13 | "dependencies": {
14 | "@snackbar/core": "^1.6.0",
15 | "stringify-attributes": "^2.0.0",
16 | "workbox-build": "^4.3.1",
17 | "workbox-window": "^4.3.1"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/packages/saber-plugin-query-posts/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) EGOIST <0x142857@gmail.com> (https://github.com/egoist)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/saber-plugin-query-posts/lib/utils.js:
--------------------------------------------------------------------------------
1 | const slugify = require('slugo')
2 |
3 | function paginate(arr, options) {
4 | options = Object.assign({ perPage: 30 }, options)
5 |
6 | if (options.firstPageOnly) {
7 | return [arr.slice(0, options.perPage)]
8 | }
9 |
10 | const totalPages = Math.ceil(arr.length / options.perPage)
11 | const result = []
12 | for (let i = 0; i < totalPages; i++) {
13 | result[i] = arr.slice(i * options.perPage, (i + 1) * options.perPage)
14 | }
15 |
16 | return result
17 | }
18 |
19 | function getIdFromMap(map, name) {
20 | let id
21 | if (map[name]) {
22 | id = map[name]
23 | } else {
24 | id = slugify(name.replace(/\//g, '-'))
25 | map[name] = id
26 | }
27 |
28 | return id
29 | }
30 |
31 | function getNameFromMap(map, id) {
32 | for (const name of Object.keys(map)) {
33 | if (map[name] === id) {
34 | return name
35 | }
36 | }
37 |
38 | return id
39 | }
40 |
41 | /**
42 | * Render permalink template
43 | * @param {string} permalink
44 | * @param {{[k:string]: string}} data
45 | */
46 | function renderPermalink(permalink, data) {
47 | for (const key of Object.keys(data)) {
48 | permalink = permalink.replace(`:${key}`, data[key])
49 | }
50 |
51 | return permalink
52 | }
53 |
54 | module.exports = {
55 | paginate,
56 | getIdFromMap,
57 | getNameFromMap,
58 | renderPermalink
59 | }
60 |
--------------------------------------------------------------------------------
/packages/saber-plugin-query-posts/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "saber-plugin-query-posts",
3 | "version": "0.4.6",
4 | "files": [
5 | "lib"
6 | ],
7 | "main": "lib/index.js",
8 | "keywords": [
9 | "saber",
10 | "saber-plugin"
11 | ],
12 | "license": "MIT",
13 | "dependencies": {
14 | "slugo": "^0.3.1",
15 | "url-join": "^4.0.0"
16 | },
17 | "peerDependencies": {
18 | "saber": ">=0.7.0"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/packages/saber-plugin-search/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 | All notable changes to this project will be documented in this file.
4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5 |
6 | # [1.0.0](https://github.com/saberland/saber/compare/saber-plugin-search@0.1.1...saber-plugin-search@1.0.0) (2019-11-03)
7 |
8 | ### Bug Fixes
9 |
10 | - don't strip html from content ([#535](https://github.com/saberland/saber/issues/535)) ([ed1e9be](https://github.com/saberland/saber/commit/ed1e9be))
11 |
12 | ### BREAKING CHANGES
13 |
14 | - now you get HTML code in your search result, if you don't like that you have to stripe HTML tags yourself.
15 |
16 | - fix: don't strip html from content
17 |
18 | - fix: don't strip html at all
19 |
20 | ## [0.1.1](https://github.com/saberland/saber/compare/saber-plugin-search@0.1.0...saber-plugin-search@0.1.1) (2019-10-05)
21 |
22 | ### Bug Fixes
23 |
24 | - **plugin-search:** generate data on demand ([#492](https://github.com/saberland/saber/issues/492)) ([18dd8b7](https://github.com/saberland/saber/commit/18dd8b7))
25 |
26 | # 0.1.0 (2019-08-22)
27 |
28 | ### Features
29 |
30 | - add saber-plugin-search ([#366](https://github.com/saberland/saber/issues/366)) ([aaf2d63](https://github.com/saberland/saber/commit/aaf2d63))
31 | - add saber-plugin-search ([#366](https://github.com/saberland/saber/issues/366)) ([bcd0693](https://github.com/saberland/saber/commit/bcd0693))
32 |
--------------------------------------------------------------------------------
/packages/saber-plugin-search/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) EGOIST <0x142857@gmail.com> (https://github.com/egoist)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/saber-plugin-search/lib/saber-browser.js:
--------------------------------------------------------------------------------
1 | /* eslint-env browser */
2 | /* globals __PUBLIC_URL__ */
3 |
4 | export default ({ Vue }) => {
5 | Vue.prototype.$fetchSearchDatabase = function() {
6 | const locale = this.$localePath.slice(1) || 'default'
7 | return window
8 | .fetch(`${__PUBLIC_URL__}_saber/plugin-search/${locale}.json`)
9 | .then(res => res.json())
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/packages/saber-plugin-search/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "saber-plugin-search",
3 | "version": "1.0.0",
4 | "description": "Add a fast search to your app.",
5 | "license": "MIT",
6 | "main": "lib/index.js",
7 | "files": [
8 | "lib"
9 | ],
10 | "peerDependencies": {
11 | "saber": ">=0.7.0"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/packages/saber-plugin-transformer-html/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) EGOIST <0x142857@gmail.com> (https://github.com/egoist)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/saber-plugin-transformer-html/README.md:
--------------------------------------------------------------------------------
1 | # saber-plugin-transformer-html
2 |
3 | Write pages in HTML.
4 |
5 | ## Install
6 |
7 | ```bash
8 | yarn add saber-plugin-transformer-html
9 | ```
10 |
11 | ## Usage
12 |
13 | In your `saber-config.yml`:
14 |
15 | ```yml
16 | plugins:
17 | - resolve: saber-plugin-transformer-html
18 | ```
19 |
20 | Try it by populating a `pages/try.html`:
21 |
22 | ```html
23 | hello world
24 | ```
25 |
26 | This page will be rendered as `/try.html`.
27 |
28 | Like Markdown pages, you can only use frontmatter to define page data:
29 |
30 | ```html
31 | ---
32 | title: Try it
33 | layout: try
34 | ---
35 |
36 | {{count}}
37 |
38 |
47 |
48 |
53 | ```
54 |
55 | ## License
56 |
57 | MIT.
58 |
--------------------------------------------------------------------------------
/packages/saber-plugin-transformer-html/example/pages/index.html:
--------------------------------------------------------------------------------
1 | hello there {{ 1+1 }}
2 |
3 |
8 |
--------------------------------------------------------------------------------
/packages/saber-plugin-transformer-html/example/saber-config.yml:
--------------------------------------------------------------------------------
1 | plugins:
2 | - resolve: ../
3 |
--------------------------------------------------------------------------------
/packages/saber-plugin-transformer-html/lib/index.js:
--------------------------------------------------------------------------------
1 | const extractSFCBlocks = require('extract-sfc-blocks')
2 |
3 | exports.name = 'transformer-html'
4 |
5 | exports.apply = api => {
6 | api.transformers.add('html', {
7 | extensions: ['html'],
8 | parse(page) {
9 | const { body, frontmatter } = api.transformers.parseFrontmatter(
10 | page.content
11 | )
12 | const { html, blocks } = extractSFCBlocks(body)
13 | Object.assign(page, frontmatter)
14 | page.content = html
15 | page.internal.hoistedTags = blocks
16 | },
17 | getPageComponent(page) {
18 | return `
19 |
20 | ${page.content || ''}
21 |
22 |
23 | `
24 | }
25 | })
26 | }
27 |
--------------------------------------------------------------------------------
/packages/saber-plugin-transformer-html/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "saber-plugin-transformer-html",
3 | "version": "0.1.3",
4 | "description": "Write pages in HTML",
5 | "license": "MIT",
6 | "main": "lib/index.js",
7 | "scripts": {
8 | "dev:example": "saber example",
9 | "build:example": "saber build example"
10 | },
11 | "files": [
12 | "lib"
13 | ],
14 | "dependencies": {
15 | "extract-sfc-blocks": "^0.0.2"
16 | },
17 | "peerDependencies": {
18 | "saber": ">=0.7.0"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/packages/saber-plugin-transformer-pug/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) EGOIST <0x142857@gmail.com> (https://github.com/egoist)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/saber-plugin-transformer-pug/README.md:
--------------------------------------------------------------------------------
1 | # saber-plugin-transformer-pug
2 |
3 | Write pages in Pug and add support for importing Pug files
4 |
5 | ## Install
6 |
7 | ```bash
8 | yarn add saber-plugin-transformer-pug
9 | ```
10 |
11 | ## Usage
12 |
13 | In your `saber-config.yml`:
14 |
15 | ```yml
16 | plugins:
17 | - resolve: saber-plugin-transformer-pug
18 | ```
19 |
20 | Try it by populating a `pages/try.pug`:
21 |
22 | ```pug
23 | strong hello world
24 | ```
25 |
26 | This page will be rendered as `/try.html`.
27 |
28 | Like Markdown pages, you can only use frontmatter to define page data:
29 |
30 | ```pug
31 | ---
32 | title: Try it
33 | layout: try
34 | ---
35 |
36 | strong hello
37 | i world
38 | ```
39 |
40 | ### Using Pug in Vue SFC template block
41 |
42 | ```vue
43 |
44 | p hello world
45 |
46 | ```
47 |
48 | ## License
49 |
50 | MIT.
51 |
--------------------------------------------------------------------------------
/packages/saber-plugin-transformer-pug/example/pages/about.vue:
--------------------------------------------------------------------------------
1 |
2 | h1 about
3 |
4 |
--------------------------------------------------------------------------------
/packages/saber-plugin-transformer-pug/example/pages/index.pug:
--------------------------------------------------------------------------------
1 | ---
2 | foo: true
3 | ---
4 |
5 | div
6 | h1 hello
7 | button(@click="count++") {{ count }}
8 |
9 | script.
10 | export default {
11 | data() {
12 | return {
13 | count: 0
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/packages/saber-plugin-transformer-pug/example/saber-config.yml:
--------------------------------------------------------------------------------
1 | plugins:
2 | - resolve: ../
3 |
--------------------------------------------------------------------------------
/packages/saber-plugin-transformer-pug/lib/index.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const pug = require('pug')
3 | const extractSFCBlocks = require('extract-sfc-blocks')
4 |
5 | const ID = 'transformer-pug'
6 |
7 | exports.name = ID
8 |
9 | exports.apply = api => {
10 | api.transformers.add('pug', {
11 | extensions: ['pug'],
12 | parse(page) {
13 | const { body, frontmatter } = api.transformers.parseFrontmatter(
14 | page.content
15 | )
16 | const { base: basename, dir: dirname } = path.parse(
17 | page.internal.absolute || ''
18 | )
19 | const html = pug.render(body, {
20 | filename: basename,
21 | basedir: dirname
22 | })
23 | const { html: pageContent, blocks } = extractSFCBlocks(html)
24 | Object.assign(page, frontmatter)
25 | page.content = pageContent
26 | page.internal.hoistedTags = blocks
27 | },
28 | getPageComponent(page) {
29 | return `
30 |
31 | ${page.content || ''}
32 |
33 |
34 | `
35 | }
36 | })
37 |
38 | api.hooks.chainWebpack.tap(ID, config => {
39 | config.module
40 | .rule('pug')
41 | .test(/\.pug$/)
42 | .use('pug-loader')
43 | .loader(require.resolve('./pug-plain-loader'))
44 | })
45 | }
46 |
--------------------------------------------------------------------------------
/packages/saber-plugin-transformer-pug/lib/pug-plain-loader.js:
--------------------------------------------------------------------------------
1 | module.exports = function(source, map) {
2 | if (
3 | this.resourcePath.endsWith('.vue') &&
4 | /\?vue&type=template/.test(this.resourceQuery)
5 | ) {
6 | // Transform in .vue files
7 | return require('pug-plain-loader').call(this, source, map)
8 | }
9 |
10 | return source
11 | }
12 |
--------------------------------------------------------------------------------
/packages/saber-plugin-transformer-pug/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "saber-plugin-transformer-pug",
3 | "version": "0.1.3",
4 | "description": "Write pages in Pug and add support for importing Pug files",
5 | "license": "MIT",
6 | "main": "lib/index.js",
7 | "scripts": {
8 | "dev:example": "saber example",
9 | "build:example": "saber build example"
10 | },
11 | "files": [
12 | "lib"
13 | ],
14 | "dependencies": {
15 | "extract-sfc-blocks": "^0.0.2",
16 | "pug": "^2.0.3",
17 | "pug-plain-loader": "^1.0.0"
18 | },
19 | "peerDependencies": {
20 | "saber": ">=0.7.0"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/packages/saber-utils/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) EGOIST <0x142857@gmail.com> (https://github.com/egoist)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/saber-utils/build.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const fs = require('fs-extra')
3 | const glob = require('fast-glob')
4 |
5 | const files = glob.sync('*.js', { cwd: './src', absolute: true })
6 |
7 | main().catch(error => {
8 | console.error(error)
9 | process.exitCode = 1
10 | })
11 |
12 | async function main() {
13 | for (const file of files) {
14 | const res = await require('@zeit/ncc')(path.normalize(file), {
15 | minify: true,
16 | sourceMap: false,
17 | watch: false
18 | })
19 | await fs.outputFile(
20 | path.join(__dirname, 'dist', path.basename(file)),
21 | res.code,
22 | 'utf8'
23 | )
24 | }
25 |
26 | await fs.outputFile(
27 | path.join(__dirname, 'dist/index.js'),
28 | `
29 | module.exports = { ${files
30 | .map(file => {
31 | const name = path.basename(file, path.extname(file))
32 | return `get ${name}() { return require('./${name}') }`
33 | })
34 | .join(',')}
35 | }
36 | `,
37 | 'utf8'
38 | )
39 | }
40 |
--------------------------------------------------------------------------------
/packages/saber-utils/index.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'saber-utils' {
2 | import fs from 'fs-extra'
3 | import glob from 'fast-glob'
4 |
5 | const slash: (input: string) => string
6 |
7 | const isAbsoluteUrl: (input: string) => boolean
8 |
9 | export { fs, glob, slash, isAbsoluteUrl }
10 | }
11 |
--------------------------------------------------------------------------------
/packages/saber-utils/index.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./dist')
2 |
3 | /**
4 | * Convert back slash to slash
5 | */
6 | module.exports.slash = input => input && input.replace(/\\/g, '/')
7 |
--------------------------------------------------------------------------------
/packages/saber-utils/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "saber-utils",
3 | "version": "0.2.4",
4 | "files": [
5 | "dist",
6 | "index.js",
7 | "index.d.ts"
8 | ],
9 | "main": "index.js",
10 | "types": "index.d.ts",
11 | "scripts": {
12 | "build": "node build",
13 | "prepublishOnly": "npm run build"
14 | },
15 | "devDependencies": {
16 | "@zeit/ncc": "0.20.5",
17 | "fast-glob": "3.1.0",
18 | "fs-extra": "8.1.0",
19 | "is-absolute-url": "^3.0.2"
20 | },
21 | "license": "MIT"
22 | }
23 |
--------------------------------------------------------------------------------
/packages/saber-utils/src/fs.js:
--------------------------------------------------------------------------------
1 | module.exports = require('fs-extra')
2 |
--------------------------------------------------------------------------------
/packages/saber-utils/src/glob.js:
--------------------------------------------------------------------------------
1 | module.exports = require('fast-glob')
2 |
--------------------------------------------------------------------------------
/packages/saber-utils/src/isAbsoluteUrl.js:
--------------------------------------------------------------------------------
1 | module.exports = require('is-absolute-url')
2 |
--------------------------------------------------------------------------------
/packages/saber/.eslintignore:
--------------------------------------------------------------------------------
1 | **/vendor/**
2 | *.min.js
3 |
--------------------------------------------------------------------------------
/packages/saber/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: 'saber',
3 | rules: {
4 | '@typescript-eslint/triple-slash-reference': 'off'
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/packages/saber/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) EGOIST <0x142857@gmail.com> (https://github.com/egoist)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/saber/babel.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./dist/babel/preset')
2 |
--------------------------------------------------------------------------------
/packages/saber/declarations.d.ts:
--------------------------------------------------------------------------------
1 | declare type TODO = any
2 |
3 | declare type NonUndefined = A extends undefined ? never : A
4 |
--------------------------------------------------------------------------------
/packages/saber/example/pages/_bar/hehe.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saberland/saber/e63d22041a9397bec38468caadec6bd7b923d8b6/packages/saber/example/pages/_bar/hehe.md
--------------------------------------------------------------------------------
/packages/saber/example/pages/_foo.vue:
--------------------------------------------------------------------------------
1 |
2 | foo!!x
3 |
4 |
--------------------------------------------------------------------------------
/packages/saber/example/pages/_posts/bar.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: barbarbar
3 | ---
4 |
5 | this is good, hmm, maybe..
6 |
--------------------------------------------------------------------------------
/packages/saber/example/pages/_posts/foo.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: sdf
3 | tags:
4 | - hello
5 | ---
6 |
7 | xxxsdfsdfdsd
8 |
9 | sdfsf
10 |
11 | sdf
12 |
13 | sdfsdfsaf
14 |
15 | sdfdsf
16 |
17 | {{ count }}
18 |
19 | __asdasd__
20 |
21 | asdsfsd
22 |
23 | dasdasdas
24 |
25 | sdfsdfsdfsdfasdsa
26 |
27 | asdsdsdfsdf
28 |
29 |
38 |
--------------------------------------------------------------------------------
/packages/saber/example/pages/about.md:
--------------------------------------------------------------------------------
1 | Hello **world**
2 |
--------------------------------------------------------------------------------
/packages/saber/example/pages/hehe.css:
--------------------------------------------------------------------------------
1 | .page-title {
2 | font-style: italic;
3 | }
4 |
--------------------------------------------------------------------------------
/packages/saber/example/pages/hehe.module.css:
--------------------------------------------------------------------------------
1 | .title {
2 | color: red;
3 | }
4 |
--------------------------------------------------------------------------------
/packages/saber/example/pages/hehe.vue:
--------------------------------------------------------------------------------
1 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/packages/saber/example/pages/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: haha
3 | ---
4 |
5 | aaa
6 |
7 | Go to hehe
8 |
--------------------------------------------------------------------------------
/packages/saber/example/pages/wow.js:
--------------------------------------------------------------------------------
1 | export default {
2 | render(h) {
3 | return h('div', {}, [
4 | h('h1', {}, ['wow'])
5 | ])
6 | }
7 | }
8 |
9 | export const attributes = {
10 | title: 'wow'
11 | }
12 |
13 | console.log('egoist is baka baka baka')
14 |
--------------------------------------------------------------------------------
/packages/saber/example/public/test.txt:
--------------------------------------------------------------------------------
1 | test
2 |
--------------------------------------------------------------------------------
/packages/saber/example/saber-browser.js:
--------------------------------------------------------------------------------
1 | export default context => {}
2 |
--------------------------------------------------------------------------------
/packages/saber/example/saber-config.json:
--------------------------------------------------------------------------------
1 | {
2 | "markdown": {
3 | "plugins": [
4 | "markdown-it-footnote"
5 | ]
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/packages/saber/example/saber-node.js:
--------------------------------------------------------------------------------
1 | exports.onCreatePage = function(page) {
2 | if (page.permalink === '/') {
3 | page.foo = 'ass'
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/packages/saber/example/src/sum.js:
--------------------------------------------------------------------------------
1 | export default (a, b) => a + b
2 |
--------------------------------------------------------------------------------
/packages/saber/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | testPathIgnorePatterns: ['/node_modules/', '/types/', '/dist/'],
3 | testEnvironment: 'node'
4 | }
5 |
--------------------------------------------------------------------------------
/packages/saber/src/BrowserApi.ts:
--------------------------------------------------------------------------------
1 | import path from 'path'
2 | import { fs, slash } from 'saber-utils'
3 | import { Saber } from './'
4 |
5 | export class BrowserApi extends Set {
6 | api: Saber
7 |
8 | constructor(api: Saber) {
9 | super()
10 | this.api = api
11 | }
12 |
13 | /**
14 | * Register a file that implements Saber's browser API
15 | */
16 | add(filepath: string) {
17 | super.add(filepath)
18 | return this
19 | }
20 |
21 | async reload() {
22 | const files = [...this.values()].map((file, i) => {
23 | const name = `_${path.basename(file).replace(/\W/gi, '_')}_${i}`
24 | return {
25 | name,
26 | path: slash(file)
27 | }
28 | })
29 |
30 | const output = `
31 | ${files
32 | .map(file => `var ${file.name} = require("${file.path}").default`)
33 | .join('\n')}
34 |
35 | var themeBrowserApi
36 | var rTheme = require.context('#theme', false, /\\.\\/saber-browser\\.[jt]s$/)
37 | rTheme.keys().forEach(function (k) {
38 | themeBrowserApi = rTheme(k).default
39 | })
40 |
41 | export default function (context) {
42 | ${files
43 | .map(
44 | file =>
45 | `typeof ${file.name} === 'function' && ${file.name}(context)`
46 | )
47 | .join('\n')}
48 | typeof themeBrowserApi === 'function' && themeBrowserApi(context)
49 | }`
50 |
51 | await fs.outputFile(
52 | this.api.resolveCache('extend-browser-api.js'),
53 | output,
54 | 'utf8'
55 | )
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/packages/saber/src/Transformers.ts:
--------------------------------------------------------------------------------
1 | import { CreatePageInput, Page } from './Pages'
2 |
3 | export interface Transformer {
4 | extensions: string[]
5 | transform?: (page: CreatePageInput) => void
6 | getPageComponent: (page: Page) => string
7 | }
8 |
9 | export class Transformers {
10 | transformers: Map
11 |
12 | constructor() {
13 | this.transformers = new Map()
14 | }
15 |
16 | get parseFrontmatter() {
17 | return require('./utils/parseFrontmatter')
18 | }
19 |
20 | add(contentType: string, transformer: Transformer) {
21 | this.transformers.set(contentType, transformer)
22 | }
23 |
24 | get(contentType: string) {
25 | return this.transformers.get(contentType)
26 | }
27 |
28 | get supportedExtensions() {
29 | let extensions: string[] = []
30 | for (const transformer of this.transformers.values()) {
31 | extensions = [...extensions, ...(transformer.extensions || [])]
32 | }
33 |
34 | return extensions
35 | }
36 |
37 | getContentTypeByExtension(extension: string) {
38 | for (const [contentType, transformer] of this.transformers.entries()) {
39 | if (
40 | transformer.extensions &&
41 | transformer.extensions.includes(extension)
42 | ) {
43 | return contentType
44 | }
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/packages/saber/src/WebpackUtils.ts:
--------------------------------------------------------------------------------
1 | import path from 'path'
2 | import { Rule } from 'webpack-chain'
3 | import { Saber } from './'
4 |
5 | export class WebpackUtils {
6 | api: Saber
7 |
8 | constructor(api: Saber) {
9 | this.api = api
10 | }
11 |
12 | get shouldCache() {
13 | return this.api.config.build.cache !== false
14 | }
15 |
16 | getCacheOptions(loader: string, obj: object) {
17 | return this.shouldCache
18 | ? {
19 | cacheDirectory: this.api.resolveCache(path.join('cache', loader)),
20 | cacheIdentifier:
21 | obj && JSON.stringify(typeof obj === 'function' ? obj() : obj)
22 | }
23 | : {}
24 | }
25 |
26 | addCacheSupport(rule: Rule, loader: string, obj: object) {
27 | if (this.shouldCache) {
28 | rule
29 | .use('cache-loader')
30 | .loader(require.resolve('cache-loader'))
31 | .options(this.getCacheOptions(loader, obj))
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/packages/saber/src/cli-commands/build.js:
--------------------------------------------------------------------------------
1 | const { setNodeEnv, handleError } = require('./utils')
2 |
3 | module.exports = function(cli) {
4 | cli
5 | .command(
6 | 'build [app-path]',
7 | 'Compile the application and generate static HTML files',
8 | {
9 | ignoreOptionDefaultValue: true
10 | }
11 | )
12 | .alias('generate')
13 | .option('--skip-compilation', 'Skip the webpack compilation process')
14 | .option('--inspect-webpack', 'Inspect webpack config in your editor')
15 | .option('--no-cache', 'Disable cache')
16 | .action(
17 | handleError((cwd = '.', options) => {
18 | setNodeEnv('production')
19 |
20 | if (cli.matchedCommandName === 'generate') {
21 | require('saber-log').log.warn(
22 | `The "generate" command is now deprecated, please use "build" instead.`
23 | )
24 | }
25 |
26 | const { skipCompilation, cache } = options
27 | delete options.skipCompilation
28 | delete options.cache
29 |
30 | return require('..')
31 | .createSaber(Object.assign({ cwd, dev: false }, options), {
32 | build: {
33 | cache
34 | }
35 | })
36 | .build({ skipCompilation })
37 | })
38 | )
39 | }
40 |
--------------------------------------------------------------------------------
/packages/saber/src/cli-commands/dev.js:
--------------------------------------------------------------------------------
1 | const { setNodeEnv, handleError } = require('./utils')
2 |
3 | module.exports = function(cli) {
4 | cli
5 | .command('[app-path]', 'Run the application in dev mode', {
6 | ignoreOptionDefaultValue: true
7 | })
8 | .alias('dev')
9 | .option('--lazy', 'Enable lazy page compilation')
10 | .option('--port ', 'Server port', { default: 3000 })
11 | .option('--host ', 'Server host', { default: '0.0.0.0' })
12 | .option('--inspect-webpack', 'Inspect webpack config in your editor')
13 | .option('--no-cache', 'Disable cache')
14 | .action(
15 | handleError((cwd = '.', options) => {
16 | setNodeEnv('development')
17 |
18 | const { host, port, lazy, cache } = options
19 | delete options.host
20 | delete options.port
21 | delete options.lazy
22 | return require('..')
23 | .createSaber(Object.assign({ cwd, dev: true }, options), {
24 | server: {
25 | host,
26 | port
27 | },
28 | build: {
29 | lazy,
30 | cache
31 | }
32 | })
33 | .serve()
34 | })
35 | )
36 | }
37 |
--------------------------------------------------------------------------------
/packages/saber/src/cli-commands/index.js:
--------------------------------------------------------------------------------
1 | const commands = ['dev', 'build', 'serve', 'eject-theme']
2 |
3 | module.exports = function(cli) {
4 | commands.forEach(command => require(`./${command}`)(cli))
5 | }
6 |
--------------------------------------------------------------------------------
/packages/saber/src/cli-commands/serve.js:
--------------------------------------------------------------------------------
1 | const { setNodeEnv, handleError } = require('./utils')
2 |
3 | module.exports = function(cli) {
4 | cli
5 | .command('serve [app-path]', 'Serve the output directory')
6 | .option('--host ', 'Server host', { default: '0.0.0.0' })
7 | .option('--port ', 'Server port', { default: 3000 })
8 | .action(
9 | handleError((cwd = '.', options) => {
10 | setNodeEnv('production')
11 |
12 | const { host, port } = options
13 | delete options.host
14 | delete options.port
15 | return require('..')
16 | .createSaber(Object.assign({ cwd, dev: true }, options), {
17 | server: {
18 | host,
19 | port
20 | }
21 | })
22 | .serveOutDir()
23 | })
24 | )
25 | }
26 |
--------------------------------------------------------------------------------
/packages/saber/src/cli-commands/utils.js:
--------------------------------------------------------------------------------
1 | const { spawn } = require('child_process')
2 |
3 | module.exports = {
4 | setNodeEnv(env) {
5 | if (!process.env.NODE_ENV) {
6 | process.env.NODE_ENV = env
7 | }
8 | },
9 | handleError(fn) {
10 | return async (...args) => {
11 | try {
12 | await fn(...args)
13 | } catch (error) {
14 | const message = typeof error === 'string' ? error : error.stack
15 | require('saber-log').log.error(message)
16 | process.exit(1) // eslint-disable-line
17 | }
18 | }
19 | },
20 | spawn(...args) {
21 | return new Promise((resolve, reject) => {
22 | const childProcess = spawn(...args)
23 | childProcess.on('close', resolve)
24 | childProcess.on('error', reject)
25 | })
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/packages/saber/src/cli.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | const cac = require('cac')
3 |
4 | const cli = cac()
5 | require('./cli-commands')(cli)
6 |
7 | cli.option('-V, --verbose', 'Output verbose logs')
8 | cli.option('--no-progress', 'Disable progress bar')
9 |
10 | cli.version(require('../package').version)
11 |
12 | cli.help()
13 |
14 | cli.parse()
15 |
16 | process.on('SIGINT', () => {
17 | const { log } = require('saber-log')
18 | log.log('')
19 | log.info(`See you later, master!`) // <-- Saber says
20 | process.exit()
21 | })
22 |
23 | process.on('unhandledRejection', error => {
24 | const { log } = require('saber-log')
25 | log.error(error.stack)
26 | process.exitCode = 1
27 | })
28 |
--------------------------------------------------------------------------------
/packages/saber/src/config-chain/Options.js:
--------------------------------------------------------------------------------
1 | const ChainedMap = require('webpack-chain/src/ChainedMap')
2 |
3 | module.exports = class extends ChainedMap {
4 | constructor(parent) {
5 | super(parent)
6 |
7 | this.extend([
8 | 'html',
9 | 'xhtmlOut',
10 | 'breaks',
11 | 'langPrefix',
12 | 'linkify',
13 | 'typographer',
14 | 'quotes',
15 | 'highlight'
16 | ])
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/packages/saber/src/config-chain/Plugin.js:
--------------------------------------------------------------------------------
1 | const ChainedMap = require('webpack-chain/src/ChainedMap')
2 | const Orderable = require('webpack-chain/src/Orderable')
3 |
4 | // eslint-disable-next-line new-cap
5 | module.exports = Orderable(
6 | class Plugin extends ChainedMap {
7 | constructor(parent) {
8 | super(parent)
9 | this.extend(['init'])
10 |
11 | this.init((plugin, args = []) => ({ plugin, args }))
12 | }
13 |
14 | use(plugin, args = []) {
15 | return this.set('plugin', plugin).set('args', args)
16 | }
17 |
18 | tap(f) {
19 | this.set('args', f(this.get('args') || []))
20 | return this
21 | }
22 |
23 | merge(obj, omit = []) {
24 | if ('plugin' in obj) {
25 | this.set('plugin', obj.plugin)
26 | }
27 |
28 | if ('args' in obj) {
29 | this.set('args', obj.args)
30 | }
31 |
32 | return super.merge(obj, [...omit, 'args', 'plugin'])
33 | }
34 |
35 | toConfig() {
36 | const init = this.get('init')
37 |
38 | return init(this.get('plugin'), this.get('args'))
39 | }
40 | }
41 | )
42 |
--------------------------------------------------------------------------------
/packages/saber/src/config-chain/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Modified: https://github.com/ulivz/markdown-it-chain/blob/master/src/index.js
3 | */
4 |
5 | const ChainedMap = require('webpack-chain/src/ChainedMap')
6 | const resolvePackage = require('../utils/resolvePackage')
7 | const Plugin = require('./Plugin')
8 | const Options = require('./Options')
9 |
10 | module.exports = class MarkdownItChain extends ChainedMap {
11 | constructor() {
12 | super()
13 | this.options = new Options(this)
14 | this.plugins = new ChainedMap(this)
15 | }
16 |
17 | toConfig() {
18 | return this.clean(
19 | Object.assign(this.entries() || {}, {
20 | options: this.options.entries(),
21 | plugins: this.plugins.values().map(plugin => plugin.toConfig())
22 | })
23 | )
24 | }
25 |
26 | plugin(name) {
27 | if (!this.plugins.has(name)) {
28 | this.plugins.set(name, new Plugin(this))
29 | }
30 |
31 | return this.plugins.get(name)
32 | }
33 |
34 | loadPlugins(rawPluginList, cwd) {
35 | const pluginList = rawPluginList.map(plugin => {
36 | if (typeof plugin === 'string') {
37 | plugin = { resolve: plugin }
38 | }
39 |
40 | plugin.resolve = resolvePackage(plugin.resolve, { cwd })
41 | plugin.name = plugin.name || plugin.resolve
42 | plugin.handler = require(plugin.resolve)
43 |
44 | return plugin
45 | })
46 |
47 | for (const plugin of pluginList) {
48 | this.plugin(plugin.name).use(
49 | plugin.handler,
50 | Array.isArray(plugin.options) ? plugin.options : [plugin.options]
51 | )
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/packages/saber/src/markdown/__test__/__snapshots__/highlight-plugin.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`code block markdown.lineNumbers = true 1`] = `" const cry = Array(3).fill('ora').join(' ')
"`;
4 |
5 | exports[`code block with {lineNumbers:true} 1`] = `" const cry = Array(3).fill('ora').join(' ')
"`;
6 |
7 | exports[`code block with {lineNumbers:true,lineStart:5} 1`] = `" const cry = Array(3).fill('ora').join(' ')
"`;
8 |
9 | exports[`main 1`] = `""`;
10 |
--------------------------------------------------------------------------------
/packages/saber/src/markdown/__test__/create-env.js:
--------------------------------------------------------------------------------
1 | module.exports = _page => {
2 | const page = { ..._page, internal: { ...(_page && _page.internal) } }
3 | return {
4 | page,
5 | env: {
6 | page
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/packages/saber/src/markdown/__test__/excerpt-plugin.test.js:
--------------------------------------------------------------------------------
1 | const Markdown = require('saber-markdown')
2 | const excerptPlugin = require('../excerpt-plugin')
3 | const createEnv = require('./create-env')
4 |
5 | test('use first paragraph as excerpt', () => {
6 | const md = new Markdown()
7 | const { env, page } = createEnv()
8 | md.use(excerptPlugin)
9 | md.render(
10 | `
11 | hello
12 | `,
13 | env
14 | )
15 | expect(page.excerpt).toBe('hello
\n')
16 | })
17 |
18 | test('do not override page excerpt', () => {
19 | const md = new Markdown()
20 | const { env, page } = createEnv({ excerpt: 'existing' })
21 | md.use(excerptPlugin)
22 | md.render(
23 | `
24 | hello
25 | `,
26 | env
27 | )
28 | md.use(excerptPlugin)
29 | expect(page.excerpt).toBe('existing')
30 | })
31 |
32 | test('disable excerpt', () => {
33 | const md = new Markdown()
34 | const { env, page } = createEnv()
35 | page.excerpt = false
36 | md.use(excerptPlugin)
37 | md.render(
38 | `
39 | hello
40 | `,
41 | env
42 | )
43 | expect(page.excerpt).toBe(false)
44 | })
45 |
46 | test(' mark', () => {
47 | const md = new Markdown({
48 | html: true
49 | })
50 | const { env, page } = createEnv()
51 | md.use(excerptPlugin)
52 | md.render(
53 | `
54 | hello
55 |
56 | world
57 |
58 |
59 |
60 | wow
61 | `,
62 | env
63 | )
64 | expect(page.excerpt).toBe('hello
\nworld
\n')
65 | })
66 |
--------------------------------------------------------------------------------
/packages/saber/src/markdown/__test__/headings-plugin.test.js:
--------------------------------------------------------------------------------
1 | const Markdown = require('saber-markdown')
2 | const headingsPlugin = require('../headings-plugin')
3 | const createEnv = require('./create-env')
4 |
5 | const input = `
6 | # Heading
7 |
8 | ## \`Heading\`
9 |
10 | ### [This time around, a link is present](http://localhost)
11 |
12 | #### Deep on so many levels!
13 |
14 | ##### Still in there`
15 |
16 | test('inject markdown headings enabled by default', () => {
17 | const md = new Markdown()
18 | const { env, page } = createEnv()
19 | md.use(headingsPlugin)
20 | md.render(input, env)
21 | expect(page.markdownHeadings).toEqual([
22 | {
23 | text: 'Heading',
24 | slug: 'heading',
25 | level: 1
26 | },
27 | {
28 | text: 'Heading',
29 | slug: 'heading-2',
30 | level: 2
31 | },
32 | {
33 | text: 'This time around, a link is present',
34 | slug: 'this-time-around-a-link-is-present',
35 | level: 3
36 | },
37 | {
38 | text: 'Deep on so many levels!',
39 | slug: 'deep-on-so-many-levels',
40 | level: 4
41 | },
42 | {
43 | text: 'Still in there',
44 | slug: 'still-in-there',
45 | level: 5
46 | }
47 | ])
48 | })
49 |
50 | test('inject markdown headings disabled', () => {
51 | const md = new Markdown()
52 | const { env, page } = createEnv()
53 | page.markdownHeadings = false
54 | md.use(headingsPlugin)
55 | md.render(input, env)
56 | expect(page.markdownHeadings).toEqual([])
57 | })
58 |
--------------------------------------------------------------------------------
/packages/saber/src/markdown/__test__/highlight-plugin.test.js:
--------------------------------------------------------------------------------
1 | const Markdown = require('saber-markdown')
2 | const fenceOptionsPlugin = require('../highlight-plugin')
3 | const createEnv = require('./create-env')
4 |
5 | test('main', () => {
6 | const md = new Markdown()
7 | const { env } = createEnv()
8 | md.use(fenceOptionsPlugin)
9 | const html = md.render(
10 | `
11 | \`\`\`vue
12 | hehe
13 | \`\`\`
14 | `,
15 | env
16 | )
17 | expect(html).toMatchSnapshot()
18 | })
19 |
20 | test('code block with {lineNumbers:true}', () => {
21 | const md = new Markdown()
22 | const { env } = createEnv()
23 | md.use(fenceOptionsPlugin)
24 | const html = md.render(
25 | `
26 | \`\`\`js {lineNumbers:true}
27 | const cry = Array(3).fill('ora').join(' ')
28 | \`\`\`
29 | `,
30 | env
31 | )
32 | expect(html).toMatchSnapshot()
33 | })
34 |
35 | test('code block with {lineNumbers:true,lineStart:5}', () => {
36 | const md = new Markdown()
37 | const { env } = createEnv()
38 | md.use(fenceOptionsPlugin)
39 | const html = md.render(
40 | `
41 | \`\`\`js {lineNumbers:true,lineStart:5}
42 | const cry = Array(3).fill('ora').join(' ')
43 | \`\`\`
44 | `,
45 | env
46 | )
47 | expect(html).toMatchSnapshot()
48 | })
49 |
50 | test('code block markdown.lineNumbers = true', () => {
51 | const md = new Markdown()
52 | const { env } = createEnv()
53 | md.use(fenceOptionsPlugin, { lineNumbers: true })
54 | const html = md.render(
55 | `
56 | \`\`\`js {lineNumbers:true}
57 | const cry = Array(3).fill('ora').join(' ')
58 | \`\`\`
59 | `,
60 | env
61 | )
62 | expect(html).toMatchSnapshot()
63 | })
64 |
--------------------------------------------------------------------------------
/packages/saber/src/markdown/escape-interpolations-plugin.js:
--------------------------------------------------------------------------------
1 | module.exports = md => {
2 | const defaultCodeInline = md.renderer.rules.code_inline
3 | md.renderer.rules.code_inline = (...args) => {
4 | const [tokens, idx] = args
5 | const token = tokens[idx]
6 | token.attrSet('v-pre', '')
7 | return defaultCodeInline(...args)
8 | }
9 |
10 | const defaultFence = md.renderer.rules.fence
11 | md.renderer.rules.fence = (...args) => {
12 | const [tokens, idx] = args
13 | const token = tokens[idx]
14 | token.attrSet('v-pre', '')
15 | return defaultFence(...args)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/packages/saber/src/markdown/excerpt-plugin.js:
--------------------------------------------------------------------------------
1 | const hasExcerptMark = value => //.test(value.trim())
2 |
3 | module.exports = (md, { paragraphOnly = true } = {}) => {
4 | md.renderer.rules.paragraph_close = (...args) => {
5 | const [tokens, idx, options, env, self] = args
6 |
7 | const { page, __excerpted } = env
8 |
9 | const autoExcerpt = !tokens.some(token => {
10 | return token.type === 'html_block' && hasExcerptMark(token.content)
11 | })
12 |
13 | if (
14 | autoExcerpt &&
15 | !__excerpted &&
16 | page.excerpt !== false &&
17 | typeof page.excerpt !== 'string'
18 | ) {
19 | env.__excerpted = true
20 | let startIndex = 0
21 | if (paragraphOnly) {
22 | for (const [index, token] of tokens.entries()) {
23 | if (token.type === 'paragraph_open') {
24 | startIndex = index
25 | break
26 | }
27 | }
28 | }
29 |
30 | page.excerpt = self.render(
31 | tokens.slice(startIndex, idx + 1),
32 | options,
33 | env
34 | )
35 | }
36 |
37 | return self.renderToken(tokens, idx, options)
38 | }
39 |
40 | const htmlRule = md.renderer.rules.html_block
41 |
42 | md.renderer.rules.html_block = (...args) => {
43 | const [tokens, idx, options, env, self] = args
44 | const token = tokens[idx]
45 |
46 | const { __excerpted, page } = env
47 |
48 | if (
49 | hasExcerptMark(token.content) &&
50 | !__excerpted &&
51 | page.excerpt !== false
52 | ) {
53 | page.excerpt = self.render(tokens.slice(0, idx), options, env)
54 | env.__excerpted = true
55 | }
56 |
57 | return htmlRule(...args)
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/packages/saber/src/markdown/hoist-tags-plugin.js:
--------------------------------------------------------------------------------
1 | module.exports = md => {
2 | const RE = /^<(style|script)(?=(\s|>|$))/i
3 |
4 | md.renderer.rules.html_block = (tokens, idx, options, env) => {
5 | const { content } = tokens[idx]
6 | const hoistedTags = env.page.internal.hoistedTags || []
7 | if (RE.test(content.trim())) {
8 | hoistedTags.push(content)
9 | env.page.internal.hoistedTags = hoistedTags
10 | return ''
11 | }
12 |
13 | return content
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/packages/saber/src/plugins/config-font.js:
--------------------------------------------------------------------------------
1 | const getFileNames = require('../utils/getFileNames')
2 |
3 | const ID = 'builtin:config-font'
4 |
5 | exports.name = ID
6 |
7 | exports.apply = api => {
8 | api.hooks.chainWebpack.tap(ID, config => {
9 | const filename = getFileNames(!api.dev).font
10 | config.module
11 | .rule('font')
12 | .test(/\.(eot|otf|ttf|woff|woff2)(\?.*)?$/)
13 | .use('file-loader')
14 | .loader(require.resolve('file-loader'))
15 | .options({
16 | name: filename
17 | })
18 | })
19 | }
20 |
--------------------------------------------------------------------------------
/packages/saber/src/plugins/config-image.js:
--------------------------------------------------------------------------------
1 | const getFileNames = require('../utils/getFileNames')
2 |
3 | const ID = 'builtin:config-image'
4 |
5 | exports.name = ID
6 |
7 | exports.apply = api => {
8 | api.hooks.chainWebpack.tap(ID, config => {
9 | const filename = getFileNames(!api.dev).image
10 |
11 | config.module
12 | .rule('image')
13 | .test([/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/, /\.webp$/])
14 | .use('url-loader')
15 | .loader(require.resolve('url-loader'))
16 | .options({
17 | name: filename,
18 | // inline the file if smaller than 2KB
19 | limit: 20000
20 | })
21 |
22 | config.module
23 | .rule('svg')
24 | .test(/\.(svg)(\?.*)?$/)
25 | .use('file-loader')
26 | // SVG files use file-loader directly
27 | // See https://github.com/facebookincubator/create-react-app/pull/1180
28 | .loader(require.resolve('file-loader'))
29 | .options({
30 | name: filename
31 | })
32 | })
33 | }
34 |
--------------------------------------------------------------------------------
/packages/saber/src/plugins/config-other-loaders.js:
--------------------------------------------------------------------------------
1 | const ID = 'builtin:config-other-loaders'
2 |
3 | exports.name = ID
4 |
5 | exports.apply = api => {
6 | api.hooks.chainWebpack.tap(ID, config => {
7 | // GraphQL
8 | config.module
9 | .rule('graphql')
10 | .test(/\.(graphql|gql)$/)
11 | .use('graphql-tag')
12 | .loader('graphql-tag/loader')
13 |
14 | config.module
15 | .rule('toml')
16 | .merge({
17 | type: 'json'
18 | })
19 | .test(/\.toml$/)
20 | .use('toml-loader')
21 | .loader(require.resolve('../webpack/toml-loader'))
22 |
23 | config.module
24 | .rule('yaml')
25 | .test(/\.ya?ml$/)
26 | .merge({
27 | type: 'json'
28 | })
29 | .use('yaml-loader')
30 | .loader(require.resolve('../webpack/yaml-loader'))
31 | })
32 | }
33 |
--------------------------------------------------------------------------------
/packages/saber/src/plugins/emit-runtime-polyfills.js:
--------------------------------------------------------------------------------
1 | const { fs, slash } = require('saber-utils')
2 |
3 | const ID = 'builtin:emit-runtime-polyfills'
4 |
5 | let previousPolyfills
6 |
7 | exports.name = ID
8 |
9 | exports.apply = api => {
10 | api.hooks.afterPlugins.tap(ID, () => {
11 | api.hooks.emitRoutes.tapPromise(ID, async () => {
12 | const polyfills = [...api.runtimePolyfills]
13 | .map(file => `import '${slash(file)}'`)
14 | .join('\n')
15 | if (polyfills !== previousPolyfills) {
16 | await fs.outputFile(
17 | api.resolveCache('runtime-polyfills.js'),
18 | polyfills,
19 | 'utf8'
20 | )
21 | // eslint-disable-next-line require-atomic-updates
22 | previousPolyfills = polyfills
23 | }
24 | })
25 | })
26 | }
27 |
--------------------------------------------------------------------------------
/packages/saber/src/plugins/emit-saber-variables.js:
--------------------------------------------------------------------------------
1 | const { fs } = require('saber-utils')
2 |
3 | const ID = 'builtin:emit-saber-variables'
4 |
5 | exports.name = ID
6 |
7 | exports.apply = api => {
8 | api.hooks.beforeRun.tapPromise(ID, async () => {
9 | const variables = {}
10 | api.hooks.defineVariables.call(variables)
11 | await fs.outputFile(
12 | api.resolveCache('variables.json'),
13 | JSON.stringify(variables),
14 | 'utf8'
15 | )
16 | })
17 | }
18 |
--------------------------------------------------------------------------------
/packages/saber/src/plugins/extend-browser-api.js:
--------------------------------------------------------------------------------
1 | const chokidar = require('chokidar')
2 | const configLoader = require('../utils/configLoader')
3 |
4 | const ID = 'builtin:extend-browser-api'
5 |
6 | exports.name = ID
7 |
8 | exports.apply = api => {
9 | const USER_BROWSER_API_FILES = ['saber-browser.js', 'saber-browser.ts']
10 |
11 | const browserApiFile = configLoader.resolve({
12 | files: USER_BROWSER_API_FILES,
13 | cwd: api.resolveCwd()
14 | })
15 | if (browserApiFile) {
16 | api.browserApi.add(browserApiFile)
17 | }
18 |
19 | api.hooks.beforeRun.tapPromise(ID, async () => {
20 | await api.browserApi.reload()
21 |
22 | if (api.dev) {
23 | const watcher = chokidar.watch(USER_BROWSER_API_FILES, {
24 | cwd: api.resolveCwd(),
25 | ignoreInitial: true
26 | })
27 | const onAdd = async filename => {
28 | api.browserApi.add(api.resolveCwd(filename))
29 | await api.browserApi.reload()
30 | }
31 | const onRemove = async filename => {
32 | api.browserApi.delete(api.resolveCwd(filename))
33 | await api.browserApi.reload()
34 | }
35 | watcher
36 | .on('add', filename => {
37 | onAdd(filename)
38 | })
39 | .on('unlink', filename => {
40 | onRemove(filename)
41 | })
42 | }
43 | })
44 | }
45 |
--------------------------------------------------------------------------------
/packages/saber/src/plugins/index.js:
--------------------------------------------------------------------------------
1 | module.exports = [
2 | { resolve: require.resolve('./source-pages') },
3 | { resolve: require.resolve('./extend-browser-api') },
4 | { resolve: require.resolve('./extend-node-api') },
5 | { resolve: require.resolve('./transformer-markdown') },
6 | { resolve: require.resolve('./transformer-default') },
7 | { resolve: require.resolve('./transformer-components') },
8 | { resolve: require.resolve('./config-css') },
9 | { resolve: require.resolve('./config-image') },
10 | { resolve: require.resolve('./config-font') },
11 | { resolve: require.resolve('./config-other-loaders') },
12 | { resolve: require.resolve('./watch-config') },
13 | { resolve: require.resolve('./layouts') },
14 | { resolve: require.resolve('./emit-saber-variables') },
15 | { resolve: require.resolve('./emit-runtime-polyfills') }
16 | ]
17 |
--------------------------------------------------------------------------------
/packages/saber/src/plugins/transformer-components.js:
--------------------------------------------------------------------------------
1 | const { slash } = require('saber-utils')
2 |
3 | exports.name = 'builtin:transformer-components'
4 |
5 | const getPageComponent = page => {
6 | return `
21 | `
22 | }
23 |
24 | exports.apply = api => {
25 | api.transformers.add('vue', {
26 | extensions: ['vue'],
27 | transform(page) {
28 | const sfc = require('vue-template-compiler').parseComponent(page.content)
29 | if (sfc.script) {
30 | const { data } = require('../utils/parseAttributes')(
31 | sfc.script.content,
32 | page.internal.absolute
33 | )
34 | Object.assign(page, data)
35 | }
36 | },
37 | getPageComponent
38 | })
39 |
40 | api.transformers.add('js', {
41 | extensions: ['js'],
42 | transform(page) {
43 | const { data } = require('../utils/parseAttributes')(
44 | page.content,
45 | page.internal.absolute
46 | )
47 | Object.assign(page, data)
48 | },
49 | getPageComponent
50 | })
51 | }
52 |
--------------------------------------------------------------------------------
/packages/saber/src/plugins/transformer-default.js:
--------------------------------------------------------------------------------
1 | exports.name = 'builtin:transformer-markdefault'
2 |
3 | exports.apply = api => {
4 | api.transformers.add('default', {
5 | getPageComponent(page) {
6 | return `
7 |
8 |
9 | ${page.content || ''}
10 |
11 |
12 | `
13 | }
14 | })
15 | }
16 |
--------------------------------------------------------------------------------
/packages/saber/src/utils/__test__/getPermalink.test.js:
--------------------------------------------------------------------------------
1 | const getPermalink = require('../getPermalink')
2 |
3 | test('use default permalink', () => {
4 | const samples = [
5 | { slug: 'index', permalink: '/' },
6 | { slug: 'foo/index', permalink: '/foo' },
7 | { slug: 'foo/bar', permalink: '/foo/bar.html' }
8 | ]
9 | for (const sample of samples) {
10 | const receivedPermalink = getPermalink(
11 | [],
12 | {
13 | slug: sample.slug,
14 | type: 'page',
15 | createdAt: new Date('2019-01-01')
16 | },
17 | {}
18 | )
19 | expect(receivedPermalink).toBe(sample.permalink)
20 | }
21 | })
22 |
23 | test('remove .html extension', () => {
24 | const samples = [
25 | { slug: 'index', permalink: '/' },
26 | { slug: 'foo/index', permalink: '/foo' },
27 | { slug: 'foo/bar', permalink: '/foo/bar' }
28 | ]
29 | for (const sample of samples) {
30 | const receivedPermalink = getPermalink(
31 | [],
32 | {
33 | slug: sample.slug,
34 | type: 'page',
35 | createdAt: new Date('2019-01-01')
36 | },
37 | {
38 | page: '/:slug'
39 | }
40 | )
41 | expect(receivedPermalink).toBe(sample.permalink)
42 | }
43 | })
44 |
--------------------------------------------------------------------------------
/packages/saber/src/utils/__test__/resolvePackage.test.js:
--------------------------------------------------------------------------------
1 | const resolvePackage = require('../resolvePackage')
2 |
3 | test('resolvePackage', () => {
4 | expect(resolvePackage('foo', { cwd: false })).toBe('foo')
5 | expect(resolvePackage('@foo/bar', { cwd: false })).toBe('@foo/bar')
6 | expect(resolvePackage('foo', { prefix: 'prefix-', cwd: false })).toBe(
7 | 'prefix-foo'
8 | )
9 | expect(resolvePackage('prefix-foo', { prefix: 'prefix-', cwd: false })).toBe(
10 | 'prefix-foo'
11 | )
12 | expect(resolvePackage('@foo/bar', { prefix: 'prefix-', cwd: false })).toBe(
13 | '@foo/prefix-bar'
14 | )
15 | expect(
16 | resolvePackage('@foo/prefix-bar', { prefix: 'prefix-', cwd: false })
17 | ).toBe('@foo/prefix-bar')
18 | })
19 |
--------------------------------------------------------------------------------
/packages/saber/src/utils/assetsAttribute.ts:
--------------------------------------------------------------------------------
1 | import { join } from 'path'
2 | import { slash, isAbsoluteUrl } from 'saber-utils'
3 |
4 | /**
5 | * It's considered external resource
6 | * When it's an absolute url or starting with `/`
7 | * `/path` is used to reference files in static folder
8 | */
9 | const isExternal = (str: string) => isAbsoluteUrl(str) || str.startsWith('/')
10 |
11 | const MARK = '@@!!SABER_ASSET_MARK_e5968b9a!!@@'
12 |
13 | const MARK_GLOBAL_RE = new RegExp(`"${MARK}([^"]+)"`, 'g')
14 |
15 | /**
16 | * Prefix MARK to asset path
17 | */
18 | const prefixAssets = (assets: { [key: string]: string }, cwd: string) => {
19 | const result: { [key: string]: string } = {}
20 | for (const key of Object.keys(assets)) {
21 | const value = assets[key]
22 | if (!isExternal(value) && !value.startsWith(MARK)) {
23 | const path = value.startsWith('@')
24 | ? value
25 | : value.startsWith('module:')
26 | ? value.slice(7)
27 | : slash(join(cwd, value))
28 | result[key] = `${MARK}${path}`
29 | } else {
30 | result[key] = value
31 | }
32 | }
33 |
34 | return result
35 | }
36 |
37 | /**
38 | * Replace strings starting with the MARK to `require` call
39 | */
40 | const requireAssets = (str: string) =>
41 | str.replace(MARK_GLOBAL_RE, (_, p1) => {
42 | return `require("${p1}")`
43 | })
44 |
45 | export { prefixAssets, requireAssets }
46 |
--------------------------------------------------------------------------------
/packages/saber/src/utils/configLoader.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 | const path = require('path')
3 | const fs = require('fs')
4 | const JoyCon = require('joycon').default
5 |
6 | const joycon = new JoyCon({
7 | stopDir: path.dirname(process.cwd())
8 | })
9 |
10 | joycon.addLoader({
11 | test: /\.ya?ml$/,
12 | loadSync: filepath =>
13 | require('./yaml.min').safeLoad(fs.readFileSync(filepath, 'utf8'))
14 | })
15 |
16 | joycon.addLoader({
17 | test: /\.toml$/,
18 | loadSync: filepath =>
19 | require('./toml.min').parse(fs.readFileSync(filepath, 'utf8'))
20 | })
21 |
22 | module.exports = {
23 | /**
24 | * Load config files synchronously
25 | * @param {import('joycon').Options} opts
26 | */
27 | load(opts) {
28 | joycon.clearCache()
29 | return joycon.loadSync(opts)
30 | },
31 | /**
32 | * Resolve config files synchronously
33 | * @param {import('joycon').Options} opts
34 | */
35 | resolve(opts) {
36 | joycon.clearCache()
37 | return joycon.resolveSync(opts)
38 | },
39 | CONFIG_FILES: [
40 | 'saber-config.json',
41 | 'saber-config.js',
42 | 'saber-config.yml',
43 | 'saber-config.toml'
44 | ]
45 | }
46 |
--------------------------------------------------------------------------------
/packages/saber/src/utils/getFileNames.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 | /**
3 | * @param {string} useHash
4 | */
5 | module.exports = useHash => {
6 | return {
7 | js: useHash ? 'js/[name].[chunkhash:8].js' : 'js/[name].js',
8 | css: useHash ? 'css/[name].[chunkhash:8].css' : 'css/[name].css',
9 | font: useHash ? 'fonts/[name].[hash:8].[ext]' : 'fonts/[path][name].[ext]',
10 | image: useHash
11 | ? 'images/[name].[hash:8].[ext]'
12 | : 'images/[path][name].[ext]'
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/packages/saber/src/utils/getPageType.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 | /**
3 | * @param {string} relative - The relative path of a file
4 | */
5 | module.exports = relative => {
6 | if (relative.startsWith('_posts/')) {
7 | return 'post'
8 | }
9 |
10 | return 'page'
11 | }
12 |
--------------------------------------------------------------------------------
/packages/saber/src/utils/inspectWebpack.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 | const path = require('path')
3 | const os = require('os')
4 | const { fs } = require('saber-utils')
5 |
6 | const ID = Math.round(Math.random() * 1000)
7 |
8 | /**
9 | * Inspect webpack config in your default editor
10 | * @param {import('webpack-chain')} config Webpack-chain instance
11 | * @param {string} type
12 | */
13 | module.exports = async (config, type) => {
14 | const tempFile = path.join(
15 | os.tmpdir(),
16 | `saber-webpack-config-${type}-${ID}.js`
17 | )
18 | await fs.writeFile(tempFile, `var config = ${config.toString()}`, 'utf8')
19 | await require('open')(tempFile)
20 | }
21 |
--------------------------------------------------------------------------------
/packages/saber/src/utils/parseFrontmatter.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 | const { log } = require('saber-log')
3 |
4 | const RE_STARTING = /^(?:\r?\n)*---([a-z]+)?(?:\r?\n)+/
5 |
6 | /**
7 | * @typedef {(str: string) => Object} Parser
8 | * @typedef {{[k: string]: Parser}} IParsers
9 | */
10 |
11 | /**
12 | * @type {IParsers}
13 | */
14 | const parsers = {
15 | yaml: str => require('./yaml.min').safeLoad(str),
16 | yml: str => require('./yaml.min').safeLoad(str),
17 | toml: str => require('./toml.min').parse(str)
18 | }
19 |
20 | /**
21 | * Extract front matter from a page
22 | * @param {string} content
23 | * @param {string} filepath
24 | * @returns {{frontmatter: {[k:string]: any}, body: string}}
25 | */
26 | module.exports = (content, filepath) => {
27 | const getEmpty = () => ({
28 | frontmatter: {},
29 | body: content && content.trim()
30 | })
31 |
32 | if (!content) {
33 | return getEmpty()
34 | }
35 |
36 | const starting = RE_STARTING.exec(content)
37 | if (!starting) {
38 | return getEmpty()
39 | }
40 |
41 | const parseType = starting[1] || 'yaml'
42 | const parse = parsers[parseType]
43 | if (!parse) {
44 | throw new Error(`Unsupported front matter type: ${parseType}`)
45 | }
46 |
47 | const rest = content.replace(RE_STARTING, '')
48 | const index = rest.indexOf('\n---')
49 | const head = rest.slice(0, index)
50 | const body = rest.slice(index + 4)
51 | let frontmatter
52 | try {
53 | frontmatter = parse(head)
54 | } catch (error) {
55 | if (filepath) {
56 | log.error(`Error parsing front matter in ${filepath}`)
57 | }
58 |
59 | throw error
60 | }
61 |
62 | return {
63 | frontmatter,
64 | body: body && body.trim()
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/packages/saber/src/utils/prettyBytes.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 |
3 | const UNITS = ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
4 |
5 | /*
6 | Formats the given number using `Number#toLocaleString`.
7 | - If locale is a string, the value is expected to be a locale-key (for example: `de`).
8 | - If locale is true, the system default locale is used for translation.
9 | - If no value for locale is specified, the number is returned unmodified.
10 | */
11 | const toLocaleString = (number, locale) => {
12 | let result = number
13 | if (typeof locale === 'string') {
14 | result = number.toLocaleString(locale)
15 | } else if (locale === true) {
16 | result = number.toLocaleString()
17 | }
18 |
19 | return result
20 | }
21 |
22 | module.exports = (number, options) => {
23 | if (!Number.isFinite(number)) {
24 | throw new TypeError(
25 | `Expected a finite number, got ${typeof number}: ${number}`
26 | )
27 | }
28 |
29 | options = Object.assign({}, options)
30 |
31 | if (options.signed && number === 0) {
32 | return ' 0 B'
33 | }
34 |
35 | const isNegative = number < 0
36 | const prefix = isNegative ? '-' : options.signed ? '+' : ''
37 |
38 | if (isNegative) {
39 | number = -number
40 | }
41 |
42 | if (number < 1) {
43 | const numberString = toLocaleString(number, options.locale)
44 | return prefix + numberString + ' B'
45 | }
46 |
47 | const exponent = Math.min(
48 | Math.floor(Math.log10(number) / 3),
49 | UNITS.length - 1
50 | )
51 | number = Number((number / Math.pow(1000, exponent)).toPrecision(3))
52 | const numberString = toLocaleString(number, options.locale)
53 |
54 | const unit = UNITS[exponent]
55 |
56 | return prefix + numberString + ' ' + unit
57 | }
58 |
--------------------------------------------------------------------------------
/packages/saber/src/utils/resolvePackage.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 | const path = require('path')
3 | const resolveFrom = require('resolve-from')
4 |
5 | const LOCAL_PATH_RE = /^[./]|(^[a-zA-Z]:)/
6 |
7 | /**
8 | * Add prefix to package name
9 | * @param {string} input
10 | * @param {string=} prefix
11 | */
12 | const addPrefix = (input, prefix) => {
13 | if (!prefix) return input
14 |
15 | if (input.startsWith('@')) {
16 | return input.replace(new RegExp(`^@(\\w+)/(${prefix})?`), `@$1/${prefix}`)
17 | }
18 |
19 | return input.startsWith(prefix) ? input : `${prefix}${input}`
20 | }
21 |
22 | /**
23 | * @param {string} input
24 | * @param {Object} options
25 | * @param {string|false} [options.cwd=process.cwd()]
26 | * @param {string=} options.prefix
27 | */
28 | module.exports = (input, { cwd = process.cwd(), prefix } = {}) => {
29 | if (LOCAL_PATH_RE.test(input)) {
30 | return cwd === false ? input : path.resolve(cwd, input)
31 | }
32 |
33 | input = addPrefix(input, prefix)
34 |
35 | if (cwd === false) {
36 | return input
37 | }
38 |
39 | return path.dirname(resolveFrom(cwd, `${input}/package.json`))
40 | }
41 |
--------------------------------------------------------------------------------
/packages/saber/src/utils/serveDir.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const { createReadStream } = require('fs')
3 | const { log } = require('saber-log')
4 | const polka = require('polka')
5 | const serveStatic = require('serve-static')
6 |
7 | module.exports = function({ dir, host, port } = {}) {
8 | const server = polka()
9 | server.use(serveStatic(dir))
10 | server.use((req, res, next) => {
11 | if (req.method !== 'GET') return next()
12 | createReadStream(path.join(dir, '404.html')).pipe(res)
13 | })
14 | server.listen(port, host)
15 | const outputHost = host === '0.0.0.0' ? 'localhost' : host
16 | log.info(`Your application is served at http://${outputHost}:${port}`)
17 | }
18 |
--------------------------------------------------------------------------------
/packages/saber/src/vue-renderer/get-initial-document-data.js:
--------------------------------------------------------------------------------
1 | const { prefixSpace } = require('./utils')
2 |
3 | module.exports = context => {
4 | if (!context.metaInfo) {
5 | return {
6 | title: '',
7 | htmlAttrs: '',
8 | headAttrs: '',
9 | bodyAttrs: '',
10 | link: '',
11 | style: '',
12 | headScript: '',
13 | bodyScript: ``,
14 | noscript: '',
15 | meta: ''
16 | }
17 | }
18 |
19 | const {
20 | title,
21 | htmlAttrs,
22 | headAttrs,
23 | bodyAttrs,
24 | link,
25 | style,
26 | script,
27 | noscript,
28 | meta
29 | } = context.metaInfo.inject()
30 |
31 | return {
32 | title: title.text(),
33 | htmlAttrs: `data-saber-ssr${prefixSpace(htmlAttrs.text())}`,
34 | headAttrs: headAttrs.text(),
35 | bodyAttrs: bodyAttrs.text(),
36 | link: link.text(),
37 | style: `${context.renderStyles()}${style.text()}`,
38 | headScript: script.text(),
39 | bodyScript: `${script.text({ body: true })}${context.renderScripts()}`,
40 | noscript: noscript.text(),
41 | meta: meta.text()
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/packages/saber/src/vue-renderer/get-initial-document.js:
--------------------------------------------------------------------------------
1 | const { prefixSpace } = require('./utils')
2 |
3 | /**
4 | * Get the initial HTML sent from server-side
5 | * @param {any} documentData
6 | */
7 | module.exports = ({
8 | title,
9 | meta,
10 | link,
11 | style,
12 | headScript,
13 | bodyScript,
14 | noscript,
15 | bodyAttrs,
16 | headAttrs,
17 | htmlAttrs
18 | }) => {
19 | return `
20 |
21 |
22 |
23 |
24 |
25 | ${meta} ${title} ${link}
26 | ${style} ${headScript} ${noscript}
27 |
28 |
29 |
30 | ${bodyScript}
31 |
32 |
33 | `
34 | }
35 |
--------------------------------------------------------------------------------
/packages/saber/src/vue-renderer/page-prop-loader.js:
--------------------------------------------------------------------------------
1 | const devalue = require('devalue')
2 | const { requireAssets } = require('../utils/assetsAttribute')
3 |
4 | module.exports = function(source, map) {
5 | const pageId = source.trim()
6 | const { getPagePublicFields } = this.query
7 | const page = requireAssets(devalue(getPagePublicFields(pageId)))
8 | const result = `
9 | export default function(Component) {
10 | var page = ${page}
11 | var beforeCreate = Component.options.beforeCreate || []
12 | Component.options.beforeCreate = [function() {
13 | this.$page = page
14 | }].concat(beforeCreate)
15 |
16 | // These options can be defined as Vue component option or page attribute
17 | // They are also available in layout component except for the 'layout' option
18 | var pageComponentOptions = ['layout', 'transition']
19 |
20 | pageComponentOptions.forEach(function(name) {
21 | var PageComponent = Component.options.PageComponent
22 | if (PageComponent) {
23 | // .vue or .js page, set route transition from PageComponent
24 | Component.options[name] = PageComponent[name]
25 | }
26 |
27 | // Fallback to page attribute
28 | if (Component.options[name] === undefined) {
29 | Component.options[name] = page[name]
30 | }
31 | })
32 |
33 | // page.slug is optional
34 | if (page.slug) {
35 | Component.options.name = 'page-wrapper-' + page.slug.replace(/[^0-9a-z\\-]/ig, '-')
36 | }
37 | if (module.hot) {
38 | var Vue = require('vue').default
39 | Component.options._Ctor = Vue.extend(Component)
40 | }
41 | }
42 | `
43 |
44 | this.callback(null, result, map)
45 | }
46 |
--------------------------------------------------------------------------------
/packages/saber/src/vue-renderer/render-html.js:
--------------------------------------------------------------------------------
1 | const getInitialDocumentData = require('./get-initial-document-data')
2 | const getInitialDocument = require('./get-initial-document')
3 |
4 | module.exports = async (renderer, { url, hooks, isProd }) => {
5 | const context = { url }
6 | context.markup = await renderer.renderToString(context)
7 |
8 | // Get document data that is used to document string
9 | const documentData = hooks.getDocumentData.call(
10 | getInitialDocumentData(context),
11 | context
12 | )
13 |
14 | // Get document string
15 | let document = hooks.getDocument.call(
16 | getInitialDocument(documentData),
17 | context
18 | )
19 |
20 | if (isProd) {
21 | // Remove whitespaces
22 | document = document.replace(/^\s+/gm, '').replace(/\n+${document}`.replace(
27 | '
',
28 | context.markup
29 | ),
30 | context
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/packages/saber/src/vue-renderer/saber-page-loader.js:
--------------------------------------------------------------------------------
1 | const qs = require('querystring')
2 |
3 | module.exports = function(source) {
4 | const pageId =
5 | this.resourceQuery && qs.parse(this.resourceQuery.slice(1)).saberPage
6 |
7 | if (!pageId) return source
8 |
9 | const { getPageById, getTransformerByContentType, resolveCache } = this.query
10 | const page = Object.assign({}, getPageById(pageId))
11 |
12 | this.addDependency(resolveCache(`pages/${pageId}.saberpage`))
13 |
14 | const transformer = getTransformerByContentType(page.contentType)
15 |
16 | return `
17 | ${transformer.getPageComponent(page)}
18 |
19 | ${pageId}
20 |
21 | ${page.internal.hoistedTags ? page.internal.hoistedTags.join('\n') : ''}
22 | `
23 | }
24 |
--------------------------------------------------------------------------------
/packages/saber/src/vue-renderer/template-plugins/__test__/link.test.js:
--------------------------------------------------------------------------------
1 | const posthtml = require('posthtml')
2 | const plugin = require('../link')
3 |
4 | const transform = source =>
5 | posthtml([plugin()])
6 | .process(source, {
7 | recognizeSelfClosing: true
8 | })
9 | .then(res => res.html)
10 |
11 | test('basic', async () => {
12 | const html = await transform(`
13 | foo
14 | foo
15 | foo
16 | foo
17 | foo
18 | `)
19 | expect(html).toBe(`
20 | foo
21 | foo
22 | foo
23 | foo
24 | foo
25 | `)
26 | })
27 |
28 | test('ignore', async () => {
29 | const html = await transform(`foo `)
30 | expect(html).toBe(`foo `)
31 | })
32 |
--------------------------------------------------------------------------------
/packages/saber/src/vue-renderer/template-plugins/index.js:
--------------------------------------------------------------------------------
1 | const ConfigChain = require('../../config-chain')
2 |
3 | module.exports = api => {
4 | const chain = new ConfigChain()
5 | const { template = {} } = api.config
6 |
7 | const builtInPlugins = [
8 | {
9 | name: 'link',
10 | resolve: require.resolve('./link'),
11 | options: {
12 | openLinkInNewTab:
13 | typeof template.openLinkInNewTab === 'boolean'
14 | ? template.openLinkInNewTab
15 | : true
16 | }
17 | }
18 | ]
19 |
20 | // Load built-in plugins
21 | chain.loadPlugins(builtInPlugins, api.configDir)
22 |
23 | api.hooks.chainTemplate.call(chain)
24 |
25 | // Load plugins from config file
26 | if (template.plugins) {
27 | chain.loadPlugins(template.plugins, api.configDir)
28 | }
29 |
30 | const { plugins } = chain.toConfig()
31 |
32 | return plugins.map(plugin => (tree, context) => {
33 | const transform = plugin.plugin(...plugin.args)
34 | return transform(tree, context)
35 | })
36 | }
37 |
--------------------------------------------------------------------------------
/packages/saber/src/vue-renderer/template-plugins/link.js:
--------------------------------------------------------------------------------
1 | const getAttribute = (node, name) => {
2 | if (node.attrs[name] !== undefined) {
3 | return { value: node.attrs[name], isStatic: true }
4 | }
5 |
6 | return {
7 | value: node.attrs[`:${name}`] || node.attrs[`v-bind:${name}`],
8 | isStatic: false
9 | }
10 | }
11 |
12 | const removeAttribute = (node, name) => {
13 | delete node.attrs[name]
14 | delete node.attrs[`:${name}`]
15 | delete node.attrs[`v-bind:${name}`]
16 | }
17 |
18 | module.exports = ({ openLinkInNewTab = true } = {}) => tree => {
19 | tree.walk(node => {
20 | if (!node.attrs) return node
21 |
22 | if ('saber-ignore' in node.attrs) {
23 | delete node.attrs['saber-ignore']
24 | return node
25 | }
26 |
27 | const href = getAttribute(node, 'href')
28 |
29 | if (node.tag === 'a' && href.value) {
30 | node.tag = 'saber-link'
31 | if (href.isStatic) {
32 | node.attrs.to = href.value
33 | } else {
34 | node.attrs[':to'] = href.value
35 | }
36 |
37 | removeAttribute(node, 'href')
38 |
39 | if (
40 | openLinkInNewTab === false &&
41 | getAttribute(node, 'openLinkInNewTab').value === undefined
42 | ) {
43 | node.attrs[':openLinkInNewTab'] = JSON.stringify(openLinkInNewTab)
44 | }
45 | }
46 |
47 | return node
48 | })
49 | }
50 |
--------------------------------------------------------------------------------
/packages/saber/src/vue-renderer/transform-template-loader.js:
--------------------------------------------------------------------------------
1 | const posthtml = require('posthtml')
2 |
3 | module.exports = async function(source) {
4 | const done = this.async()
5 | try {
6 | const { plugins } = this.query
7 | const context = { filename: this.resourcePath }
8 | const { html } = await posthtml(
9 | plugins.map(plugin => tree => plugin(tree, context))
10 | ).process(source, {
11 | recognizeSelfClosing: true
12 | })
13 | done(null, html)
14 | } catch (error) {
15 | done(error)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/packages/saber/src/vue-renderer/utils.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs')
2 |
3 | exports.prefixSpace = input => (input ? ` ${input}` : '')
4 |
5 | exports.readJSON = (file, readFile = fs.readFileSync) =>
6 | JSON.parse(readFile(file, 'utf8'))
7 |
--------------------------------------------------------------------------------
/packages/saber/src/webpack/toml-loader.js:
--------------------------------------------------------------------------------
1 | const toml = require('../utils/toml.min')
2 |
3 | module.exports = function(source) {
4 | return JSON.stringify(toml.parse(source))
5 | }
6 |
--------------------------------------------------------------------------------
/packages/saber/src/webpack/yaml-loader.js:
--------------------------------------------------------------------------------
1 | const yaml = require('../utils/yaml.min')
2 |
3 | module.exports = function(source) {
4 | return JSON.stringify(yaml.safeLoad(source))
5 | }
6 |
--------------------------------------------------------------------------------
/packages/saber/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "include": ["src"],
4 | "compilerOptions": {
5 | "allowJs": true,
6 | "outDir": "dist",
7 | "lib": ["esnext", "dom"],
8 | "declaration": true,
9 | "declarationDir": "types"
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/packages/saber/vue-app/404.vue:
--------------------------------------------------------------------------------
1 |
29 |
30 |
--------------------------------------------------------------------------------
/packages/saber/vue-app/components/ClientOnly.js:
--------------------------------------------------------------------------------
1 | export default {
2 | name: 'ClientOnly',
3 | functional: true,
4 | render(h, { parent, children }) {
5 | if (parent._isMounted) {
6 | return children
7 | }
8 |
9 | parent.$once('hook:mounted', () => {
10 | parent.$forceUpdate()
11 | })
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/packages/saber/vue-app/components/LayoutManager.vue:
--------------------------------------------------------------------------------
1 |
62 |
--------------------------------------------------------------------------------
/packages/saber/vue-app/components/SaberLink.js:
--------------------------------------------------------------------------------
1 | import isAbsoluteUrl from '../utils/is-absolute-url'
2 |
3 | const setAttribute = (attrs, name, value) => {
4 | if (attrs[name] === undefined) {
5 | attrs[name] = value
6 | }
7 | }
8 |
9 | const HTTP_RE = /^https?:\/\//
10 |
11 | export default {
12 | name: 'SaberLink',
13 |
14 | functional: true,
15 |
16 | render(h, { data, children, parent }) {
17 | const attrs = { ...data.attrs }
18 | const isExternal = typeof attrs.to === 'string' && isAbsoluteUrl(attrs.to)
19 | let tag = 'a'
20 |
21 | if (isExternal) {
22 | if (HTTP_RE.test(attrs.to)) {
23 | setAttribute(attrs, 'rel', 'noopener noreferrer')
24 |
25 | if (attrs.openLinkInNewTab !== false) {
26 | setAttribute(attrs, 'target', '_blank')
27 | }
28 | }
29 |
30 | attrs.href = attrs.to
31 | delete attrs.to
32 | } else if (typeof attrs.to === 'string') {
33 | const link = parent.$saber.getPageLink(attrs.to)
34 | if (link) {
35 | tag = 'router-link'
36 | attrs.to = link
37 | } else {
38 | attrs.href = attrs.to
39 | delete attrs.to
40 | }
41 | } else {
42 | tag = 'router-link'
43 | const { route } = parent.$router.resolve(attrs.to)
44 | attrs.to = parent.$saber.getPageLink(route.fullPath)
45 | }
46 |
47 | delete attrs.openLinkInNewTab
48 |
49 | return h(
50 | tag,
51 | {
52 | ...data,
53 | attrs
54 | },
55 | children
56 | )
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/packages/saber/vue-app/dev-client.js:
--------------------------------------------------------------------------------
1 | import client from 'webpack-hot-middleware/client'
2 |
3 | export const init = ({ router }) => {
4 | window.__SABER_DEV_CLIENT_ID__ = Math.random()
5 | .toString(36)
6 | .substring(7)
7 |
8 | client.subscribe(obj => {
9 | if (obj.action === 'router:push' && obj.id === __SABER_DEV_CLIENT_ID__) {
10 | if (obj.hasError) {
11 | console.error(`You need to refresh the page when the error is fixed!`)
12 | }
13 |
14 | if (obj.alreadyBuilt) {
15 | router.push(obj.route)
16 | } else {
17 | const handler = status => {
18 | if (status === 'idle') {
19 | module.hot.removeStatusHandler(handler)
20 | router.push(obj.route)
21 | }
22 | }
23 |
24 | module.hot.addStatusHandler(handler)
25 | }
26 | }
27 | })
28 | }
29 |
--------------------------------------------------------------------------------
/packages/saber/vue-app/entry-client.js:
--------------------------------------------------------------------------------
1 | import createApp from './create-app'
2 |
3 | const { app, router } = createApp()
4 |
5 | if (process.env.NODE_ENV === 'development') {
6 | require('./dev-client').init({ router })
7 | }
8 |
9 | router.onReady(() => {
10 | app.$mount('#_saber')
11 | })
12 |
--------------------------------------------------------------------------------
/packages/saber/vue-app/entry-server.js:
--------------------------------------------------------------------------------
1 | import createApp from './create-app'
2 |
3 | export default async context => {
4 | const { app, router } = createApp(context)
5 | router.push(context.url)
6 |
7 | await new Promise((resolve, reject) => {
8 | router.onReady(resolve, reject)
9 | })
10 |
11 | if (context.res && router.currentRoute.name === 404) {
12 | context.res.statusCode = 404
13 | }
14 |
15 | context.metaInfo = app.$meta()
16 | context.app = app
17 |
18 | return app
19 | }
20 |
--------------------------------------------------------------------------------
/packages/saber/vue-app/helpers/inject-config.js:
--------------------------------------------------------------------------------
1 | import { siteConfig, themeConfig, locales } from 'saber/config'
2 |
3 | export default ({ Vue }) => {
4 | const store = Vue.observable({ siteConfig, themeConfig, locales })
5 |
6 | const getMatchedConfig = (localePath, key) => {
7 | const matchedLocale = store.locales[localePath]
8 | return Object.assign({}, store[key], matchedLocale && matchedLocale[key])
9 | }
10 |
11 | Vue.mixin({
12 | computed: {
13 | $localePath() {
14 | const allLocalePaths = Object.keys(store.locales)
15 | let localePath = '/'
16 | for (const path of allLocalePaths) {
17 | if (path !== '/') {
18 | if (
19 | this.$route.path === path ||
20 | this.$route.path.indexOf(`${path}/`) === 0
21 | ) {
22 | localePath = path
23 | }
24 | }
25 | }
26 |
27 | return localePath
28 | },
29 | $locales() {
30 | return store.locales
31 | },
32 | $siteConfig() {
33 | return getMatchedConfig(this.$localePath, 'siteConfig')
34 | },
35 | $themeConfig() {
36 | return getMatchedConfig(this.$localePath, 'themeConfig')
37 | }
38 | }
39 | })
40 |
41 | if (module.hot) {
42 | module.hot.accept('saber/config', () => {
43 | const config = require('saber/config')
44 | store.siteConfig = config.siteConfig
45 | store.themeConfig = config.themeConfig
46 | store.locales = config.locales
47 | })
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/packages/saber/vue-app/polyfills.js:
--------------------------------------------------------------------------------
1 | if (process.browser) {
2 | window.Promise = window.Promise || require('./vendor/promise')
3 | Object.assign = require('object-assign')
4 | }
5 |
--------------------------------------------------------------------------------
/packages/saber/vue-app/theme/layouts/default.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
{{ page.title }}
4 |
5 |
6 |
7 |
8 |
13 |
--------------------------------------------------------------------------------
/packages/saber/vue-app/utils/is-absolute-url.js:
--------------------------------------------------------------------------------
1 | export default url => {
2 | if (typeof url !== 'string') {
3 | throw new TypeError(`Expected a \`string\`, got \`${typeof url}\``)
4 | }
5 |
6 | // Don't match Windows paths `c:\`
7 | if (/^[a-zA-Z]:\\/.test(url)) {
8 | return false
9 | }
10 |
11 | // Scheme: https://tools.ietf.org/html/rfc3986#section-3.1
12 | // Absolute URL: https://tools.ietf.org/html/rfc3986#section-4.3
13 | return /^[a-zA-Z][a-zA-Z\d+\-.]*:/.test(url)
14 | }
15 |
--------------------------------------------------------------------------------
/scripts/release-pr.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | // Publish a canary release from a Pull Request
4 | const { spawnSync } = require('child_process')
5 | const { prompt } = require('enquirer')
6 |
7 | async function main() {
8 | let { NPM_TOKEN, GH_TOKEN } = process.env
9 |
10 | if (!NPM_TOKEN) {
11 | NPM_TOKEN = await prompt({
12 | name: 'token',
13 | type: 'input',
14 | message: 'NPM access token',
15 | validate: v => Boolean(v)
16 | }).then(res => res.token)
17 | }
18 |
19 | if (!GH_TOKEN) {
20 | GH_TOKEN = await prompt({
21 | name: 'token',
22 | type: 'input',
23 | message: 'GitHub access token',
24 | validate: v => Boolean(v)
25 | }).then(res => res.token)
26 | }
27 |
28 | const PR_NUMBER = await prompt({
29 | name: 'pr',
30 | type: 'input',
31 | message: 'Which PR number do you want to publish',
32 | validate: v => Boolean(v)
33 | }).then(res => res.pr)
34 |
35 | const { status } = spawnSync('auto', ['canary', '--pr', PR_NUMBER, '-w'], {
36 | stdio: 'inherit',
37 | env: Object.assign({}, process.env, {
38 | GH_TOKEN,
39 | NPM_TOKEN
40 | })
41 | })
42 |
43 | if (status !== 0) {
44 | throw new Error(`Command failed.`)
45 | }
46 | }
47 |
48 | main().catch(error => {
49 | console.error(error)
50 | process.exit(1)
51 | })
52 |
--------------------------------------------------------------------------------
/tsconfig.base.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "strict": true,
4 | "lib": ["es2017"],
5 | "module": "commonjs",
6 | "target": "es2017",
7 | "esModuleInterop": true,
8 | "moduleResolution": "node",
9 | "noImplicitAny": true,
10 | "noUnusedParameters": true
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/website/images/gh-pages-setting.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saberland/saber/e63d22041a9397bec38468caadec6bd7b923d8b6/website/images/gh-pages-setting.png
--------------------------------------------------------------------------------
/website/images/logo-square.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saberland/saber/e63d22041a9397bec38468caadec6bd7b923d8b6/website/images/logo-square.jpeg
--------------------------------------------------------------------------------
/website/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saberland/saber/e63d22041a9397bec38468caadec6bd7b923d8b6/website/images/logo.png
--------------------------------------------------------------------------------
/website/images/simple-index-md-page.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saberland/saber/e63d22041a9397bec38468caadec6bd7b923d8b6/website/images/simple-index-md-page.png
--------------------------------------------------------------------------------
/website/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "version": "0.0.0",
4 | "name": "website",
5 | "scripts": {
6 | "dev": "saber",
7 | "generate": "saber generate && yarn workspace saber build:typedoc"
8 | },
9 | "dependencies": {
10 | "date-fns": "1.30.1",
11 | "docsearch.js": "^2.6.3",
12 | "grid.css": "^0.6.1",
13 | "headroom.js": "^0.9.4",
14 | "markdown-it-footnote": "3.0.1",
15 | "nprogress": "0.2.0",
16 | "prismjs": "1.16.0"
17 | },
18 | "devDependencies": {
19 | "postcss-preset-env": "6.6.0",
20 | "speed-measure-webpack-plugin": "^1.3.1",
21 | "@bundle-analyzer/webpack-plugin": "^0.5.0"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/website/pages/_posts/html-and-pug-page/1.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saberland/saber/e63d22041a9397bec38468caadec6bd7b923d8b6/website/pages/_posts/html-and-pug-page/1.webp
--------------------------------------------------------------------------------
/website/pages/_posts/html-and-pug-page/2.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saberland/saber/e63d22041a9397bec38468caadec6bd7b923d8b6/website/pages/_posts/html-and-pug-page/2.webp
--------------------------------------------------------------------------------
/website/pages/_posts/html-and-pug-page/3.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saberland/saber/e63d22041a9397bec38468caadec6bd7b923d8b6/website/pages/_posts/html-and-pug-page/3.gif
--------------------------------------------------------------------------------
/website/pages/_posts/html-and-pug-page/4.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saberland/saber/e63d22041a9397bec38468caadec6bd7b923d8b6/website/pages/_posts/html-and-pug-page/4.webp
--------------------------------------------------------------------------------
/website/pages/_posts/saber/size-compare.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saberland/saber/e63d22041a9397bec38468caadec6bd7b923d8b6/website/pages/_posts/saber/size-compare.png
--------------------------------------------------------------------------------
/website/pages/blog/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | injectAllPosts: true
3 | layout: blog
4 | title: Blog
5 | ---
6 |
--------------------------------------------------------------------------------
/website/pages/docs/browser-apis.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Browser APIs
3 | layout: docs
4 | ---
5 |
6 | ## Usage
7 |
8 | Exporting a function in `saber-browser.js` in the root of your project to extend Saber's browser APIs.
9 |
10 | ```js
11 | export default context => {
12 | // Do something
13 | }
14 | ```
15 |
16 | The `default` export is optional, you can use this file to import JavaScript or CSS files if you want.
17 |
18 | ## API
19 |
20 | ### context
21 |
22 | The `context` varies in different renderers, Saber currently only supports Vue.
23 |
24 | ### context.router
25 |
26 | The Vue Router instance.
27 |
28 | ### context.setHead
29 |
30 | - Params:
31 | - `head`: `object` `function`, like the component option `head`
32 |
33 | Set the `head` option for root Vue instance.
34 |
35 | ### context.setRootComponent
36 |
37 | - Params:
38 | - `component`: `VueComponentOptions`, a Vue component
39 |
40 | Wrap your whole app in a Vue component, for example:
41 |
42 | `saber-browser.js`:
43 |
44 | ```js
45 | import RootComponent from './RootComponent.vue'
46 |
47 | context.setRootComponent(RootComponent)
48 | ```
49 |
50 | `RootComponent.vue`:
51 |
52 | ```vue
53 |
54 |
55 |
The title will appear on every page
56 |
57 |
58 |
59 |
60 | ```
61 |
62 | ### context.rootOptions
63 |
64 | The [options](https://vuejs.org/v2/api/#Options-Data) for root Vue instance.
65 |
66 | It's **not** recommended to mutate existing properties in `rootOptions`.
67 |
--------------------------------------------------------------------------------
/website/pages/docs/components.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Components
3 | layout: docs
4 | ---
5 |
6 | Saber registered a set of Vue components by default.
7 |
8 | ## `SaberLink`
9 |
10 | A drop-in replacement for Vue Router's `` component, we added addtional features like prefetching route components.
11 |
12 | ## `ClientOnly`
13 |
14 | Use this component to wrap non SSR friendly components.
15 |
16 |
--------------------------------------------------------------------------------
/website/pages/docs/css-modules.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: CSS Modules
3 | layout: docs
4 | ---
5 |
6 | Saber supports [CSS Modules](https://github.com/css-modules/css-modules) alongside regular stylesheets using the `[name].module.css` file naming convention. CSS Modules allows the scoping of CSS by automatically creating a unique classname of the format `[filename]\_[classname]\_\_[hash]`.
7 |
8 | > __TIP__: Should you want to preprocess a stylesheet with Sass then make sure to follow the installation instructions and then change the stylesheet file extension as follows: `[name].module.scss` or `[name].module.sass`.
9 |
10 | CSS Modules let you use the same CSS class name in different files without worrying about naming clashes. Learn more about CSS Modules [here](https://css-tricks.com/css-modules-part-1-need/).
11 |
12 | With `styles.module.css`
13 |
14 | ```css
15 | .error {
16 | background-color: red;
17 | }
18 | ```
19 |
20 | You can import it directly:
21 |
22 | ```js
23 | import styles from './styles.module.css'
24 |
25 | console.log(styles.error)
26 | // => You get a class name:
27 | // Button_error_ax7yz
28 | ```
29 |
30 |
--------------------------------------------------------------------------------
/website/pages/docs/css-preprocessors.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: CSS Preprocessors
3 | layout: docs
4 | ---
5 |
6 | CSS pre-processors like Sass, SCSS, Less and Stylus are still popular today, so it's natual that Saber supports them out of the box. However relevant dependencies for transpilation are required, be sure to follow the installation instructions below.
7 |
8 | ## Sass / SCSS
9 |
10 | ```bash
11 | yarn add sass-loader sass --dev
12 | ```
13 |
14 | Then you can import `.scss` and `.sass` files. If you want to import files from `node_modules`, make sure to add `~` prefix as follows:
15 |
16 | ```scss
17 | // importing a css file from the nprogress node module
18 | @import '~nprogress/nprogress';
19 | ```
20 |
21 | ## Less
22 |
23 | ```bash
24 | yarn add less-loader less --dev
25 | ```
26 |
27 | ## Stylus
28 |
29 | ```bash
30 | yarn add stylus-loader stylus --dev
31 | ```
32 |
33 | ## Passing Options to Pre-Processor Loaders
34 |
35 | Sometimes you may want to pass options to the pre-processor's webpack loader. You can do that using the `build.loaderOptions` option in `saber-config.js`. For example, to pass some shared global variables to all your Sass/Less styles:
36 |
37 | ```js
38 | module.exports = {
39 | build: {
40 | loaderOptions: {
41 | // pass options to sass-loader
42 | sass: {
43 | // @/ is an alias to your project root
44 | // so this assumes you have a file named `scss/variables.scss`
45 | data: `@import "@/scss/variables.scss";`
46 | },
47 | // pass Less.js Options to less-loader
48 | less:{
49 | // http://lesscss.org/usage/#less-options-strict-units `Global Variables`
50 | // `primary` is global variables fields name
51 | globalVars: {
52 | primary: '#fff'
53 | }
54 | }
55 | }
56 | }
57 | }
58 | ```
59 |
--------------------------------------------------------------------------------
/website/pages/docs/i18n.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Internationalization
3 | layout: docs
4 | ---
5 |
6 | ## URL-Based I18n Solution
7 |
8 | To leverage multi-language support in Saber, you first need to use the following file structure in your `pages` folder:
9 |
10 | ```
11 | pages/
12 | ├─ index.md
13 | ├─ foo.md
14 | ├─ nested
15 | │ └─ index.md
16 | ├─ cn
17 | │ ├─ index.md
18 | │ ├─ foo.md
19 | │ └─ nested
20 | │ └─ index.md
21 | └─ fr
22 | ├─ index.md
23 | ├─ foo.md
24 | └─ nested
25 | └─ index.md
26 | ```
27 |
28 | In this way, the `cn` (Chinese) version of your site is available under the path `/cn`, the `fr` (French) version of your site is available under the path `/fr`.
29 |
30 | Then, specify the `locales` option in your `saber-config.yml`:
31 |
32 | ```yaml
33 | siteConfig:
34 | lang: en
35 | title: My Site
36 |
37 | locales:
38 | /cn:
39 | siteConfig:
40 | lang: zh
41 | title: 我的网站
42 | /fr:
43 | siteConfig:
44 | lang: fr
45 | title: Mon Site
46 | ```
47 |
48 | The [`siteConfig.lang`](saber-config.md#lang) option is used to set the `lang` attribute for `` element.
49 |
50 | The [`locales`](saber-config.md#locales) option is used to override [`siteConfig`](saber-config.md#siteconfig) and [`themeConfig`](saber-config.md#themeconfig) for specific paths. For example, when you visit the homepage `/` or `/about` page, `this.$siteConfig.title` in your component will be `My Site`, while if you visit `/cn` or `/cn/about`, it will be `我的网站` instead.
51 |
52 | ## Access Locale Path in the Component
53 |
54 | Use `this.$localePath` in your component to find out the locale for current page, with the `saber-config.yml` we used above, `this.$localePath` will be `'/fr'` when you visit page `/fr/about`, or `'/'` when you visit `/about`.
55 |
--------------------------------------------------------------------------------
/website/pages/docs/layouts.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Layouts
3 | layout: docs
4 | ---
5 |
6 | Layouts are Vue components that wrap around your page. They allow you to have the source code for your template in one place so you don’t have to repeat things like your navigation and footer on every page.
7 |
8 | You can use the `layout` attribute to use a layout component for specific page.
9 |
10 | Saber loads `*.{vue,js}` files from `./layouts` in your project root and [theme](./themes.md) directory as layout components, when the desired layout component does not exist, it will fallback to the `default` layout component in your layouts directory.
11 |
12 | Layout components have a prop named `page` which implements the [Page Interface](./page-interface.md) and allows you to access page data.
13 |
14 | The page contents will be available as the default slot in your layout component, for example:
15 |
16 | A page `pages/about.md`:
17 |
18 | ```markdown
19 | ---
20 | title: Hello World
21 | layout: page
22 | ---
23 |
24 | Saber is fantastic!
25 | ```
26 |
27 | and with the layout component `layouts/page.vue`:
28 |
29 | ```vue
30 |
31 |
32 |
{{ page.title }}
33 |
34 |
35 |
36 |
37 |
42 | ```
43 |
44 | This page will be rendered to the following HTML:
45 |
46 | ```html
47 |
48 |
Hello World
49 |
50 |
Saber is fantastic!
51 |
52 |
53 | ```
54 |
55 | ## 404 page
56 |
57 | You can use the `404` layout to customize 404 page.
58 |
59 | In production build, Saber will also generate `/404.html` which will be automatically used as 404 error page by hosting platforms like GitHub pages and Netlify.
60 |
--------------------------------------------------------------------------------
/website/pages/docs/node-apis.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Node APIs
3 | layout: docs
4 | ---
5 |
6 | ## Usage
7 |
8 | Implement any of [Saber Hooks](saber-instance.md#hooks) by exporting them from a file named `saber-node.js` in the root of your project.
9 |
10 | During developing, you need to restart the server if you made any changes in this file.
11 |
12 | For example:
13 |
14 | ```js
15 | exports.onCreatePage = function (page) {
16 | page.foo = true
17 | }
18 | ```
19 |
20 | You can access [Saber Instance](saber-instance.md) via `this`.
21 |
--------------------------------------------------------------------------------
/website/pages/docs/plugin-api.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Plugin API
3 | layout: docs
4 | ---
5 |
6 | A plugin is an object which has following properties.
7 |
8 | ## Plugin Interface
9 |
10 | ### name
11 |
12 | - Type: `string`
13 | - Required: `true`
14 |
15 | Required property, the plugin name.
16 |
17 | ### apply
18 |
19 | - Type: `(api: SaberInstance, options: any) => void`
20 | - Required: `true`
21 |
22 | A function to invoke.
23 |
24 | ### filterPlugins
25 |
26 | - Type: `FilterPlugins`
27 | - Required: `false`
28 |
29 | Filter the plugins, you can use it to add or remove plugins.
30 |
31 | ```ts
32 | type FilterPlugins = (plugins: Plugin[], options: any) => Plugins[]
33 |
34 | interface Plugin {
35 | /* Plugin name */
36 | name: string
37 | apply: (api: SaberInstance, options?: any) => void
38 | filterPlugins: FilterPlugins
39 | /* Plugin options */
40 | options?: any
41 | /* The path to the plugin, only used in logs */
42 | location?: string
43 | }
44 | ```
45 |
--------------------------------------------------------------------------------
/website/pages/docs/postcss.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: PostCSS
3 | layout: docs
4 | ---
5 |
6 | All kinds of stylesheets, regardless of the extensions, will be processed by [PostCSS](https://postcss.org/) at the end.
7 |
8 | When a stylesheet is being processed, Saber will use the nearest `postcss.config.js` or `.postcssrc.js` etc.
9 |
10 | For example you can add [Autoprefixer](https://github.com/postcss/autoprefixer) in `postcss.config.js` to automatically add vendor prefixes based on the browsers you target:
11 |
12 | ```bash
13 | yarn add autoprefixer --dev
14 | ```
15 |
16 | Add this plugin in PostCSS config file:
17 |
18 | ```js
19 | // postcss.config.js
20 | module.exports = {
21 | plugins: [
22 | require('autoprefixer')()
23 | ]
24 | }
25 | ```
26 |
27 | Configure the browsers you wanna support in `package.json`:
28 |
29 | ```json
30 | {
31 | "browserslist": ["ie > 8", "last 2 versions"]
32 | }
33 | ```
34 |
35 | Then such CSS:
36 |
37 | ```css
38 | .App {
39 | display: flex;
40 | flex-direction: row;
41 | align-items: center;
42 | }
43 | ```
44 |
45 | Will become:
46 |
47 | ```css
48 | .App {
49 | display: -webkit-box;
50 | display: -ms-flexbox;
51 | display: flex;
52 | -webkit-box-orient: horizontal;
53 | -webkit-box-direction: normal;
54 | -ms-flex-direction: row;
55 | flex-direction: row;
56 | -webkit-box-align: center;
57 | -ms-flex-align: center;
58 | align-items: center;
59 | }
60 | ```
61 |
--------------------------------------------------------------------------------
/website/pages/docs/project-structure.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Project Structure
3 | layout: docs
4 | ---
5 |
6 | Inside a Saber project, you may see some or all of the following folders and files:
7 |
8 | ```
9 | .
10 | ├── .saber/
11 | ├── public/
12 | ├── pages/
13 | ├── static/
14 | ├── theme/
15 | ├── saber-config.js
16 | ├── saber-node.js
17 | └── saber-browser.js
18 | ```
19 |
20 | ## Folders:
21 |
22 | - `.saber`: Automatically generated. The files in this folder are used by Saber internally and they are not meant for modification. Should be added to the `.gitignore` file if not added already.
23 | - `public`: Automatically generated. The output of the build process will be exposed inside this folder. Should be added to the `.gitignore` file if not added already.
24 | - `pages`: Components under this folder become pages automatically with paths based on their file name. Check out the pages docs for more detail.
25 | - `static`: If you put a file into the static folder, it will not be processed by Webpack. Instead it will be copied into the public folder untouched. Check out the [assets docs](static-folder.md) for more details.
26 | - `theme`: The theme directory, you need to configure the theme directory explicitly in your `saber-config.js`
27 |
28 | ## Files:
29 |
30 | - `saber-config.js`: The Saber config file, you can write config in YAML or TOML. Check out the [config docs](saber-config.md) for more details.
31 | - `saber-browser.js`: This file is where Saber expects to find any usage of the [Saber browser APIs (if any)](browser-apis.md). These allow customization/extension of default Saber settings affecting the browser.
32 | - `saber-node.js`: This file is where Saber expects to find any usage of the [Saber node APIs (if any)](node-apis.md). These allow customization/extension of default Saber settings affecting pieces of the site build process.
33 |
--------------------------------------------------------------------------------
/website/pages/docs/routing.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Routing
3 | layout: docs
4 | ---
5 |
6 | ## Client-side transitions with `` element
7 |
8 | In Saber, client-side transitions between routes can be enabled via ` ` elements:
9 |
10 | Basic example, `./pages/index.vue`:
11 |
12 | ```vue
13 |
14 |
15 |
Welcome to Saber!
16 |
About
17 |
18 |
19 | ```
20 |
21 | It also works in Markdown since links are transformed to ` ` elements as well.
22 |
23 | Internally, ` ` elements are converted to a built-in component [``](components.md#saberlink), so these are equivalent:
24 |
25 | ```vue
26 | About
27 | About
28 | ```
29 |
30 | `` will be rendered as `` element if the link is an absolute URL (like `https://github.com`), otherwise it's rendered as Vue Router's `` component.
31 |
32 | ## Reference local pages
33 |
34 | You can use the `` element to reference local pages by filename:
35 |
36 | ```vue
37 | About
38 | ```
39 |
40 | ...is converted to:
41 |
42 | ```html
43 | About
44 | ```
45 |
46 | This is useful if you're not sure what the permalink is or you might change the permalink format in the future.
47 |
48 | ## Disable this with `saber-ignore`
49 |
50 | If you dont' want to use `` for client-side transitions, you can use the `saber-ignore` attribute:
51 |
52 | ```vue
53 | Home
54 | ```
55 |
56 | Then this will be rendered as `` instead of `` and make the browser fully reload the page.
57 |
--------------------------------------------------------------------------------
/website/pages/docs/working-with-webpack.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Working with Webpack
3 | layout: docs
4 | ---
5 |
6 | ## Simple Configuration
7 |
8 | The easiest way to tweak the webpack configuration is using the [`getWebpackConfig`](saber-instance.md#getwebpackconfig) hook in your [`saber-node.js`](node-apis.md) like this:
9 |
10 | ```js
11 | exports.getWebpackConfig = function(config, { type }) {
12 | // e.g. Adding a webpack plugin
13 | config.plugins.push(new SomeWebpackPlugin())
14 |
15 | // `type` is either `client` or `server`
16 | if (type === 'client') {
17 | config.plugins.push(new SomeWebpackPluginForClientBuild())
18 | }
19 |
20 | // You must return the `config`!
21 | return config
22 | }
23 | ```
24 |
25 | ## Advanced Configuration
26 |
27 | You can use [webpack-chain](https://github.com/neutrinojs/webpack-chain) to tweak webpack configuration in a more predictable way.
28 |
29 | > webpack's core configuration is based on creating and modifying a potentially unwieldy JavaScript object. While this is OK for configurations on individual projects, trying to share these objects across projects and make subsequent modifications gets messy, as you need to have a deep understanding of the underlying object structure to make those changes.
30 | >
31 | > webpack-chain attempts to improve this process by providing a chainable or fluent API for creating and modifying webpack configurations. Key portions of the API can be referenced by user-specified names, which helps to standardize how to modify a configuration across projects.
32 |
33 | Using the [chainWebpack](saber-instance.md#chainwebpack) hook in [`saber-node.js`](node-apis.md) to access the webpack-chain instance:
34 |
35 | ```js
36 | exports.chainWebpack = function(chain) {
37 | // e.g. Resolve .css files
38 | chain.resolve.extensions.add('.css')
39 | }
40 | ```
41 |
--------------------------------------------------------------------------------
/website/pages/themes/_themes.yml:
--------------------------------------------------------------------------------
1 | - name: Minima
2 | npm: saber-theme-minima
3 | demo: https://minima.saber.land
4 | repo: https://github.com/saberland/saber-theme-minima
5 | tags:
6 | - Blog
7 | - Minimal
8 | - Clean
9 |
10 | - name: Tailsaw
11 | npm: saber-theme-tailsaw
12 | demo: https://tailsaw.saber.land
13 | repo: https://github.com/saberland/saber-theme-tailsaw
14 | tags:
15 | - Blog
16 | - Clean
17 | - Modern
18 | - TailwindCSS
19 |
--------------------------------------------------------------------------------
/website/pages/themes/previews/saber-theme-minima.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saberland/saber/e63d22041a9397bec38468caadec6bd7b923d8b6/website/pages/themes/previews/saber-theme-minima.png
--------------------------------------------------------------------------------
/website/pages/themes/previews/saber-theme-tailsaw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saberland/saber/e63d22041a9397bec38468caadec6bd7b923d8b6/website/pages/themes/previews/saber-theme-tailsaw.png
--------------------------------------------------------------------------------
/website/pages/tutorial/images/about.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saberland/saber/e63d22041a9397bec38468caadec6bd7b923d8b6/website/pages/tutorial/images/about.png
--------------------------------------------------------------------------------
/website/pages/tutorial/images/first-page.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saberland/saber/e63d22041a9397bec38468caadec6bd7b923d8b6/website/pages/tutorial/images/first-page.png
--------------------------------------------------------------------------------
/website/pages/tutorial/images/hello-world.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saberland/saber/e63d22041a9397bec38468caadec6bd7b923d8b6/website/pages/tutorial/images/hello-world.png
--------------------------------------------------------------------------------
/website/pages/tutorial/images/navbar-dumb.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saberland/saber/e63d22041a9397bec38468caadec6bd7b923d8b6/website/pages/tutorial/images/navbar-dumb.gif
--------------------------------------------------------------------------------
/website/pages/tutorial/images/not-found.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saberland/saber/e63d22041a9397bec38468caadec6bd7b923d8b6/website/pages/tutorial/images/not-found.png
--------------------------------------------------------------------------------
/website/pages/tutorial/images/post-list.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saberland/saber/e63d22041a9397bec38468caadec6bd7b923d8b6/website/pages/tutorial/images/post-list.png
--------------------------------------------------------------------------------
/website/pages/tutorial/images/prev-next-post.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saberland/saber/e63d22041a9397bec38468caadec6bd7b923d8b6/website/pages/tutorial/images/prev-next-post.gif
--------------------------------------------------------------------------------
/website/pages/tutorial/images/vue-devtools-page-prop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saberland/saber/e63d22041a9397bec38468caadec6bd7b923d8b6/website/pages/tutorial/images/vue-devtools-page-prop.png
--------------------------------------------------------------------------------
/website/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: [
3 | require('postcss-preset-env')({
4 | stage: 0
5 | })
6 | ]
7 | }
8 |
--------------------------------------------------------------------------------
/website/saber-node.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const SpeedMeasurePlugin = require('speed-measure-webpack-plugin')
3 |
4 | const { MEASURE_SPEED, NODE_ENV, BUNDLE_ANALYZER_TOKEN } = process.env
5 |
6 | exports.getWebpackConfig = (config, { type }) => {
7 | if (type === 'client' && NODE_ENV === 'production' && BUNDLE_ANALYZER_TOKEN) {
8 | const BundleAnalyzer = require('@bundle-analyzer/webpack-plugin')
9 |
10 | config.plugins.push(
11 | new BundleAnalyzer({
12 | token: BUNDLE_ANALYZER_TOKEN
13 | })
14 | )
15 | }
16 |
17 | if (MEASURE_SPEED !== undefined) {
18 | // This plugin will disable hot reloading
19 | const smp = new SpeedMeasurePlugin({
20 | outputFormat: 'json',
21 | outputTarget: path.join(__dirname, `.saber/speed-measure-${type}.json`)
22 | })
23 | return smp.wrap(config)
24 | }
25 |
26 | return config
27 | }
28 |
--------------------------------------------------------------------------------
/website/src/components/PostList.vue:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
13 |
24 |
25 |
38 |
39 |
--------------------------------------------------------------------------------
/website/src/components/PostMeta.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{ format(new Date(page.createdAt), 'MMM DD, YYYY') }}
5 |
6 |
7 |
8 |
9 |
20 |
21 |
26 |
--------------------------------------------------------------------------------
/website/src/components/SiteSearch.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
36 |
37 |
38 |
39 |
47 |
--------------------------------------------------------------------------------
/website/src/css/button.css:
--------------------------------------------------------------------------------
1 | .button {
2 | color: white;
3 | background: var(--theme-color);
4 | padding: 15px 20px;
5 | font-weight: 500;
6 | font-size: 1.2rem;
7 | transition: background 0.2s ease-in-out;
8 | border-radius: 4px;
9 |
10 | &:hover {
11 | background: rgb(27, 119, 117);
12 | }
13 |
14 | @media (max-width: 768px) {
15 | font-size: 1rem;
16 | }
17 | }
18 |
19 | .button.is-secondary {
20 | background: #cedfe7;
21 | color: inherit;
22 | &:hover {
23 | background: rgb(179, 207, 220, .8);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/website/src/layouts/blog.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
29 |
--------------------------------------------------------------------------------
/website/src/layouts/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
25 |
--------------------------------------------------------------------------------
/website/src/layouts/post.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{ page.title }}
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
48 |
--------------------------------------------------------------------------------
/website/src/layouts/tutorial.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{ page.title }}
4 |
5 |
6 |
7 |
8 |
9 |
10 |
30 |
--------------------------------------------------------------------------------
/website/src/mixins/doc.js:
--------------------------------------------------------------------------------
1 | export default {
2 | mounted() {
3 | // Make footnotes focusable
4 | const items = this.$el.querySelectorAll('.footnote-item,.footnote-ref a')
5 | Array.prototype.forEach.call(items, el => {
6 | el.tabIndex = 1
7 | })
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/website/src/saber-browser.js:
--------------------------------------------------------------------------------
1 | import 'nprogress/nprogress.css'
2 | import 'grid.css/grid.css'
3 | import '../../packages/saber-highlight-css/default.css'
4 | import './css/global.css'
5 | import './css/button.css'
6 | import './css/prism.css'
7 | import './css/page.css'
8 |
9 | export default ({ router, setHead }) => {
10 | if (process.browser) {
11 | const nprogress = require('nprogress')
12 |
13 | const loaded = Object.create(null)
14 |
15 | nprogress.configure({ showSpinner: false })
16 |
17 | router.beforeEach((to, from, next) => {
18 | if (!loaded[to.path]) {
19 | nprogress.start()
20 | }
21 | next()
22 | })
23 |
24 | router.afterEach(to => {
25 | loaded[to.path] = true
26 | nprogress.done()
27 |
28 | document.body.classList.remove('show-sidebar')
29 | })
30 | }
31 |
32 | setHead(vm => ({
33 | meta: [
34 | {
35 | name: 'description',
36 | hid: 'description',
37 | content: vm.$siteConfig.description
38 | }
39 | ],
40 | link: [
41 | {
42 | rel: 'icon',
43 | type: 'image/png',
44 | href: '/img/icons/icon_128x128.png'
45 | }
46 | ]
47 | }))
48 | }
49 |
--------------------------------------------------------------------------------
/website/static/_redirects:
--------------------------------------------------------------------------------
1 | # Redirect domain aliases to primary domain
2 | https://saberjs.org/* https://saber.land/:splat 301!
3 |
4 | # Optional: Redirect default Netlify subdomain to primary domain
5 | https://saber.netlify.com/* https://saber.land/:splat 301!
6 |
--------------------------------------------------------------------------------
/website/static/img/icons/icon_128x128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saberland/saber/e63d22041a9397bec38468caadec6bd7b923d8b6/website/static/img/icons/icon_128x128.png
--------------------------------------------------------------------------------
/website/static/img/icons/icon_16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saberland/saber/e63d22041a9397bec38468caadec6bd7b923d8b6/website/static/img/icons/icon_16x16.png
--------------------------------------------------------------------------------
/website/static/img/icons/icon_256x256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saberland/saber/e63d22041a9397bec38468caadec6bd7b923d8b6/website/static/img/icons/icon_256x256.png
--------------------------------------------------------------------------------
/website/static/img/icons/icon_32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saberland/saber/e63d22041a9397bec38468caadec6bd7b923d8b6/website/static/img/icons/icon_32x32.png
--------------------------------------------------------------------------------
/website/static/img/icons/icon_512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saberland/saber/e63d22041a9397bec38468caadec6bd7b923d8b6/website/static/img/icons/icon_512x512.png
--------------------------------------------------------------------------------
/website/static/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "icons": [
3 | {
4 | "src": "img/icons/icon_32x32.png",
5 | "type": "image/png",
6 | "sizes": "32x32"
7 | },
8 | {
9 | "src": "img/icons/icon_128x128.png",
10 | "type": "image/png",
11 | "sizes": "128x128"
12 | },
13 | {
14 | "src": "img/icons/icon_256x256.png",
15 | "type": "image/png",
16 | "sizes": "256x256"
17 | },
18 | {
19 | "src": "img/icons/icon_512x512.png",
20 | "type": "image/png",
21 | "sizes": "512x512"
22 | }
23 | ]
24 | }
25 |
--------------------------------------------------------------------------------