diff --git a/README.adoc b/README.adoc deleted file mode 100644 index 9bffb35..0000000 --- a/README.adoc +++ /dev/null @@ -1,246 +0,0 @@ -= Antora Default UI -// Settings: -:experimental: -:hide-uri-scheme: -// Project URLs: -:url-project: https://gitlab.com/antora/antora-ui-default -:url-preview: https://antora.gitlab.io/antora-ui-default -:url-ci-pipelines: {url-project}/pipelines -:img-ci-status: {url-project}/badges/master/pipeline.svg -// External URLs: -:url-antora: https://antora.org -:url-antora-docs: https://docs.antora.org -:url-git: https://git-scm.com -:url-git-dl: {url-git}/downloads -:url-gulp: http://gulpjs.com -:url-opendevise: https://opendevise.com -:url-nodejs: https://nodejs.org -:url-nvm: https://github.com/creationix/nvm -:url-nvm-install: {url-nvm}#installation -:url-source-maps: https://developer.mozilla.org/en-US/docs/Tools/Debugger/How_to/Use_a_source_map - -image:{img-ci-status}[CI Status (GitLab CI), link={url-ci-pipelines}] - -This project is an archetype that demonstrates how to produce a UI bundle that can be used by {url-antora}[Antora] to generated a documentation site. -You can see a preview of the default UI at {url-preview}. - -While the default UI is ready to be used with Antora, the intent is that you'll fork it and customize it for your own needs. -It's intentionally minimalistic so as to give you a good starting point without requiring too much effort to customize. - -== Code of Conduct - -The Antora project and its project spaces are governed by our https://gitlab.com/antora/antora/-/blob/master/CODE-OF-CONDUCT.adoc[Code of Conduct]. -By participating, you're agreeing to honor this code. -Let's work together to make this a welcoming, professional, inclusive, and safe environment for everyone. - -== Use the Default UI - -If you want to simply use the default UI for your Antora-generated site, add the following UI configuration to your playbook: - -[source,yaml] ----- -ui: - bundle: - url: https://gitlab.com/antora/antora-ui-default/-/jobs/artifacts/master/raw/build/ui-bundle.zip?job=bundle-stable - snapshot: true ----- - -NOTE: The `snapshot` flag tells Antora to fetch the UI when the `--fetch` command-line flag is present. -This setting is required because updates to the UI bundle are pushed to the same URL. -If the URL were to be unique, this setting would not be required. - -Read on to learn how to customize the default UI for your own documentation. - -== Development Quickstart - -This section offers a basic tutorial to teach you how to set up the default UI project, preview it locally, and bundle it for use with Antora. -A more comprehensive tutorial can be found in the documentation at {url-antora-docs}. - -=== Prerequisites - -To preview and bundle the default UI, you need the following software on your computer: - -* {url-git}[git] (command: `git`) -* {url-nodejs}[Node.js] (commands: `node` and `npm`) -* {url-gulp}[Gulp CLI] (command: `gulp`) - -==== git - -First, make sure you have git installed. - - $ git --version - -If not, {url-git-dl}[download and install] the git package for your system. - -==== Node.js - -Next, make sure that you have Node.js installed (which also provides npm). - - $ node --version - -If this command fails with an error, you don't have Node.js installed. -If the command doesn't report an LTS version of Node.js (e.g., v10.15.3), it means you don't have a suitable version of Node.js installed. -In this guide, we'll be installing Node.js 10. - -While you can install Node.js from the official packages, we strongly recommend that you use {url-nvm}[nvm] (Node Version Manager) to manage your Node.js installation(s). -Follow the {url-nvm-install}[nvm installation instructions] to set up nvm on your machine. - -Once you've installed nvm, open a new terminal and install Node.js 10 using the following command: - - $ nvm install 10 - -You can switch to this version of Node.js at any time using the following command: - - $ nvm use 10 - -To make Node.js 10 the default in new terminals, type: - - $ nvm alias default 10 - -Now that you have Node.js installed, you can proceed with installing the Gulp CLI. - -==== Gulp CLI - -You'll need the Gulp command-line interface (CLI) to run the build. -The Gulp CLI package provides the `gulp` command which, in turn, executes the version of Gulp declared by the project. - -You can install the Gulp CLI globally (which resolves to a location in your user directory if you're using nvm) using the following command: - - $ npm install -g gulp-cli - -Verify the Gulp CLI is installed and on your PATH by running: - - $ gulp --version - -If you prefer to install global packages using Yarn, run this command instead: - - $ yarn global add gulp-cli - -Alternately, you can use the `gulp` command that is installed by the project's dependencies. - - $ $(npm bin)/gulp --version - -Now that you have the prerequisites installed, you can fetch and build the UI project. - -=== Clone and Initialize the UI Project - -Clone the default UI project using git: - -[subs=attributes+] - $ git clone {url-project} && - cd "`basename $_`" - -The example above clones Antora's default UI project and then switches to the project folder on your filesystem. -Stay in this project folder when executing all subsequent commands. - -Use npm to install the project's dependencies inside the project. -In your terminal, execute the following command: - - $ npm install - -This command installs the dependencies listed in [.path]_package.json_ into the [.path]_node_modules/_ folder inside the project. -This folder does not get included in the UI bundle and should _not_ be committed to the source control repository. - -[TIP] -==== -If you prefer to install packages using Yarn, run this command instead: - - $ yarn -==== - -=== Preview the UI - -The default UI project is configured to preview offline. -The files in the [.path]_preview-src/_ folder provide the sample content that allow you to see the UI in action. -In this folder, you'll primarily find pages written in AsciiDoc. -These pages provide a representative sample and kitchen sink of content from the real site. - -To build the UI and preview it in a local web server, run the `preview` command: - - $ gulp preview - -You'll see a URL listed in the output of this command: - -.... -[12:00:00] Starting server... -[12:00:00] Server started http://localhost:5252 -[12:00:00] Running server -.... - -Navigate to this URL to preview the site locally. - -While this command is running, any changes you make to the source files will be instantly reflected in the browser. -This works by monitoring the project for changes, running the `preview:build` task if a change is detected, and sending the updates to the browser. - -Press kbd:[Ctrl+C] to stop the preview server and end the continuous build. - -=== Package for Use with Antora - -If you need to package the UI so you can use it to generate the documentation site locally, run the following command: - - $ gulp bundle - -If any errors are reported by lint, you'll need to fix them. - -When the command completes successfully, the UI bundle will be available at [.path]_build/ui-bundle.zip_. -You can point Antora at this bundle using the `--ui-bundle-url` command-line option. - -If you have the preview running, and you want to bundle without causing the preview to be clobbered, use: - - $ gulp bundle:pack - -The UI bundle will again be available at [.path]_build/ui-bundle.zip_. - -==== Source Maps - -The build consolidates all the CSS and client-side JavaScript into combined files, [.path]_site.css_ and [.path]_site.js_, respectively, in order to reduce the size of the bundle. -{url-source-maps}[Source maps] correlate these combined files with their original sources. - -This "`source mapping`" is accomplished by generating additional map files that make this association. -These map files sit adjacent to the combined files in the build folder. -The mapping they provide allows the debugger to present the original source rather than the obfuscated file, an essential tool for debugging. - -In preview mode, source maps are enabled automatically, so there's nothing you have to do to make use of them. -If you need to include source maps in the bundle, you can do so by setting the `SOURCEMAPS` environment variable to `true` when you run the bundle command: - - $ SOURCEMAPS=true gulp bundle - -In this case, the bundle will include the source maps, which can be used for debugging your production site. - -== Copyright and License - -Copyright (C) 2017-present OpenDevise Inc. and the Antora Project. - -Use of this software is granted under the terms of the https://www.mozilla.org/en-US/MPL/2.0/[Mozilla Public License Version 2.0] (MPL-2.0). -See link:LICENSE[] to find the full license text. - -== Authors - -Development of Antora is led and sponsored by {url-opendevise}[OpenDevise Inc]. - -== Extensions to the UI - -=== Related Documentation - -The UI presents a list of related documentation and that documentation can be filtered using two attributes: - -* page-related-doc-categories - The categories to be included in the related documentation -* page-related-doc-projects - The project ids to be included in the related documentation - -For a complete listing of valid categories and ids view https://github.com/spring-io/antora-ui-spring/blob/main/src/helpers/related_projects.js[related_projects.js] - -The configuration is typically specified in asciidoc attributes section of the antora-playbook.yml: - -.antora-playbook.yml -[source,yml] ----- -asciidoc: - attributes: - # Include the projects with the security category - page-related-doc-categories: security - # Include the projects with ids framework and graphql - page-related-doc-projects: framework,graphql ----- - -The Related Documentation links to the `All Docs...` page. -To include this resource, ensure that the https://github.com/spring-io/antora-extensions/blob/main/README.adoc[antora-extensions] is using 1.7.0+ and the https://github.com/spring-io/antora-extensions/blob/main/README.adoc#static-page[Static Page Extension] is included. diff --git a/README.md b/README.md new file mode 100644 index 0000000..1849de8 --- /dev/null +++ b/README.md @@ -0,0 +1,113 @@ + +

+ + Micrometer + +

+ +[![On Push](https://github.com/micrometer-metrics/antora-ui-micrometer/actions/workflows/push.yml/badge.svg?branch=main)](https://github.com/micrometer-metrics/antora-ui-micrometer/actions/workflows/push.yml) + + +This project generates and packages the static resources that Spring uses for document production. + +This project is based on [Antora](https://antora.org). + + +## Development Quickstart + +This section offers a basic tutorial to teach you how to preview it locally, and bundle it for use with Antora. +A more comprehensive tutorial can be found in the documentation at [docs.antora.org](https://docs.antora.org/). + +### Prerequisites + +To preview and bundle the Antora Spring UI, you need the following software on your computer: + +* [git](https://git-scm.com/) (command: `git`) +* [Node.js](https://nodejs.org/) (commands: `node` and `npm`) +* [Gulp CLI](http://gulpjs.com/) (command: `gulp`) + +### Preview the UI + +The Spring Antora UI project is configured to preview offline. +The files in the `preview-src/` folder provide the sample content that allow you to see the UI in action. +In this folder, you'll primarily find pages written in AsciiDoc. +These pages provide a representative sample and kitchen sink of content from the real site. + +To build the UI and preview it in a local web server, run the `preview` command: + +``` +npm install +gulp preview +``` + +You'll see a URL listed in the output of this command: + +``` +[12:00:00] Starting server... +[12:00:00] Server started http://localhost:5252 +[12:00:00] Running server +``` + +Navigate to this URL to preview the site locally. + +While this command is running, any changes you make to the source files will be instantly reflected in the browser. +This works by monitoring the project for changes, running the `preview:build` task if a change is detected, and sending the updates to the browser. + +Press `Ctrl`+`C` to stop the preview server and end the continuous build. + +### Package for Use with Antora + +If you need to package the UI so you can use it to generate the documentation site locally, run the following command: + +``` +gulp bundle +``` + +If any errors are reported by lint, you'll need to fix them. + +When the command completes successfully, the UI bundle will be available at `build/ui-bundle.zip`. +You can point Antora at this bundle using the `--ui-bundle-url` command-line option. + +If you have the preview running, and you want to bundle without causing the preview to be clobbered, use: + +``` +gulp bundle:pack +``` + +The UI bundle will again be available at `build/ui-bundle.zip`. + +## Extensions to the UI + +### Related Documentation + +The UI presents a list of related documentation and that documentation can be filtered using two attributes: + +* page-related-doc-categories - The categories to be included in the related documentation +* page-related-doc-projects - The project ids to be included in the related documentation + +For a complete listing of valid categories and ids view [related_projects.js](https://github.com/spring-io/antora-ui-spring/blob/main/src/helpers/related_projects.js) + +The configuration is typically specified in asciidoc attributes section of the `antora-playbook.yml`: + +``` +asciidoc: + attributes: + # Include the projects with the security category + page-related-doc-categories: security + # Include the projects with ids framework and graphql + page-related-doc-projects: framework,graphql +``` + +The Related Documentation links to the `All Docs...` page. +To include this resource, ensure that the [antora-extensions](https://github.com/spring-io/antora-extensions/blob/main/README.adoc) is using 1.7.0+ and the [Static Page Extension](https://github.com/spring-io/antora-extensions/blob/main/README.adoc#static-page) is included. + +## Authors + +Development of Antora is led and sponsored by [OpenDevise Inc](https://opendevise.com/). + +## Copyright and License + +Copyright (C) 2017-present OpenDevise Inc. and the Antora Project. + +Use of this software is granted under the terms of the [Mozilla Public License Version 2.0](https://www.mozilla.org/en-US/MPL/2.0/) (MPL-2.0). +See [LICENSE](https://github.com/spring-io/antora-ui-spring/blob/feat/gh-226/LICENSE) to find the full license text. diff --git a/gulp.d/tasks/build.js b/gulp.d/tasks/build.js index 5d31899..5f352b6 100644 --- a/gulp.d/tasks/build.js +++ b/gulp.d/tasks/build.js @@ -17,9 +17,11 @@ const postcssUrl = require('postcss-url') const postcssVar = require('postcss-custom-properties') const { Transform } = require('stream') const map = (transform) => new Transform({ objectMode: true, transform }) +const replace = require('gulp-replace') const through = () => map((file, enc, next) => next(null, file)) const uglify = require('gulp-uglify') const vfs = require('vinyl-fs') +const git = require('git-rev-sync') module.exports = (src, dest, preview) => () => { const opts = { base: src, cwd: src } @@ -100,7 +102,7 @@ module.exports = (src, dest, preview) => () => { ), vfs.src('helpers/*.js', opts), vfs.src('layouts/*.hbs', opts), - vfs.src('partials/*.hbs', opts) + vfs.src('partials/*.hbs', opts).pipe(replace('@@antora-ui-version', git.isTagDirty() ? git.long() : git.tag())) ).pipe(vfs.dest(dest, { sourcemaps: sourcemaps && '.' })) } diff --git a/gulp.d/tasks/generate-octicons.js b/gulp.d/tasks/generate-octicons.js index de19fce..dbd48af 100644 --- a/gulp.d/tasks/generate-octicons.js +++ b/gulp.d/tasks/generate-octicons.js @@ -16,6 +16,7 @@ const icons = [ 'info', 'law', 'light-bulb', + 'link-external', 'moon', 'question', 'rocket', diff --git a/package-lock.json b/package-lock.json index 381cab4..4f196e3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,8 @@ "license": "MPL-2.0", "dependencies": { "@algolia/client-search": "^4.17.0", - "@primer/octicons": "^19.8.0" + "@primer/octicons": "^19.8.0", + "init": "^0.1.2" }, "devDependencies": { "@asciidoctor/core": "~2.2", @@ -32,12 +33,14 @@ "eslint-plugin-promise": "~4.2", "eslint-plugin-standard": "~4.0", "fancy-log": "~2.0", + "git-rev-sync": "^3.0.2", "gulp": "~4.0", "gulp-concat": "~2.6", "gulp-connect": "~5.7", "gulp-eslint": "~6.0", "gulp-imagemin": "~6.2", "gulp-postcss": "~9.0", + "gulp-replace": "~1.1", "gulp-stylelint": "~13.0", "gulp-uglify": "~3.0", "gulp-vinyl-zip": "~2.5", @@ -929,6 +932,12 @@ "integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==", "dev": true }, + "node_modules/@types/expect": { + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/@types/expect/-/expect-1.20.4.tgz", + "integrity": "sha512-Q5Vn3yjTDyCMV50TB6VRIbQNxSE4OmZR86VSbGaNpfUolm0iePBB4KdEEHmxoY5sT2+2DIvXW0rvMDP2nHZ4Mg==", + "dev": true + }, "node_modules/@types/glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", @@ -1024,6 +1033,16 @@ "integrity": "sha512-zC0iXxAv1C1ERURduJueYzkzZ2zaGyc+P2c95hgkikHPr3z8EdUZOlgEQ5X0DRmwDZn+hekycQnoeiiRVrmilQ==", "dev": true }, + "node_modules/@types/vinyl": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/@types/vinyl/-/vinyl-2.0.12.tgz", + "integrity": "sha512-Sr2fYMBUVGYq8kj3UthXFAu5UN6ZW+rYr4NACjZQJvHvj+c8lYv0CahmZ2P/r7iUkN44gGUBwqxZkrKXYPb7cw==", + "dev": true, + "dependencies": { + "@types/expect": "^1.20.4", + "@types/node": "*" + } + }, "node_modules/@typescript-eslint/experimental-utils": { "version": "3.10.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-3.10.1.tgz", @@ -2356,6 +2375,18 @@ "node": ">=0.10.0" } }, + "node_modules/binaryextensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binaryextensions/-/binaryextensions-2.3.0.tgz", + "integrity": "sha512-nAihlQsYGyc5Bwq6+EsubvANYGExeJKHDO3RjnvwU042fawQTQfM3Kxn7IHUXQOz4bzfwsGYYHGSvXyW4zOGLg==", + "dev": true, + "engines": { + "node": ">=0.8" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, "node_modules/bindings": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", @@ -4041,6 +4072,14 @@ "type": "^1.0.1" } }, + "node_modules/daemon": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/daemon/-/daemon-1.1.0.tgz", + "integrity": "sha512-1vX9YVcP21gt12nSD3SQRC/uPU7fyA6M8qyClTBIFuiRWoylFn57PwXhjBAqRl085bZAje7sILhZU48qcS9SWw==", + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/dash-ast": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/dash-ast/-/dash-ast-2.0.1.tgz", @@ -6618,6 +6657,23 @@ "once": "^1.3.1" } }, + "node_modules/git-rev-sync": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/git-rev-sync/-/git-rev-sync-3.0.2.tgz", + "integrity": "sha512-Nd5RiYpyncjLv0j6IONy0lGzAqdRXUaBctuGBbrEA2m6Bn4iDrN/9MeQTXuiquw8AEKL9D2BW0nw5m/lQvxqnQ==", + "dev": true, + "dependencies": { + "escape-string-regexp": "1.0.5", + "graceful-fs": "4.1.15", + "shelljs": "0.8.5" + } + }, + "node_modules/git-rev-sync/node_modules/graceful-fs": { + "version": "4.1.15", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", + "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", + "dev": true + }, "node_modules/glob": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", @@ -7118,6 +7174,22 @@ "node": ">= 0.10" } }, + "node_modules/gulp-replace": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/gulp-replace/-/gulp-replace-1.1.4.tgz", + "integrity": "sha512-SVSF7ikuWKhpAW4l4wapAqPPSToJoiNKsbDoUnRrSgwZHH7lH8pbPeQj1aOVYQrbZKhfSVBxVW+Py7vtulRktw==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/vinyl": "^2.0.4", + "istextorbinary": "^3.0.0", + "replacestream": "^4.0.3", + "yargs-parser": ">=5.0.0-security.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/gulp-stylelint": { "version": "13.0.0", "resolved": "https://registry.npmjs.org/gulp-stylelint/-/gulp-stylelint-13.0.0.tgz", @@ -7971,6 +8043,17 @@ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "dev": true }, + "node_modules/init": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/init/-/init-0.1.2.tgz", + "integrity": "sha512-IvHUjULS2q+BXJdiu4FHkByh3+qSFmkOXQ2ItSfYTtkdUksQc0yNX6f1uDyokzRV71tjpFsFc3ckeYLJXunTGw==", + "dependencies": { + "daemon": ">=0.3.0" + }, + "engines": { + "node": ">=0.4.7" + } + }, "node_modules/inline-source-map": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/inline-source-map/-/inline-source-map-0.6.2.tgz", @@ -8870,6 +8953,22 @@ "node": ">=0.10.0" } }, + "node_modules/istextorbinary": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/istextorbinary/-/istextorbinary-3.3.0.tgz", + "integrity": "sha512-Tvq1W6NAcZeJ8op+Hq7tdZ434rqnMx4CCZ7H0ff83uEloDvVbqAwaMTZcafKGJT0VHkYzuXUiCY4hlXQg6WfoQ==", + "dev": true, + "dependencies": { + "binaryextensions": "^2.2.0", + "textextensions": "^3.2.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, "node_modules/isurl": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isurl/-/isurl-1.0.0.tgz", @@ -14047,6 +14146,17 @@ "node": ">= 0.10" } }, + "node_modules/replacestream": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/replacestream/-/replacestream-4.0.3.tgz", + "integrity": "sha512-AC0FiLS352pBBiZhd4VXB1Ab/lh0lEgpP+GGvZqbQh8a5cmXVoTe5EX/YeTFArnp4SRGTHh1qCHu9lGs1qG8sA==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.3", + "object-assign": "^4.0.1", + "readable-stream": "^2.0.2" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -14771,6 +14881,23 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/shelljs": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", + "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", + "dev": true, + "dependencies": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + }, + "bin": { + "shjs": "bin/shjs" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/side-channel": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", @@ -16766,6 +16893,18 @@ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, + "node_modules/textextensions": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/textextensions/-/textextensions-3.3.0.tgz", + "integrity": "sha512-mk82dS8eRABNbeVJrEiN5/UMSCliINAuz8mkUwH4SwslkNP//gbEzlWNS5au0z5Dpx40SQxzqZevZkn+WYJ9Dw==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, "node_modules/through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", diff --git a/package.json b/package.json index 9f0367b..efbdce6 100644 --- a/package.json +++ b/package.json @@ -39,12 +39,14 @@ "eslint-plugin-promise": "~4.2", "eslint-plugin-standard": "~4.0", "fancy-log": "~2.0", + "git-rev-sync": "^3.0.2", "gulp": "~4.0", "gulp-concat": "~2.6", "gulp-connect": "~5.7", "gulp-eslint": "~6.0", "gulp-imagemin": "~6.2", "gulp-postcss": "~9.0", + "gulp-replace": "~1.1", "gulp-stylelint": "~13.0", "gulp-uglify": "~3.0", "gulp-vinyl-zip": "~2.5", @@ -69,6 +71,7 @@ }, "dependencies": { "@algolia/client-search": "^4.17.0", - "@primer/octicons": "^19.8.0" + "@primer/octicons": "^19.8.0", + "init": "^0.1.2" } } diff --git a/preview-src/samples/content/index.adoc b/preview-src/samples/content/index.adoc index cadb0e5..c6c2503 100644 --- a/preview-src/samples/content/index.adoc +++ b/preview-src/samples/content/index.adoc @@ -142,6 +142,11 @@ image::/_/img/spring-logo.svg[Interactive,300,opts=interactive] image::/_/img/spring-logo.svg[Embedded,300,opts=inline] +=== Image invert dark mode + +[.invert-dark] +image::/_/img/lorem.png[Spring,100] + [.impact] == Text formatting diff --git a/preview-src/ui-model.yml b/preview-src/ui-model.yml index f036673..4906a0b 100644 --- a/preview-src/ui-model.yml +++ b/preview-src/ui-model.yml @@ -104,6 +104,10 @@ shared: url: '/samples/edge-cases/index.html' - content: 404 url: '/404.html' + - content: External Link + url: 'https://spring.io' + roles: 'link-external' + target: '_blank' baz: 1.0.0: home: true diff --git a/src/css/breadcrumbs.css b/src/css/breadcrumbs.css index 0eb079b..289004b 100644 --- a/src/css/breadcrumbs.css +++ b/src/css/breadcrumbs.css @@ -1,5 +1,8 @@ +.breadcrumbs-container { + padding-top: 2rem; +} + .breadcrumbs { - margin: 2.5rem 0 0; max-width: var(--doc-max-width); flex: 1 1; padding: 0; @@ -14,6 +17,21 @@ max-width: var(--doc-max-width--desktop); min-width: 0; } + .breadcrumbs-container { + position: fixed; + top: 83px; + padding: 1rem 0; + right: var(--toc-width--widescreen); + left: var(--nav-width); + margin-left: 3rem; + margin-right: 3rem; + background-color: var(--body-background-color); + border-bottom: 1px solid var(--nav-panel-divider-color); + z-index: 99; + } + .doc { + padding-top: 60px; + } } a + .breadcrumbs { diff --git a/src/css/doc.css b/src/css/doc.css index 2ec10ee..e1c740b 100644 --- a/src/css/doc.css +++ b/src/css/doc.css @@ -42,6 +42,12 @@ } } +@media screen and (min-width: 1024px) { + .doc { + padding-top: 60px; + } +} + .doc > h1.page:first-child + aside.toc.embedded { margin-top: -0.5rem; } diff --git a/src/css/header.css b/src/css/header.css index 9acc168..88ee928 100644 --- a/src/css/header.css +++ b/src/css/header.css @@ -221,12 +221,6 @@ body { align-items: center; } - .navbar-item.is-hoverable:hover .navbar-dropdown { - opacity: 1; - transform: translateY(0); - pointer-events: auto; - } - .navbar-link::after { border-width: 0 0 2px 2px; border-style: solid; @@ -270,12 +264,33 @@ body { box-shadow: var(--navbar-menu-boxshadow, 0 5px 30px 0 rgb(108 135 135 / 50%)); pointer-events: none; opacity: 0; - transition-duration: 86ms; - transform: translateY(-5px); - transition-property: opacity, transform; + visibility: hidden; z-index: 999; } + @keyframes dropdown { + from { + opacity: 0; + visibility: hidden; + transform: translateY(-5px); + } + 1% { + visibility: visible; + } + to { + visibility: visible; + opacity: 1; + transform: translateY(0); + pointer-events: auto; + } + } + + .navbar-item.is-hoverable:hover .navbar-dropdown, + .navbar-item.is-hoverable:focus .navbar-dropdown, + .navbar-item.is-hoverable:focus-within .navbar-dropdown { + animation: dropdown 86ms linear forwards; + } + .navbar-dropdown.lg { width: 300px; left: -20px; diff --git a/src/css/main.css b/src/css/main.css index d52f2c4..4ca9407 100644 --- a/src/css/main.css +++ b/src/css/main.css @@ -195,6 +195,10 @@ html.dark-theme #modal-versions .colset .col-right { color: var(--link-font-color); } +#modal-versions ul li.component { + padding-bottom: 1rem; +} + #modal-versions ul li.component > a.title { color: var(--body-font-dark-color); } @@ -211,6 +215,55 @@ html.dark-theme #modal-versions .colset .col-right { list-style: circle; } +#modal-versions .nav-versions .version-item .versions { + display: none; + padding: 0; + margin: 0; + margin-left: 45px; +} + +#modal-versions .nav-versions .version-item.is-active .versions { + display: block; +} + +#modal-versions .nav-versions a.title { + color: var(--body-font-dark-color); + padding-bottom: 10px; +} + +#modal-versions .version-toggle { + position: relative; + border: none; + margin: 8px 0; + padding: 0; + padding-left: 30px; + width: auto; + overflow: visible; + background: transparent; + color: var(--body-font-dark-color); +} + +#modal-versions .version-toggle span { + background: transparent url(../img/chevron.svg) no-repeat center / 50%; + border: none; + outline: none; + line-height: inherit; + position: absolute; + height: 30px; + width: 30px; + left: 0; + top: -4px; + transform: rotate(-90deg); +} + +html.dark-theme #modal-versions .version-toggle span { + background: transparent url(../img/chevron-white.svg) no-repeat center / 50%; +} + +#modal-versions .is-active .version-toggle span { + transform: rotate(0); +} + #modal-versions ul.projects { width: 100%; } @@ -221,6 +274,11 @@ html.dark-theme #modal-versions .colset .col-right { margin-left: 0; } +#modal-versions .col-left { + max-height: 80vh; + overflow: auto; +} + #modal-versions .current { background-color: #80ea6e; color: #111; @@ -265,3 +323,7 @@ html.dark-theme #modal-versions .modal-versions-close .cls-1h { display: block; } } + +html.dark-theme .invert-dark img { + filter: invert(1); +} diff --git a/src/css/nav.css b/src/css/nav.css index 35874be..e6e47d4 100644 --- a/src/css/nav.css +++ b/src/css/nav.css @@ -72,12 +72,28 @@ html.is-clipped--nav { .nav-list { margin: 0; - padding: 0 1rem; + padding: 0; + padding-right: 1rem; + padding-left: 5px; } .nav-menu > .nav-list .nav-list { padding: 0; - margin: 0 0 0 0.75rem; + margin: 0 0 0 0.5rem; +} + +.nav-list a.link-external::after { + content: url(../img/link-external.svg); + width: 14px; + height: 14px; + display: inline-block; + vertical-align: middle; + margin: 0 0 6px 5px; + filter: opacity(0.5); +} + +.dark-theme .nav-list a.link-external::after { + filter: invert() opacity(0.4); } .nav-menu > .nav-list + .nav-list { @@ -110,11 +126,10 @@ html.is-clipped--nav { outline: none; line-height: inherit; position: absolute; - height: calc(var(--nav-line-height) * 1.5em); - width: calc(var(--nav-line-height) * 1.5em); - margin-top: 2px; - margin-left: calc(var(--nav-line-height) * -1em); - right: 2px; + height: calc(var(--nav-line-height) * 1.1em); + width: calc(var(--nav-line-height) * 1.1em); + margin-top: 6px; + left: 2px; transform: rotate(-90deg); } @@ -150,14 +165,38 @@ html.is-clipped--nav { display: block; padding: 0.4rem 0.5rem; border-radius: 3px; - padding-right: 28px; + padding-left: 12px; + margin-left: 10px; +} + +.nav-item-toggle + .nav-link, +.nav-item-toggle + .nav-text { + margin-left: 0; + padding-left: 22px; +} + +.nav-item-toggle:hover + .nav-link, +.nav-item-toggle:hover + .nav-text { + background: #ebf2f2; +} + +.nav-item.is-current-page > .nav-item-toggle:hover + .nav-link { + background: var(--selected-background-color); +} + +.nav-link:hover { + color: #06c; } .nav-link:hover, .nav-text:hover { text-decoration: none; background: #ebf2f2; - color: #06c; +} + +.dark-theme .nav-item-toggle:hover + .nav-link, +.dark-theme .nav-item-toggle:hover + .nav-text { + background: #262a2d; } .dark-theme .nav-link:hover, @@ -177,6 +216,12 @@ html.is-clipped--nav { .nav-panel-menu .context { padding: 0.8rem 1rem; + padding-bottom: 0; + margin-bottom: 0.5rem; + position: sticky; + top: 0; + background-color: var(--body-background-color); + z-index: 99; } .nav-panel-menu .context .title { @@ -252,9 +297,10 @@ html.dark-theme button.browse-version:hover { .nav-menu .search { position: relative; border: 0 none; - height: 44px; + height: 40px; display: none; - margin-bottom: 0.5rem; + margin: 0 -1rem; + margin-top: 1rem; } .nav-resize { @@ -309,6 +355,7 @@ html.dark-theme button.browse-version:hover { @media screen and (min-width: 1024px) { body.nav-sm .nav-container { width: 60px; + z-index: 800; } body.nav-sm .nav-collapse, @@ -327,10 +374,21 @@ html.dark-theme button.browse-version:hover { body.nav-sm .nav-menu .search { margin-bottom: 0; + margin-top: 0; + } + + body.nav-sm button.browse-version { + position: relative; + top: auto; + right: auto; + margin: 0; + margin-left: -5px; + margin-bottom: 8px; } body.nav-sm .nav-panel-menu .context { - height: 50px; + margin: 0; + position: relative; } body.nav-sm .nav-panel-menu .context .title, @@ -340,8 +398,13 @@ html.dark-theme button.browse-version:hover { display: none; } + body.nav-sm .header .navbar { + z-index: 900; + } + html.is-clipped--nav body.nav-sm .nav-menu > .nav-list { display: block; + padding-left: 10px; } body.nav-sm .nav-menu > .nav-list { display: none; @@ -350,9 +413,11 @@ html.dark-theme button.browse-version:hover { left: 61px; padding: 0.5rem 1rem; width: 300px; - bottom: -40px; + bottom: -36px; background: var(--body-background-color); border-right: 1px solid var(--nav-panel-divider-color); + z-index: 800; + overflow: auto; } body.nav-sm .toggle-sm button.nav-toggle { diff --git a/src/css/spring/spring-404.css b/src/css/spring/spring-404.css index 2308827..22f1c78 100644 --- a/src/css/spring/spring-404.css +++ b/src/css/spring/spring-404.css @@ -4,7 +4,7 @@ body.status-404 .page-404 { padding: 4rem 0; } body.status-404 h1.page { - color: #086dc3; + color: #6db33f; font-size: 130px; font-weight: 100; line-height: 110px; diff --git a/src/css/vars.css b/src/css/vars.css index 6a4ce80..d8dc8b9 100644 --- a/src/css/vars.css +++ b/src/css/vars.css @@ -16,7 +16,6 @@ /* Fonts */ --rem-base: 18; /* used to compute rem value from desired pixel value (e.g., calc(18 / var(--rem-base) * 1rem) = 18px) */ - --navbar-background: rgb(33, 37, 41); --body-font-size: 1.0625em; /* 17px */ --body-font-size--desktop: 1.125em; /* 18px */ --body-font-size--print: 0.9375em; /* 15px */ @@ -31,20 +30,25 @@ --font-weight: 400; --monospace-font-family: "SFMono-Regular", "Consolas", "Liberation Mono", "Menlo", monospace; --body-background-color: white; - --body-background: var(--color-white); - --panel-background: var(--color-smoke-30); - --panel-border-color: var(--color-smoke-90); - --scrollbar-thumb-color: var(--color-gray-10); + --body-background: var(--body-background-color); + --panel-background-color: #f6f8fa; + --panel-background: var(--panel-background-color); --panel-group-background-color: #e1e8e8; + --panel-border-color: #eaedf0; --color-accent-1: #ebf2f2; --color-accent-1-hover: #d3dddd; --color-accent-2: #d7e7e7; - --color-accent-3: rgb(27, 168, 156); + --color-accent-3: #1ba89c; + --body-font-color: #191e1e; --body-font-light-color: #273030; --body-font-dark-color: #141818; --link-font-color: #1565c0; --link_hover-font-color: #104d92; --code-link-font-color: var(--link-font-color); + --scrollbar-thumb-color: silver; + --mark-background-color: #80ea6e; + --selected-background-color: #191e1e; + --caption-font-style: italic; /* Layout */ --layout-banner-height: 80px; @@ -123,12 +127,8 @@ --social-icon-circle: #191e1e; /* Navbar */ - --navbar-font-color: #191e1e; - --mark-background-color: rgb(27, 168, 156); - --selected-background-color: #191e1e; - - /* fonts */ - --body-font-color: #191e1e; + --navbar-background: var(--body-background-color); + --navbar-font-color: var(--body-font-color); --navbar_hover-background: var(--navbar-background); --navbar-button-background: var(--color-white); --navbar-button-border-color: var(--panel-border-color); @@ -205,7 +205,7 @@ --footer-link-font-color: #111; /* Dimensions and Positioning */ - --navbar-height: calc(73 / var(--rem-base) * 1rem); + --navbar-height: calc(83 / var(--rem-base) * 1rem); --toolbar-height: calc(45 / var(--rem-base) * 1rem); --drawer-height: var(--toolbar-height); --body-top: var(--navbar-height); @@ -224,7 +224,7 @@ --z-index-nav: 1; --z-index-toolbar: 2; --z-index-page-version-menu: 3; - --z-index-navbar: 4; + --z-index-navbar: 100; } /* RESPONSIVE OVERRIDES */ diff --git a/src/css/vendor/asciidoctor-tabs.css b/src/css/vendor/asciidoctor-tabs.css index 7e12d2a..499b507 100644 --- a/src/css/vendor/asciidoctor-tabs.css +++ b/src/css/vendor/asciidoctor-tabs.css @@ -65,3 +65,7 @@ border: 0; padding: 0; } + +.doc li.tab[data-sync-id="Xml"] { + text-transform: uppercase; +} diff --git a/src/css/vendor/search.css b/src/css/vendor/search.css index 62b7152..eb79a0b 100644 --- a/src/css/vendor/search.css +++ b/src/css/vendor/search.css @@ -12,7 +12,7 @@ color: var(--body-font-color); background: var(--body-background-color); text-align: left; - padding-left: 35px; + padding-left: 40px; border: 1px solid var(--nav-panel-divider-color); border-left-width: 0; border-right-width: 0; @@ -67,7 +67,7 @@ .DocSearch-Button svg { position: absolute; top: 10px; - left: 10px; + left: 15px; width: 20px; fill: var(--body-font-color); } @@ -294,6 +294,10 @@ a.ais-Hits-item:hover { border-color: var(--nav-panel-divider-color); } +.dark-theme .search-link-box .search-link { + color: white; +} + .dark-theme #nomore { border-color: var(--nav-panel-divider-color); color: #fff; @@ -329,5 +333,9 @@ a.ais-Hits-item:hover { .DocSearch-Button { border-left-width: 1px; border-right-width: 1px; + padding-left: 35px; + } + .DocSearch-Button svg { + left: 10px; } } diff --git a/src/css/vendor/spring-tabs.css b/src/css/vendor/spring-tabs.css index 7c99d5f..afcfba5 100644 --- a/src/css/vendor/spring-tabs.css +++ b/src/css/vendor/spring-tabs.css @@ -68,7 +68,3 @@ padding: 1rem; background-color: var(--tabs-group-background-color); } - -.doc li.tab[data-sync-id="Xml"] { - text-transform: uppercase; -} diff --git a/src/helpers/log.js b/src/helpers/log.js new file mode 100644 index 0000000..87e6622 --- /dev/null +++ b/src/helpers/log.js @@ -0,0 +1,3 @@ +'use strict' + +module.exports = (a) => console.log(JSON.stringify(a, null, 2)) diff --git a/src/helpers/notEmpty.js b/src/helpers/notEmpty.js new file mode 100644 index 0000000..69e83fb --- /dev/null +++ b/src/helpers/notEmpty.js @@ -0,0 +1,3 @@ +'use strict' + +module.exports = (a) => a !== null diff --git a/src/helpers/versionTree.js b/src/helpers/versionTree.js new file mode 100644 index 0000000..2efb15e --- /dev/null +++ b/src/helpers/versionTree.js @@ -0,0 +1,21 @@ +'use strict' + +module.exports = (components) => versionTree(components) + +function versionTree (components) { + for (const [, component] of Object.entries(components)) { + component.versionTree = splitVersions(component.versions) + } + return components +} + +function splitVersions (versions) { + const snapshot = versions.filter((v) => v.displayVersion.includes('SNAPSHOT')) + const stable = versions.filter((v) => !v.displayVersion.includes('-')) + const preview = versions.filter((v) => !snapshot.includes(v) && !stable.includes(v)) + return { + snapshot: snapshot.length > 0 ? snapshot : null, + stable: stable.length > 0 ? stable : null, + preview: preview.length > 0 ? preview : null, + } +} diff --git a/src/img/link-external.svg b/src/img/link-external.svg new file mode 100644 index 0000000..8824c41 --- /dev/null +++ b/src/img/link-external.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/lorem.png b/src/img/lorem.png new file mode 100644 index 0000000..688d887 Binary files /dev/null and b/src/img/lorem.png differ diff --git a/src/img/octicons-16.svg b/src/img/octicons-16.svg index b58752f..1c7cad3 100644 --- a/src/img/octicons-16.svg +++ b/src/img/octicons-16.svg @@ -92,58 +92,64 @@ + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + \ No newline at end of file diff --git a/src/js/02-on-this-page.js b/src/js/02-on-this-page.js index 0e81790..d214554 100644 --- a/src/js/02-on-this-page.js +++ b/src/js/02-on-this-page.js @@ -69,7 +69,7 @@ function onScroll () { var scrolledBy = window.pageYOffset - var buffer = getNumericStyleVal(document.documentElement, 'fontSize') * 1.15 + var buffer = getNumericStyleVal(document.documentElement, 'fontSize') * 1.15 + 80 var ceil = article.offsetTop if (scrolledBy && window.innerHeight + scrolledBy + 2 >= document.documentElement.scrollHeight) { lastActiveFragment = Array.isArray(lastActiveFragment) ? lastActiveFragment : Array(lastActiveFragment || 0) diff --git a/src/js/03-fragment-jumper.js b/src/js/03-fragment-jumper.js index 58474a2..7923a86 100644 --- a/src/js/03-fragment-jumper.js +++ b/src/js/03-fragment-jumper.js @@ -18,7 +18,7 @@ window.location.hash = '#' + this.id e.preventDefault() } - window.scrollTo(0, computePosition(this, 0) - toolbar.getBoundingClientRect().bottom - 10) + window.scrollTo(0, computePosition(this, 0) - toolbar.getBoundingClientRect().bottom - 80) } window.addEventListener('load', function jumpOnLoad (e) { diff --git a/src/js/vendor/redirect.js b/src/js/vendor/redirect.js index 98bbf08..debfab2 100644 --- a/src/js/vendor/redirect.js +++ b/src/js/vendor/redirect.js @@ -8,7 +8,7 @@ const params = new URLSearchParams(window.location.search) const page = params.get('page') || '' const fragment = window.location.hash - const pageAndFragment = page + ((fragment.length === 1) ? '' : fragment) + const pageAndFragment = page + (fragment.length === 1 ? '' : fragment) let foundForFragment let foundForPageAndFragment const candidates = document.querySelector('body ul') diff --git a/src/partials/article.hbs b/src/partials/article.hbs index 7a99727..f66a510 100644 --- a/src/partials/article.hbs +++ b/src/partials/article.hbs @@ -1,6 +1,6 @@
-{{> article-latest}} {{> breadcrumbs}} +{{> article-latest}} {{#with page.title}}

{{{this}}}

{{/with}} @@ -10,4 +10,4 @@ {{{page.contents}}} {{> nav-section-summary}} {{> pagination}} -
+ \ No newline at end of file diff --git a/src/partials/breadcrumbs.hbs b/src/partials/breadcrumbs.hbs index 24b6fb0..82d653a 100644 --- a/src/partials/breadcrumbs.hbs +++ b/src/partials/breadcrumbs.hbs @@ -1,20 +1,22 @@ - + + \ No newline at end of file diff --git a/src/partials/head-meta.hbs b/src/partials/head-meta.hbs index a3fd907..e348936 100644 --- a/src/partials/head-meta.hbs +++ b/src/partials/head-meta.hbs @@ -1,4 +1,5 @@ {{!-- Add additional meta tags here --}} + {{!-- replaced by the gulp build --}} diff --git a/src/partials/nav-explore.hbs b/src/partials/nav-explore.hbs index 0aa2622..9d7bbf0 100644 --- a/src/partials/nav-explore.hbs +++ b/src/partials/nav-explore.hbs @@ -18,4 +18,5 @@ d="M384,224c-17.7,0-32,14.3-32,32s14.3,32,32,32s32-14.3,32-32S401.7,224,384,224L384,224z" > + {{> nav-search}} \ No newline at end of file diff --git a/src/partials/nav-menu.hbs b/src/partials/nav-menu.hbs index c99b74a..4a04b9d 100644 --- a/src/partials/nav-menu.hbs +++ b/src/partials/nav-menu.hbs @@ -1,4 +1,3 @@ -{{> nav-search}} {{#with page.navigation}} {{> nav-tree navigation=this}} {{/with}} diff --git a/src/partials/nav-tree.hbs b/src/partials/nav-tree.hbs index 79e3c1e..3bb495b 100644 --- a/src/partials/nav-tree.hbs +++ b/src/partials/nav-tree.hbs @@ -7,7 +7,7 @@ {{/if}} {{#if ./url}} - {{{./content}}} {{else}} diff --git a/src/partials/version-nav.hbs b/src/partials/version-nav.hbs new file mode 100644 index 0000000..e8b63d5 --- /dev/null +++ b/src/partials/version-nav.hbs @@ -0,0 +1,12 @@ + \ No newline at end of file diff --git a/src/partials/version.hbs b/src/partials/version.hbs index 2cbc4df..4e6f714 100644 --- a/src/partials/version.hbs +++ b/src/partials/version.hbs @@ -7,23 +7,52 @@
+
diff --git a/test/versionTree-test.js b/test/versionTree-test.js new file mode 100644 index 0000000..c456ec9 --- /dev/null +++ b/test/versionTree-test.js @@ -0,0 +1,59 @@ +/* eslint-env mocha */ +'use strict' + +const { expect } = require('./harness') +const versionTree = require('../src/helpers/versionTree.js') + +describe('versionTree', () => { + it('should return stable, preview and snapshot versions', () => { + const result = versionTree({ + test: { + versions: [ + { + displayVersion: '3.0.1-SNAPSHOT', + }, + { + displayVersion: '3.0.0-SNAPSHOT', + }, + { + displayVersion: '2.0.0', + }, + { + displayVersion: '1.0.0', + }, + { + displayVersion: '1.0.0-RC1', + }, + { + displayVersion: '1.0.0-RC2', + }, + ], + }, + }) + + const tree = result.test.versionTree + expect(tree.stable.length).is.eql(2) + expect(tree.stable[0].displayVersion).is.eql('2.0.0') + expect(tree.stable[1].displayVersion).is.eql('1.0.0') + + expect(tree.preview.length).is.eql(2) + expect(tree.preview[0].displayVersion).is.eql('1.0.0-RC1') + expect(tree.preview[1].displayVersion).is.eql('1.0.0-RC2') + + expect(tree.snapshot.length).is.eql(2) + expect(tree.snapshot[0].displayVersion).is.eql('3.0.1-SNAPSHOT') + expect(tree.snapshot[1].displayVersion).is.eql('3.0.0-SNAPSHOT') + }) + + it('should return an empty structure', () => { + const result = versionTree({ + test: { + versions: [], + }, + }) + const tree = result.test.versionTree + expect(tree.stable).is.eql(null) + expect(tree.preview).is.eql(null) + expect(tree.snapshot).is.eql(null) + }) +})