diff --git a/e2e/docs/.vuepress/config.ts b/e2e/docs/.vuepress/config.ts index ca30feae74..363ef38117 100644 --- a/e2e/docs/.vuepress/config.ts +++ b/e2e/docs/.vuepress/config.ts @@ -3,6 +3,7 @@ import { viteBundler } from '@vuepress/bundler-vite' import { webpackBundler } from '@vuepress/bundler-webpack' import { defineUserConfig } from 'vuepress' import { path } from 'vuepress/utils' +import { fooPlugin } from './plugins/foo/fooPlugin.js' import { e2eTheme } from './theme/node/e2eTheme.js' const E2E_BASE = (process.env.E2E_BASE ?? '/') as '/' | `/${string}/` @@ -80,4 +81,6 @@ export default defineUserConfig({ } } }, + + plugins: [fooPlugin], }) diff --git a/e2e/docs/.vuepress/plugins/foo/fooPlugin.ts b/e2e/docs/.vuepress/plugins/foo/fooPlugin.ts new file mode 100644 index 0000000000..93ef0c8cff --- /dev/null +++ b/e2e/docs/.vuepress/plugins/foo/fooPlugin.ts @@ -0,0 +1,11 @@ +import { getDirname, path } from 'vuepress/utils' + +const __dirname = getDirname(import.meta.url) + +export const fooPlugin = { + name: 'test-plugin', + clientConfigFile: path.resolve( + __dirname, + './nonDefaultExportClientConfig.js', + ), +} diff --git a/e2e/docs/.vuepress/plugins/foo/nonDefaultExportClientConfig.js b/e2e/docs/.vuepress/plugins/foo/nonDefaultExportClientConfig.js new file mode 100644 index 0000000000..760529c3ed --- /dev/null +++ b/e2e/docs/.vuepress/plugins/foo/nonDefaultExportClientConfig.js @@ -0,0 +1,2 @@ +// test non-default-export clientConfig +import './test.css' diff --git a/e2e/docs/.vuepress/plugins/foo/test.css b/e2e/docs/.vuepress/plugins/foo/test.css new file mode 100644 index 0000000000..086105eaef --- /dev/null +++ b/e2e/docs/.vuepress/plugins/foo/test.css @@ -0,0 +1,3 @@ +#non-default-export { + font-size: 123px; +} diff --git a/e2e/docs/.vuepress/theme/client/layouts/NotFound.vue b/e2e/docs/.vuepress/theme/client/layouts/NotFound.vue index 75e27d4c0e..1e98d1e27f 100644 --- a/e2e/docs/.vuepress/theme/client/layouts/NotFound.vue +++ b/e2e/docs/.vuepress/theme/client/layouts/NotFound.vue @@ -1,3 +1,4 @@ diff --git a/e2e/docs/404.md b/e2e/docs/404.md index fac3cec274..937c74d960 100644 --- a/e2e/docs/404.md +++ b/e2e/docs/404.md @@ -2,3 +2,5 @@ routeMeta: foo: bar --- + +## NotFound H2 diff --git a/e2e/docs/README.md b/e2e/docs/README.md index 257cc5642c..eb63f0ccbf 100644 --- a/e2e/docs/README.md +++ b/e2e/docs/README.md @@ -1 +1,3 @@ foo + +## Home H2 diff --git a/e2e/docs/client-config/non-default-export.md b/e2e/docs/client-config/non-default-export.md new file mode 100644 index 0000000000..cf25bf5c20 --- /dev/null +++ b/e2e/docs/client-config/non-default-export.md @@ -0,0 +1 @@ +# non-default-export diff --git a/e2e/docs/router/navigate-by-link.md b/e2e/docs/router/navigate-by-link.md new file mode 100644 index 0000000000..6d28173095 --- /dev/null +++ b/e2e/docs/router/navigate-by-link.md @@ -0,0 +1,20 @@ +## Markdown Links with html + +- [Home with query](/?home=true) +- [Home with query and hash](/?home=true#home) +- [404 with hash](/404.html#404) +- [404 with hash and query](/404.html#404?notFound=true) + +## Markdown Links with md + +- [Home with query](/README.md?home=true) +- [Home with query and hash](/README.md?home=true#home) +- [404 with hash](/404.md#404) +- [404 with hash and query](/404.md#404?notFound=true) + +## HTML Links + +Home +Home +404 +404 diff --git a/e2e/docs/router/navigate-by-router.md b/e2e/docs/router/navigate-by-router.md new file mode 100644 index 0000000000..444c5ae29f --- /dev/null +++ b/e2e/docs/router/navigate-by-router.md @@ -0,0 +1,26 @@ + + + + + + diff --git a/e2e/docs/router/navigation.md b/e2e/docs/router/navigation.md deleted file mode 100644 index 624df7c8f1..0000000000 --- a/e2e/docs/router/navigation.md +++ /dev/null @@ -1,16 +0,0 @@ - - - - diff --git a/e2e/tests/client-config/non-default-export.spec.ts b/e2e/tests/client-config/non-default-export.spec.ts new file mode 100644 index 0000000000..b00265a6ae --- /dev/null +++ b/e2e/tests/client-config/non-default-export.spec.ts @@ -0,0 +1,11 @@ +import { expect, test } from '@playwright/test' + +test('should apply styles correctly if the client config file does not have default export', async ({ + page, +}) => { + await page.goto('client-config/non-default-export.html') + await expect(page.locator('#non-default-export')).toHaveCSS( + 'font-size', + '123px', + ) +}) diff --git a/e2e/tests/router/navigate-by-link.spec.ts b/e2e/tests/router/navigate-by-link.spec.ts new file mode 100644 index 0000000000..a2076b0dd2 --- /dev/null +++ b/e2e/tests/router/navigate-by-link.spec.ts @@ -0,0 +1,86 @@ +import { expect, test } from '@playwright/test' +import { BASE } from '../../utils/env' + +test.beforeEach(async ({ page }) => { + await page.goto('router/navigate-by-link.html') +}) + +test.describe('should preserve query', () => { + test('markdown links with html suffix', async ({ page }) => { + await page.locator('#markdown-links-with-html + ul > li > a').nth(0).click() + await expect(page).toHaveURL(`${BASE}?home=true`) + await expect(page.locator('#home-h2')).toHaveText('Home H2') + }) + + test('markdown links with md suffix', async ({ page }) => { + await page.locator('#markdown-links-with-md + ul > li > a').nth(0).click() + await expect(page).toHaveURL(`${BASE}?home=true`) + await expect(page.locator('#home-h2')).toHaveText('Home H2') + }) + + test('html links', async ({ page }) => { + await page.locator('#html-links + p > a').nth(0).click() + await expect(page).toHaveURL(`${BASE}?home=true`) + await expect(page.locator('#home-h2')).toHaveText('Home H2') + }) +}) + +test.describe('should preserve query and hash', () => { + test('markdown links with html suffix', async ({ page }) => { + await page.locator('#markdown-links-with-html + ul > li > a').nth(1).click() + await expect(page).toHaveURL(`${BASE}?home=true#home`) + await expect(page.locator('#home-h2')).toHaveText('Home H2') + }) + + test('markdown links with md suffix', async ({ page }) => { + await page.locator('#markdown-links-with-md + ul > li > a').nth(1).click() + await expect(page).toHaveURL(`${BASE}?home=true#home`) + await expect(page.locator('#home-h2')).toHaveText('Home H2') + }) + + test('html links', async ({ page }) => { + await page.locator('#html-links + p > a').nth(1).click() + await expect(page).toHaveURL(`${BASE}?home=true#home`) + await expect(page.locator('#home-h2')).toHaveText('Home H2') + }) +}) + +test.describe('should preserve hash', () => { + test('markdown links with html suffix', async ({ page }) => { + await page.locator('#markdown-links-with-html + ul > li > a').nth(2).click() + await expect(page).toHaveURL(`${BASE}404.html#404`) + await expect(page.locator('#notfound-h2')).toHaveText('NotFound H2') + }) + + test('markdown links with md suffix', async ({ page }) => { + await page.locator('#markdown-links-with-md + ul > li > a').nth(2).click() + await expect(page).toHaveURL(`${BASE}404.html#404`) + await expect(page.locator('#notfound-h2')).toHaveText('NotFound H2') + }) + + test('html links', async ({ page }) => { + await page.locator('#html-links + p > a').nth(2).click() + await expect(page).toHaveURL(`${BASE}404.html#404`) + await expect(page.locator('#notfound-h2')).toHaveText('NotFound H2') + }) +}) + +test.describe('should preserve hash and query', () => { + test('markdown links with html suffix', async ({ page }) => { + await page.locator('#markdown-links-with-html + ul > li > a').nth(3).click() + await expect(page).toHaveURL(`${BASE}404.html#404?notFound=true`) + await expect(page.locator('#notfound-h2')).toHaveText('NotFound H2') + }) + + test('markdown links with md suffix', async ({ page }) => { + await page.locator('#markdown-links-with-md + ul > li > a').nth(3).click() + await expect(page).toHaveURL(`${BASE}404.html#404?notFound=true`) + await expect(page.locator('#notfound-h2')).toHaveText('NotFound H2') + }) + + test('html links', async ({ page }) => { + await page.locator('#html-links + p > a').nth(3).click() + await expect(page).toHaveURL(`${BASE}404.html#404?notFound=true`) + await expect(page.locator('#notfound-h2')).toHaveText('NotFound H2') + }) +}) diff --git a/e2e/tests/router/navigate-by-router.spec.ts b/e2e/tests/router/navigate-by-router.spec.ts new file mode 100644 index 0000000000..9ae3c7b325 --- /dev/null +++ b/e2e/tests/router/navigate-by-router.spec.ts @@ -0,0 +1,30 @@ +import { expect, test } from '@playwright/test' +import { BASE } from '../../utils/env' + +test.beforeEach(async ({ page }) => { + await page.goto('router/navigate-by-router.html') +}) + +test('should preserve query', async ({ page }) => { + await page.locator('#home-with-query').click() + await expect(page).toHaveURL(`${BASE}?home=true`) + await expect(page.locator('#home-h2')).toHaveText('Home H2') +}) + +test('should preserve query and hash', async ({ page }) => { + await page.locator('#home-with-query-and-hash').click() + await expect(page).toHaveURL(`${BASE}?home=true#home`) + await expect(page.locator('#home-h2')).toHaveText('Home H2') +}) + +test('should preserve hash', async ({ page }) => { + await page.locator('#not-found-with-hash').click() + await expect(page).toHaveURL(`${BASE}404.html#404`) + await expect(page.locator('#notfound-h2')).toHaveText('NotFound H2') +}) + +test('should preserve hash and query', async ({ page }) => { + await page.locator('#not-found-with-hash-and-query').click() + await expect(page).toHaveURL(`${BASE}404.html#404?notFound=true`) + await expect(page.locator('#notfound-h2')).toHaveText('NotFound H2') +}) diff --git a/e2e/tests/router/navigation.spec.ts b/e2e/tests/router/navigation.spec.ts deleted file mode 100644 index 76573acf6c..0000000000 --- a/e2e/tests/router/navigation.spec.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { expect, test } from '@playwright/test' -import { BASE } from '../../utils/env' - -test('should preserve query', async ({ page }) => { - await page.goto('router/navigation.html') - - await page.locator('#home').click() - - await expect(page).toHaveURL(`${BASE}?home=true`) -}) - -test('should preserve hash', async ({ page }) => { - await page.goto('router/navigation.html') - - await page.locator('#not-found').click() - - await expect(page).toHaveURL(`${BASE}404.html#404`) -}) diff --git a/packages/client/src/router/resolveRoute.ts b/packages/client/src/router/resolveRoute.ts index d25f396785..c63be5763f 100644 --- a/packages/client/src/router/resolveRoute.ts +++ b/packages/client/src/router/resolveRoute.ts @@ -1,4 +1,4 @@ -import { resolvePathInfo, resolveRoutePathWithExt } from '@vuepress/shared' +import { resolveRoutePathWithExt, splitPath } from '@vuepress/shared' import { routes } from '../internal/routes.js' import type { Route, RouteMeta } from '../types/index.js' import { resolveRouteKey } from './resolveRouteKey.js' @@ -17,7 +17,7 @@ export const resolveRoute = ( currentPath?: string, ): ResolvedRoute => { // get only the pathname from the path - const [pathname, hashAndQueries] = resolvePathInfo(path) + const { pathname, hashAndQueries } = splitPath(path) // resolve the route path const routeKey = resolveRouteKey(pathname, currentPath) diff --git a/packages/client/src/router/resolveRouteFullPath.ts b/packages/client/src/router/resolveRouteFullPath.ts index a1ab501de6..694386d74b 100644 --- a/packages/client/src/router/resolveRouteFullPath.ts +++ b/packages/client/src/router/resolveRouteFullPath.ts @@ -1,4 +1,4 @@ -import { resolvePathInfo } from '@vuepress/shared' +import { splitPath } from '@vuepress/shared' import { resolveRoutePath } from './resolveRoutePath.js' /** @@ -8,7 +8,6 @@ export const resolveRouteFullPath = ( path: string, currentPath?: string, ): string => { - const [pathname, hashAndQueries] = resolvePathInfo(path) - + const { pathname, hashAndQueries } = splitPath(path) return resolveRoutePath(pathname, currentPath) + hashAndQueries } diff --git a/packages/core/src/app/prepare/prepareClientConfigs.ts b/packages/core/src/app/prepare/prepareClientConfigs.ts index 6803da2296..59bb1f5624 100644 --- a/packages/core/src/app/prepare/prepareClientConfigs.ts +++ b/packages/core/src/app/prepare/prepareClientConfigs.ts @@ -11,12 +11,14 @@ export const prepareClientConfigs = async (app: App): Promise => { // generate client config files entry const content = `\ ${clientConfigFiles - .map((filePath, index) => `import clientConfig${index} from '${filePath}'`) + .map( + (filePath, index) => `import * as clientConfig${index} from '${filePath}'`, + ) .join('\n')} export const clientConfigs = [ ${clientConfigFiles.map((_, index) => ` clientConfig${index},`).join('\n')} -] +].map((m) => m.default).filter(Boolean) ` await app.writeTemp('internal/clientConfigs.js', content) diff --git a/packages/shared/src/utils/routes/index.ts b/packages/shared/src/utils/routes/index.ts index 0a6f4a5596..e0558e4a4c 100644 --- a/packages/shared/src/utils/routes/index.ts +++ b/packages/shared/src/utils/routes/index.ts @@ -3,4 +3,4 @@ export * from './normalizeRoutePath.js' export * from './resolveRoutePathWithExt.js' export * from './resolveLocalePath.js' export * from './resolveRoutePathFromUrl.js' -export * from './resolvePathInfo.js' +export * from './splitPath.js' diff --git a/packages/shared/src/utils/routes/resolvePathInfo.ts b/packages/shared/src/utils/routes/resolvePathInfo.ts deleted file mode 100644 index 99ab36c9c7..0000000000 --- a/packages/shared/src/utils/routes/resolvePathInfo.ts +++ /dev/null @@ -1,12 +0,0 @@ -const SPLIT_CHAR_REGEXP = /(#|\?)/ - -/** - * Extract pathname / hash and queries from a relative URL - */ -export const resolvePathInfo = ( - path: string, -): [pathname: string, hashAndQueries: string] => { - const [pathname, ...hashAndQueries] = path.split(SPLIT_CHAR_REGEXP) - - return [pathname, hashAndQueries.join('')] -} diff --git a/packages/shared/src/utils/routes/splitPath.ts b/packages/shared/src/utils/routes/splitPath.ts new file mode 100644 index 0000000000..2aa3906dc6 --- /dev/null +++ b/packages/shared/src/utils/routes/splitPath.ts @@ -0,0 +1,17 @@ +const SPLIT_CHAR_REGEXP = /(#|\?)/ + +/** + * Split a path into pathname and hashAndQueries + */ +export const splitPath = ( + path: string, +): { + pathname: string + hashAndQueries: string +} => { + const [pathname, ...hashAndQueries] = path.split(SPLIT_CHAR_REGEXP) + return { + pathname, + hashAndQueries: hashAndQueries.join(''), + } +} diff --git a/packages/shared/tests/routes/resolvePathInfo.spec.ts b/packages/shared/tests/routes/resolvePathInfo.spec.ts deleted file mode 100644 index a71f71b5e0..0000000000 --- a/packages/shared/tests/routes/resolvePathInfo.spec.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { resolvePathInfo } from '../../src/index.js' - -const testCases: [string, [string, string]][] = [ - ['/a/b/c/', ['/a/b/c/', '']], - ['/a/b/c/?a=1', ['/a/b/c/', '?a=1']], - ['/a/b/c/#b', ['/a/b/c/', '#b']], - ['/a/b/c/?a=1#b', ['/a/b/c/', '?a=1#b']], - ['a/index.html', ['a/index.html', '']], - ['/a/index.html?a=1', ['/a/index.html', '?a=1']], - ['/a/index.html#a', ['/a/index.html', '#a']], - ['/a/index.html?a=1#b', ['/a/index.html', '?a=1#b']], -] - -describe('should resolve path info correctly', () => { - testCases.forEach(([source, expected]) => { - it(`${source} -> ${expected}`, () => { - expect(resolvePathInfo(source)).toEqual(expected) - }) - }) -}) diff --git a/packages/shared/tests/routes/splitPath.spec.ts b/packages/shared/tests/routes/splitPath.spec.ts new file mode 100644 index 0000000000..6dfd2452fb --- /dev/null +++ b/packages/shared/tests/routes/splitPath.spec.ts @@ -0,0 +1,22 @@ +import { expect, it } from 'vitest' +import { splitPath } from '../../src/index.js' + +const testCases: [string, ReturnType][] = [ + ['/a/b/c/', { pathname: '/a/b/c/', hashAndQueries: '' }], + ['/a/b/c/?a=1', { pathname: '/a/b/c/', hashAndQueries: '?a=1' }], + ['/a/b/c/#b', { pathname: '/a/b/c/', hashAndQueries: '#b' }], + ['/a/b/c/?a=1#b', { pathname: '/a/b/c/', hashAndQueries: '?a=1#b' }], + ['a/index.html', { pathname: 'a/index.html', hashAndQueries: '' }], + ['/a/index.html?a=1', { pathname: '/a/index.html', hashAndQueries: '?a=1' }], + ['/a/index.html#a', { pathname: '/a/index.html', hashAndQueries: '#a' }], + [ + '/a/index.html?a=1#b', + { pathname: '/a/index.html', hashAndQueries: '?a=1#b' }, + ], +] + +testCases.forEach(([source, expected]) => { + it(`${source} -> ${expected}`, () => { + expect(splitPath(source)).toEqual(expected) + }) +})