Skip to content

Commit 2534495

Browse files
author
Franck Freiburger
committed
fix(core): support resource path with search string (ex. comp.vue?v=a87e5cc1)
customizable through getPathname option that defaults to just strip search string. pathResolve() get "options" as second argument closes #182
1 parent 1ea5b2f commit 2534495

File tree

5 files changed

+66
-10
lines changed

5 files changed

+66
-10
lines changed

docs/examples.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -948,7 +948,7 @@ In the following example we use a trick to preserve reactivity through the `Vue.
948948
}
949949
950950
// get the actual path of the file
951-
const path = pathResolve({ refPath, relPath: resourceRelPath });
951+
const path = pathResolve({ refPath, relPath: resourceRelPath }, options);
952952
953953
// the resource id must be unique in its path context
954954
const id = loaders.join('') + path;
@@ -1197,7 +1197,7 @@ This example use Vue2 because **vue-calendar-picker** is written for Vue2.
11971197
vue: Vue,
11981198
'date-fns/locale/en/index.js': {}, // handle require('date-fns/locale/' + this.locale.toLowerCase() + '/index.js');
11991199
},
1200-
pathResolve({ refPath, relPath }) {
1200+
pathResolve({ refPath, relPath }, options) {
12011201
12021202
if ( relPath === 'date-fns' )
12031203
return 'https://cdnjs.cloudflare.com/ajax/libs/date-fns/1.30.1/date_fns.min.js';

src/index.ts

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,30 @@ function throwNotDefined(details : string) : never {
4141
}
4242

4343

44+
/**
45+
* Default getPathname implementation
46+
* remove search string
47+
*/
48+
const defaultGetPathname = (path : string) => {
49+
50+
// alternative: new URL(path, 'file://').pathname
51+
const searchPos = path.indexOf('?');
52+
if ( searchPos !== -1 )
53+
return path.slice(0, searchPos);
54+
return path;
55+
}
56+
57+
4458
/**
4559
* Default resolve implementation
4660
* resolve() should handle 3 situations :
4761
* - resolve a relative path ( eg. import './details.vue' )
4862
* - resolve an absolute path ( eg. import '/components/card.vue' )
4963
* - resolve a module name ( eg. import { format } from 'date-fns' )
5064
*/
51-
const defaultPathResolve : PathResolve = ({ refPath, relPath } : PathContext) => {
65+
const defaultPathResolve : PathResolve = ({ refPath, relPath } : PathContext, options : Options) => {
66+
67+
const { getPathname } = options;
5268

5369
// initial resolution: refPath is not defined
5470
if ( refPath === undefined )
@@ -64,7 +80,7 @@ const defaultPathResolve : PathResolve = ({ refPath, relPath } : PathContext) =>
6480
// normalize('./test') -> 'test'
6581
// normalize('/test') -> '/test'
6682

67-
return Path.normalize(Path.join(Path.dirname(refPath.toString()), relPathStr));
83+
return Path.normalize(Path.join(Path.dirname(getPathname(refPath.toString())), relPathStr));
6884
}
6985

7086
/**
@@ -73,8 +89,8 @@ const defaultPathResolve : PathResolve = ({ refPath, relPath } : PathContext) =>
7389
*/
7490
function defaultGetResource(pathCx : PathContext, options : Options) : Resource {
7591

76-
const { pathResolve, getFile, log } = options;
77-
const path = pathResolve(pathCx);
92+
const { pathResolve, getPathname, getFile, log } = options;
93+
const path = pathResolve(pathCx, options);
7894
const pathStr = path.toString();
7995
return {
8096
id: pathStr,
@@ -86,7 +102,7 @@ function defaultGetResource(pathCx : PathContext, options : Options) : Resource
86102
if ( typeof res === 'string' || res instanceof ArrayBuffer ) {
87103

88104
return {
89-
type: Path.extname(pathStr),
105+
type: Path.extname(getPathname(pathStr)),
90106
getContentData: async (asBinary) => {
91107

92108
if ( res instanceof ArrayBuffer !== asBinary )
@@ -103,7 +119,7 @@ function defaultGetResource(pathCx : PathContext, options : Options) : Resource
103119
}
104120

105121
return {
106-
type: res.type !== undefined ? res.type : Path.extname(pathStr),
122+
type: res.type !== undefined ? res.type : Path.extname(getPathname(pathStr)),
107123
getContentData: res.getContentData,
108124
}
109125
}
@@ -161,6 +177,7 @@ export async function loadModule(path : AbstractPath, options : Options = throwN
161177
pathResolve = defaultPathResolve,
162178
getResource = defaultGetResource,
163179
createCJSModule = defaultCreateCJSModule,
180+
getPathname = defaultGetPathname,
164181
} = options;
165182

166183
// moduleCache should be defined with Object.create(null). require('constructor') etc... should not be a default module
@@ -176,6 +193,8 @@ export async function loadModule(path : AbstractPath, options : Options = throwN
176193
getResource,
177194
//@ts-ignore: is specified more than once, so this usage will be overwritten.ts(2783)
178195
createCJSModule,
196+
//@ts-ignore: is specified more than once, so this usage will be overwritten.ts(2783)
197+
getPathname,
179198
...options,
180199
};
181200

src/tools.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ export function defaultCreateCJSModule(refPath : AbstractPath, source : string,
349349
// see https://github.com/nodejs/node/blob/a46b21f556a83e43965897088778ddc7d46019ae/lib/internal/modules/cjs/loader.js#L195-L198
350350
// see https://github.com/nodejs/node/blob/a46b21f556a83e43965897088778ddc7d46019ae/lib/internal/modules/cjs/loader.js#L1102
351351
const moduleFunction = Function('exports', 'require', 'module', '__filename', '__dirname', '__vsfcl_import__', source);
352-
moduleFunction.call(module.exports, module.exports, require, module, refPath, pathResolve({ refPath, relPath: '.' }), importFunction);
352+
moduleFunction.call(module.exports, module.exports, require, module, refPath, pathResolve({ refPath, relPath: '.' }, options), importFunction);
353353

354354
return module;
355355
}

src/types.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export type PathContext = {
4747

4848

4949
/** relative to absolute module path resolution */
50-
export type PathResolve = (pathCx : PathContext) => AbstractPath;
50+
export type PathResolve = (pathCx : PathContext, options : Options) => AbstractPath;
5151

5252

5353
/**
@@ -387,6 +387,13 @@ export type Options = {
387387
*/
388388
pathResolve : PathResolve,
389389

390+
/**
391+
* by default, remove the search string
392+
* in situation where you need to keep the path intact, use `getPathname: path => path`
393+
* @param path a path that may contains extra components like search params or hash (eg. ./mydir/mycomponent.vue?v=123)
394+
* @returns the pathname component of the path withouy any orher component (eg. ./mydir/mycomponent.vue)
395+
*/
396+
getPathname : (path : string) => string,
390397

391398
/**
392399
* Abstact resource handling

tests/basic.test.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1270,5 +1270,35 @@ const { defaultFilesFactory, createPage } = require('./testsTools.js');
12701270
await expect(page.$eval('#app', el => el.textContent.trim())).resolves.toBe('main_comp comp_1');
12711271
});
12721272
}
1273+
1274+
1275+
test('should properly handle search string in path', async () => {
1276+
1277+
const { page, output } = await createPage({
1278+
files: {
1279+
...files,
1280+
'/components/comp1.vue': `
1281+
<template>1</template>
1282+
`,
1283+
'/components/comp2.vue': `
1284+
<template>2</template>
1285+
`,
1286+
'/main.vue': `
1287+
<script setup>
1288+
import comp1 from './components/comp1.vue?a=1.txt'
1289+
import comp2 from './components/comp2.vue?a=/2'
1290+
</script>
1291+
<template><comp1/><comp2/></template>
1292+
`,
1293+
}
1294+
});
1295+
1296+
await expect(page.$eval('#app', el => el.textContent.trim())).resolves.toBe('12');
1297+
});
1298+
1299+
1300+
1301+
1302+
12731303
});
12741304
})

0 commit comments

Comments
 (0)