diff --git a/src/chrome/chromeUtils.ts b/src/chrome/chromeUtils.ts index 39618f449..62f6c6424 100644 --- a/src/chrome/chromeUtils.ts +++ b/src/chrome/chromeUtils.ts @@ -11,32 +11,62 @@ import {ITarget} from './chromeConnection'; export function targetUrlToClientPathByPathMappings(scriptUrl: string, pathMapping: any): string { const parsedUrl = url.parse(scriptUrl); - const origin = `${parsedUrl.protocol}//${parsedUrl.host}`; if (!parsedUrl.protocol || parsedUrl.protocol.startsWith('file') || !parsedUrl.pathname) { // Skip file: URLs and paths, and invalid things return ''; } - let pSegments = parsedUrl.pathname.split('/'); - while (pSegments.length) { - let p = pSegments.join('/'); - - if (pSegments.length === 1 && p === '') { - // Root path segment. - p = '/'; + const urlWithoutQuery = parsedUrl.protocol + "//" + parsedUrl.host + parsedUrl.pathname; + for (let pattern of Object.keys(pathMapping)) { + // empty pattern match nothing use / to match root + if (pattern) { + const localPath = pathMapping[pattern]; + const parsedPattern = url.parse(pattern); + + if (parsedPattern.protocol) { + // pattern is an url with protocol + if (urlWithoutQuery.startsWith(pattern)) { + const clientPath = toClientPath(localPath, parsedUrl.pathname, pattern); + if (clientPath) { + return clientPath; + } + } + } else if (pattern[0] === "/") { + // pattern is absolute + if (parsedUrl.pathname.startsWith(pattern)) { + const clientPath = toClientPath(localPath, parsedUrl.pathname, pattern); + if (clientPath) { + return clientPath; + } + } + } else { + // pattern is relative + // avoid matching whole segment + pattern = "/" + pattern; + const indexOf = parsedUrl.pathname.indexOf(pattern); + if (indexOf !== -1) { + const clientPath = toClientPath(localPath, parsedUrl.pathname.substring(indexOf), pattern); + if (clientPath) { + return clientPath; + } + } + } } + } + return ''; +} - let localPath = pathMapping[origin + p] || pathMapping[origin + p + '/'] || - pathMapping[p + '/'] || pathMapping[p]; - - if (localPath) { - const r = decodeURIComponent(parsedUrl.pathname.substring(p.length)); +function toClientPath(localPath: string, source: string, pattern: string): string { + if (source.length === pattern.length) { + return localPath; + } else { + // Verify that matching whole segment of the pattern + if (source[pattern.length - 1] === "/" + || source[pattern.length] === "/") { + const r = decodeURIComponent(source.substring(pattern.length)); return path.join(localPath, r); } - - pSegments.pop(); } - return ''; } diff --git a/test/chrome/chromeUtils.test.ts b/test/chrome/chromeUtils.test.ts index 24a4c63e3..a243754d8 100644 --- a/test/chrome/chromeUtils.test.ts +++ b/test/chrome/chromeUtils.test.ts @@ -96,8 +96,9 @@ suite('ChromeUtils', () => { const ROOT_MAPPING = { '/': TEST_WEB_ROOT }; const PAGE_MAPPING = { '/page/': TEST_WEB_ROOT }; - const PARTIAL_PAGE_MAPPING = { '/page': TEST_WEB_ROOT }; + const PARTIAL_PAGE_MAPPING = { '/page': TEST_WEB_ROOT, 'page': TEST_WEB_ROOT}; const FILE_MAPPING = { '/page.js': TEST_CLIENT_PATH }; + const RELATIVE_FILE_MAPPING = { 'page.js': TEST_CLIENT_PATH}; test('an empty string is returned for a missing url', () => { assert.equal(getChromeUtils().targetUrlToClientPathByPathMappings('', { }), ''); @@ -145,7 +146,7 @@ suite('ChromeUtils', () => { TEST_CLIENT_PATH); }); - test('resolves webroot-style mapping without tailing slash', () => { + test('resolves webroot-style mapping without trailing slash', () => { assert.equal( getChromeUtils().targetUrlToClientPathByPathMappings(TEST_TARGET_HTTP_URL, PARTIAL_PAGE_MAPPING), TEST_CLIENT_PATH); @@ -168,6 +169,13 @@ suite('ChromeUtils', () => { assert.equal(getChromeUtils().targetUrlToClientPathByPathMappings(url, PARTIAL_PAGE_MAPPING), ''); }); + + test('resolves pathMapping for a particular relative file', () => { + const url = 'http://site.com/page.js'; + + assert.equal(getChromeUtils().targetUrlToClientPathByPathMappings(url, RELATIVE_FILE_MAPPING), TEST_CLIENT_PATH); + }); + }); suite('remoteObjectToValue()', () => {