Skip to content

Commit 7aa777a

Browse files
authored
Merge pull request #226 from vankop/add-hash-support
add fragment support
2 parents cf9cd25 + 7265478 commit 7aa777a

File tree

8 files changed

+128
-40
lines changed

8 files changed

+128
-40
lines changed

lib/ExportsFieldPlugin.js

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,12 @@ module.exports = class ExportsFieldPlugin {
5151
)
5252
return callback();
5353

54-
const remainingRequest = request.query
55-
? (request.request === "." ? "./" : request.request) + request.query
56-
: request.request;
54+
const remainingRequest =
55+
request.query || request.fragment
56+
? (request.request === "." ? "./" : request.request) +
57+
request.query +
58+
request.fragment
59+
: request.request;
5760
/** @type {ExportsField|null} */
5861
const exportsField = DescriptionFileUtils.getField(
5962
request.descriptionFileData,
@@ -106,20 +109,12 @@ module.exports = class ExportsFieldPlugin {
106109
forEachBail(
107110
paths,
108111
(p, callback) => {
109-
let query;
110-
let relativePath;
111-
112-
if (request.query) {
113-
const queryIndex = p.indexOf("?");
114-
if (queryIndex > -1) {
115-
query = p.slice(queryIndex);
116-
relativePath = p.slice(0, queryIndex);
117-
} else {
118-
relativePath = p;
119-
}
120-
} else {
121-
relativePath = p;
122-
}
112+
const match = /^([^?#]*)(\?[^#]*)?(#.*)?$/.exec(p);
113+
114+
if (!match) return callback();
115+
116+
const [, relativePath, query, fragment] = match;
117+
123118
const error = checkExportsFieldTarget(relativePath);
124119

125120
if (error) {
@@ -134,7 +129,8 @@ module.exports = class ExportsFieldPlugin {
134129
relativePath
135130
),
136131
relativePath,
137-
query
132+
query: query || "",
133+
fragment: fragment || ""
138134
};
139135

140136
resolver.doResolve(

lib/LogInfoPlugin.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ module.exports = class LogInfoPlugin {
3232
if (request.directory) log(prefix + "Request is a directory request.");
3333
if (request.query)
3434
log(prefix + "Resolving request query: " + request.query);
35+
if (request.fragment)
36+
log(prefix + "Resolving request fragment: " + request.fragment);
3537
if (request.descriptionFilePath)
3638
log(
3739
prefix + "Has description data from " + request.descriptionFilePath

lib/ParsePlugin.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,14 @@ module.exports = class ParsePlugin {
2727
resolver
2828
.getHook(this.source)
2929
.tapAsync("ParsePlugin", (request, resolveContext, callback) => {
30-
const parsed = resolver.parse(request.request);
30+
const parsed = resolver.parse(/** @type {string} */ (request.request));
3131
const obj = { ...request, ...parsed };
3232
if (request.query && !parsed.query) {
3333
obj.query = request.query;
3434
}
35+
if (request.fragment && !parsed.fragment) {
36+
obj.fragment = request.fragment;
37+
}
3538
if (parsed && resolveContext.log) {
3639
if (parsed.module) resolveContext.log("Parsed request is a module");
3740
if (parsed.directory)

lib/Resolver.js

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,22 @@ const {
3838
* @property {function(string, function(PossibleFileSystemError & Error | null | undefined, FileSystemStats=): void): void} stat
3939
*/
4040

41+
/**
42+
* @typedef {Object} ParsedIdentifier
43+
* @property {string} request
44+
* @property {string} query
45+
* @property {string} fragment
46+
* @property {boolean} directory
47+
* @property {boolean} module
48+
* @property {boolean} file
49+
*/
50+
4151
/**
4252
* @typedef {Object} ResolveRequest
4353
* @property {string | false} path
4454
* @property {string=} request
4555
* @property {string=} query
56+
* @property {string=} fragment
4657
* @property {boolean=} directory
4758
* @property {boolean=} module
4859
* @property {string=} descriptionFilePath
@@ -93,6 +104,7 @@ class Resolver {
93104
") " +
94105
(request.request || "") +
95106
(request.query || "") +
107+
(request.fragment || "") +
96108
(request.directory ? " directory" : "") +
97109
(request.module ? " module" : "")
98110
);
@@ -215,7 +227,7 @@ class Resolver {
215227
* @param {string} path context path
216228
* @param {string} request request string
217229
* @param {ResolveContext} resolveContext resolve context
218-
* @param {function(Error | null, string=, ResolveRequest=): void} callback callback function
230+
* @param {function(Error | null, (string|false)=, ResolveRequest=): void} callback callback function
219231
* @returns {void}
220232
*/
221233
resolve(context, path, request, resolveContext, callback) {
@@ -230,7 +242,9 @@ class Resolver {
230242
const finishResolved = result => {
231243
return callback(
232244
null,
233-
result.path === false ? false : result.path + (result.query || ""),
245+
result.path === false
246+
? false
247+
: `${result.path}${result.query || ""}${result.fragment || ""}`,
234248
result
235249
);
236250
};
@@ -363,23 +377,28 @@ class Resolver {
363377
}
364378
}
365379

380+
/**
381+
* @param {string} identifier identifier
382+
* @returns {ParsedIdentifier} parsed identifier
383+
*/
366384
parse(identifier) {
367385
const part = {
368386
request: "",
369387
query: "",
388+
fragment: "",
370389
module: false,
371390
directory: false,
372391
file: false
373392
};
374-
const idxQuery = identifier.indexOf("?");
375-
if (idxQuery === 0) {
376-
part.query = identifier;
377-
} else if (idxQuery > 0) {
378-
part.request = identifier.slice(0, idxQuery);
379-
part.query = identifier.slice(idxQuery);
380-
} else {
381-
part.request = identifier;
382-
}
393+
394+
const match = /^([^?#]*)(\?[^#]*)?(#.*)?$/.exec(identifier);
395+
396+
if (!match) return part;
397+
398+
part.request = match[1] || "";
399+
part.query = match[2] || "";
400+
part.fragment = match[3] || "";
401+
383402
if (part.request) {
384403
part.module = this.isModule(part.request);
385404
part.directory = this.isDirectory(part.request);

lib/UnsafeCachePlugin.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ function getCacheId(request, withContext) {
1515
context: withContext ? request.context : "",
1616
path: request.path,
1717
query: request.query,
18+
fragment: request.fragment,
1819
request: request.request
1920
});
2021
}

test/exportsField.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1429,6 +1429,35 @@ describe("ExportsFieldPlugin", () => {
14291429
});
14301430
});
14311431

1432+
it("resolver should respect fragment parameters #1", done => {
1433+
resolver.resolve(
1434+
{},
1435+
fixture2,
1436+
"exports-field/dist/browser.js#foo",
1437+
{},
1438+
(err, result) => {
1439+
if (err) return done(err);
1440+
if (!result) throw new Error("No result");
1441+
result.should.equal(
1442+
path.resolve(
1443+
fixture2,
1444+
"node_modules/exports-field/lib/browser.js#foo"
1445+
)
1446+
);
1447+
done();
1448+
}
1449+
);
1450+
});
1451+
1452+
it("resolver should respect fragment parameters #2. Direct matching", done => {
1453+
resolver.resolve({}, fixture2, "exports-field#foo", {}, (err, result) => {
1454+
if (!err) throw new Error(`expect error, got ${result}`);
1455+
err.should.be.instanceof(Error);
1456+
err.message.should.match(/Package path \.\/#foo is not exported/);
1457+
done();
1458+
});
1459+
});
1460+
14321461
it("relative path should work, if relative path as request is used", done => {
14331462
resolver.resolve(
14341463
{},

test/resolve.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,12 +131,49 @@ describe("resolve", function() {
131131
"./main1.js?query",
132132
path.join(fixtures, "main1.js") + "?query"
133133
);
134+
testResolve(
135+
"file with fragment",
136+
fixtures,
137+
"./main1.js#fragment",
138+
path.join(fixtures, "main1.js") + "#fragment"
139+
);
140+
testResolve(
141+
"file with fragment and query",
142+
fixtures,
143+
"./main1.js#fragment?query",
144+
path.join(fixtures, "main1.js") + "#fragment?query"
145+
);
146+
testResolve(
147+
"file with query and fragment",
148+
fixtures,
149+
"./main1.js?#fragment",
150+
path.join(fixtures, "main1.js") + "?#fragment"
151+
);
152+
134153
testResolve(
135154
"file in module with query",
136155
fixtures,
137156
"m1/a?query",
138157
path.join(fixtures, "node_modules", "m1", "a.js") + "?query"
139158
);
159+
testResolve(
160+
"file in module with fragment",
161+
fixtures,
162+
"m1/a#fragment",
163+
path.join(fixtures, "node_modules", "m1", "a.js") + "#fragment"
164+
);
165+
testResolve(
166+
"file in module with fragment and query",
167+
fixtures,
168+
"m1/a#fragment?query",
169+
path.join(fixtures, "node_modules", "m1", "a.js") + "#fragment?query"
170+
);
171+
testResolve(
172+
"file in module with query and fragment",
173+
fixtures,
174+
"m1/a?#fragment",
175+
path.join(fixtures, "node_modules", "m1", "a.js") + "?#fragment"
176+
);
140177

141178
testResolveContext("context for fixtures", fixtures, "./", fixtures);
142179
testResolveContext(

types.d.ts

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,14 @@ declare class LogInfoPlugin {
6868
source: any;
6969
apply(resolver: Resolver): void;
7070
}
71+
declare interface ParsedIdentifier {
72+
request: string;
73+
query: string;
74+
fragment: string;
75+
directory: boolean;
76+
module: boolean;
77+
file: boolean;
78+
}
7179
declare interface PnpApiImpl {
7280
resolveToUnqualified: (arg0: string, arg1: string, arg2?: any) => string;
7381
}
@@ -140,6 +148,7 @@ declare interface ResolveRequest {
140148
path: string | false;
141149
request?: undefined | string;
142150
query?: undefined | string;
151+
fragment?: undefined | string;
143152
directory?: undefined | boolean;
144153
module?: undefined | boolean;
145154
descriptionFilePath?: undefined | string;
@@ -199,7 +208,7 @@ declare abstract class Resolver {
199208
resolveContext: ResolveContext,
200209
callback: (
201210
arg0: null | Error,
202-
arg1: undefined | string,
211+
arg1: undefined | string | false,
203212
arg2: undefined | ResolveRequest
204213
) => void
205214
): void;
@@ -210,15 +219,7 @@ declare abstract class Resolver {
210219
resolveContext?: any,
211220
callback?: any
212221
): any;
213-
parse(
214-
identifier?: any
215-
): {
216-
request: string;
217-
query: string;
218-
module: boolean;
219-
directory: boolean;
220-
file: boolean;
221-
};
222+
parse(identifier: string): ParsedIdentifier;
222223
isModule(path?: any): boolean;
223224
isDirectory(path: string): boolean;
224225
join(path?: any, request?: any): string;

0 commit comments

Comments
 (0)