Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Cannot serve off /.../index.html #1372

Merged
13 changes: 10 additions & 3 deletions src/core/router/history/hash.js
Original file line number Diff line number Diff line change
@@ -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);
Expand All @@ -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() {
Expand Down
36 changes: 35 additions & 1 deletion src/core/router/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
28 changes: 28 additions & 0 deletions test/e2e/index-file.test.js
Original file line number Diff line number Diff line change
@@ -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$/);
});
});