fix: Unable to navigate on server without default index support (#1372)

* Fix: Cannot serve off `/.../index.html`

Docsify must be hosted on a server that supports a default directory
index (i.e. maps `/.../` -> `/.../index.html`).

Some platforms do not support this, however. For example, HTML apps
hosted on the popular game/software platform, Itch.io.

This change supports hosting Docsify off an explicit path file, such as
`/index.html`. It does this by:

 1. Adding handling for paths like `index.html#/blah`, and
 2. Normalising paths with fragments back to markdown paths

For example, `http://example.org/index.html#/blah` would be mapped to
`http://example.org/blah.md`.

This fixes:

https://github.com/docsifyjs/docsify/issues/427

* Add end-to-end test for index file hosting

* Add code comments for explicit file changes

* Add additional tests for index file hosting

* Add additional tests for index file hosting

* [wip] Attempt to switch tests to Jest

* Add e2e test for new Jest test framework

* Verify sidebar links use file hosting

* Fix: endsWith() not supported for IE11

* Refactor: utility method moved to utility file

* Fix IE11 error from use of String.includes()

Co-authored-by: John Hildenbiddle <jhildenbiddle@users.noreply.github.com>
This commit is contained in:
Ricardo Gladwell 2021-02-07 16:25:37 +00:00 committed by GitHub
parent 14ce7f3d86
commit 759ffac992
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 73 additions and 4 deletions

View File

@ -1,13 +1,12 @@
import { noop } from '../../util/core';
import { on } from '../../util/dom';
import { parseQuery, cleanPath, replaceSlug } from '../util';
import { parseQuery, cleanPath, replaceSlug, endsWith } from '../util';
import { History } from './base';
function replaceHash(path) {
const i = location.href.indexOf('#');
location.replace(location.href.slice(0, i >= 0 ? i : 0) + '#' + path);
}
export class HashHistory extends History {
constructor(config) {
super(config);
@ -18,7 +17,15 @@ export class HashHistory extends History {
const path = window.location.pathname || '';
const base = this.config.basePath;
return /^(\/|https?:)/g.test(base) ? base : cleanPath(path + '/' + base);
// This handles the case where Docsify is served off an
// explicit file path, i.e.`/base/index.html#/blah`. This
// prevents the `/index.html` part of the URI from being
// remove during routing.
// See here: https://github.com/docsifyjs/docsify/pull/1372
const basePath = endsWith(path, '.html')
? path + '#/' + base
: path + '/' + base;
return /^(\/|https?:)/g.test(base) ? base : cleanPath(basePath);
}
getCurrentPath() {

View File

@ -76,10 +76,44 @@ export const resolvePath = cached(path => {
return '/' + resolved.join('/');
});
/**
* Normalises the URI path to handle the case where Docsify is
* hosted off explicit files, i.e. /index.html. This function
* eliminates any path segments that contain `#` fragments.
*
* This is used to map browser URIs to markdown file sources.
*
* For example:
*
* http://example.org/base/index.html#/blah
*
* would be mapped to:
*
* http://example.org/base/blah.md.
*
* See here for more information:
*
* https://github.com/docsifyjs/docsify/pull/1372
*
* @param {string} path The URI path to normalise
* @return {string} { path, query }
*/
function normaliseFragment(path) {
return path
.split('/')
.filter(p => p.indexOf('#') === -1)
.join('/');
}
export function getPath(...args) {
return cleanPath(args.join('/'));
return cleanPath(args.map(normaliseFragment).join('/'));
}
export const replaceSlug = cached(path => {
return path.replace('#', '?id=');
});
export function endsWith(str, suffix) {
return str.indexOf(suffix, str.length - suffix.length) !== -1;
}

View File

@ -0,0 +1,28 @@
const docsifyInit = require('../helpers/docsify-init');
describe(`Index file hosting`, function() {
const sharedOptions = {
config: {
basePath: `${TEST_HOST}/docs/index.html#/`,
},
testURL: `${TEST_HOST}/docs/index.html#/`,
};
test('should serve from index file', async () => {
await docsifyInit(sharedOptions);
await expect(page).toHaveText(
'#main',
'A magical documentation site generator'
);
expect(page.url()).toMatch(/index\.html#\/$/);
});
test('should use index file links in sidebar from index file hosting', async () => {
await docsifyInit(sharedOptions);
await page.click('a[href="#/quickstart"]');
await expect(page).toHaveText('#main', 'Quick start');
expect(page.url()).toMatch(/index\.html#\/quickstart$/);
});
});