123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601 |
- "use strict";
- const { AsyncSeriesBailHook, AsyncSeriesHook, SyncHook } = require("tapable");
- const createInnerContext = require("./createInnerContext");
- const { parseIdentifier } = require("./util/identifier");
- const {
- normalize,
- cachedJoin: join,
- getType,
- PathType
- } = require("./util/path");
- function toCamelCase(str) {
- return str.replace(/-([a-z])/g, str => str.slice(1).toUpperCase());
- }
- class Resolver {
-
- static createStackEntry(hook, request) {
- return (
- hook.name +
- ": (" +
- request.path +
- ") " +
- (request.request || "") +
- (request.query || "") +
- (request.fragment || "") +
- (request.directory ? " directory" : "") +
- (request.module ? " module" : "")
- );
- }
-
- constructor(fileSystem, options) {
- this.fileSystem = fileSystem;
- this.options = options;
-
- this.hooks = {
- resolveStep: new SyncHook(["hook", "request"], "resolveStep"),
- noResolve: new SyncHook(["request", "error"], "noResolve"),
- resolve: new AsyncSeriesBailHook(
- ["request", "resolveContext"],
- "resolve"
- ),
- result: new AsyncSeriesHook(["result", "resolveContext"], "result")
- };
- }
-
- ensureHook(name) {
- if (typeof name !== "string") {
- return name;
- }
- name = toCamelCase(name);
- if (/^before/.test(name)) {
- return (
- this.ensureHook(name[6].toLowerCase() + name.slice(7)).withOptions({
- stage: -10
- })
- );
- }
- if (/^after/.test(name)) {
- return (
- this.ensureHook(name[5].toLowerCase() + name.slice(6)).withOptions({
- stage: 10
- })
- );
- }
-
- const hook = (this.hooks)[name];
- if (!hook) {
-
- (this.hooks)[name] = new AsyncSeriesBailHook(
- ["request", "resolveContext"],
- name
- );
- return (this.hooks)[name];
- }
- return hook;
- }
-
- getHook(name) {
- if (typeof name !== "string") {
- return name;
- }
- name = toCamelCase(name);
- if (/^before/.test(name)) {
- return (
- this.getHook(name[6].toLowerCase() + name.slice(7)).withOptions({
- stage: -10
- })
- );
- }
- if (/^after/.test(name)) {
- return (
- this.getHook(name[5].toLowerCase() + name.slice(6)).withOptions({
- stage: 10
- })
- );
- }
-
- const hook = (this.hooks)[name];
- if (!hook) {
- throw new Error(`Hook ${name} doesn't exist`);
- }
- return hook;
- }
-
- resolveSync(context, path, request) {
-
- let err = undefined;
-
- let result = undefined;
- let sync = false;
- this.resolve(context, path, request, {}, (e, r) => {
- err = e;
- result = r;
- sync = true;
- });
- if (!sync) {
- throw new Error(
- "Cannot 'resolveSync' because the fileSystem is not sync. Use 'resolve'!"
- );
- }
- if (err) throw err;
- if (result === undefined) throw new Error("No result");
- return result;
- }
-
- resolve(context, path, request, resolveContext, callback) {
- if (!context || typeof context !== "object")
- return callback(new Error("context argument is not an object"));
- if (typeof path !== "string")
- return callback(new Error("path argument is not a string"));
- if (typeof request !== "string")
- return callback(new Error("request argument is not a string"));
- if (!resolveContext)
- return callback(new Error("resolveContext argument is not set"));
-
- const obj = {
- context: context,
- path: path,
- request: request
- };
-
- let yield_;
- let yieldCalled = false;
-
- let finishYield;
- if (typeof resolveContext.yield === "function") {
- const old = resolveContext.yield;
-
- yield_ = obj => {
- old(obj);
- yieldCalled = true;
- };
-
- finishYield = result => {
- if (result) {
- (yield_)(result);
- }
- callback(null);
- };
- }
- const message = `resolve '${request}' in '${path}'`;
-
- const finishResolved = result => {
- return callback(
- null,
- result.path === false
- ? false
- : `${result.path.replace(/#/g, "\0#")}${
- result.query ? result.query.replace(/#/g, "\0#") : ""
- }${result.fragment || ""}`,
- result
- );
- };
-
- const finishWithoutResolve = log => {
-
- const error = new Error("Can't " + message);
- error.details = log.join("\n");
- this.hooks.noResolve.call(obj, error);
- return callback(error);
- };
- if (resolveContext.log) {
-
- const parentLog = resolveContext.log;
-
- const log = [];
- return this.doResolve(
- this.hooks.resolve,
- obj,
- message,
- {
- log: msg => {
- parentLog(msg);
- log.push(msg);
- },
- yield: yield_,
- fileDependencies: resolveContext.fileDependencies,
- contextDependencies: resolveContext.contextDependencies,
- missingDependencies: resolveContext.missingDependencies,
- stack: resolveContext.stack
- },
- (err, result) => {
- if (err) return callback(err);
- if (yieldCalled || (result && yield_)) {
- return (finishYield)(
- (result)
- );
- }
- if (result) return finishResolved(result);
- return finishWithoutResolve(log);
- }
- );
- } else {
-
-
- return this.doResolve(
- this.hooks.resolve,
- obj,
- message,
- {
- log: undefined,
- yield: yield_,
- fileDependencies: resolveContext.fileDependencies,
- contextDependencies: resolveContext.contextDependencies,
- missingDependencies: resolveContext.missingDependencies,
- stack: resolveContext.stack
- },
- (err, result) => {
- if (err) return callback(err);
- if (yieldCalled || (result && yield_)) {
- return (finishYield)(
- (result)
- );
- }
- if (result) return finishResolved(result);
-
-
-
-
-
- const log = [];
- return this.doResolve(
- this.hooks.resolve,
- obj,
- message,
- {
- log: msg => log.push(msg),
- yield: yield_,
- stack: resolveContext.stack
- },
- (err, result) => {
- if (err) return callback(err);
-
- if (yieldCalled || (result && yield_)) {
- return (finishYield)(
- (result)
- );
- }
- return finishWithoutResolve(log);
- }
- );
- }
- );
- }
- }
-
- doResolve(hook, request, message, resolveContext, callback) {
- const stackEntry = Resolver.createStackEntry(hook, request);
-
- let newStack;
- if (resolveContext.stack) {
- newStack = new Set(resolveContext.stack);
- if (resolveContext.stack.has(stackEntry)) {
-
- const recursionError = new Error(
- "Recursion in resolving\nStack:\n " +
- Array.from(newStack).join("\n ")
- );
- recursionError.recursion = true;
- if (resolveContext.log)
- resolveContext.log("abort resolving because of recursion");
- return callback(recursionError);
- }
- newStack.add(stackEntry);
- } else {
- newStack = new Set([stackEntry]);
- }
- this.hooks.resolveStep.call(hook, request);
- if (hook.isUsed()) {
- const innerContext = createInnerContext(
- {
- log: resolveContext.log,
- yield: resolveContext.yield,
- fileDependencies: resolveContext.fileDependencies,
- contextDependencies: resolveContext.contextDependencies,
- missingDependencies: resolveContext.missingDependencies,
- stack: newStack
- },
- message
- );
- return hook.callAsync(request, innerContext, (err, result) => {
- if (err) return callback(err);
- if (result) return callback(null, result);
- callback();
- });
- } else {
- callback();
- }
- }
-
- parse(identifier) {
- const part = {
- request: "",
- query: "",
- fragment: "",
- module: false,
- directory: false,
- file: false,
- internal: false
- };
- const parsedIdentifier = parseIdentifier(identifier);
- if (!parsedIdentifier) return part;
- [part.request, part.query, part.fragment] = parsedIdentifier;
- if (part.request.length > 0) {
- part.internal = this.isPrivate(identifier);
- part.module = this.isModule(part.request);
- part.directory = this.isDirectory(part.request);
- if (part.directory) {
- part.request = part.request.slice(0, -1);
- }
- }
- return part;
- }
-
- isModule(path) {
- return getType(path) === PathType.Normal;
- }
-
- isPrivate(path) {
- return getType(path) === PathType.Internal;
- }
-
- isDirectory(path) {
- return path.endsWith("/");
- }
-
- join(path, request) {
- return join(path, request);
- }
-
- normalize(path) {
- return normalize(path);
- }
- }
- module.exports = Resolver;
|