| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 4b5ab0a commit 92c2654
6 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,5 @@ | |||
| 1 | + // Performs a "../" relative dynamic import reaching up one directory. | ||
| 2 | + export async function loadParentSibling() { | ||
| 3 | + const sibling = await import("../sibling.mjs"); | ||
| 4 | + return sibling.value; | ||
| 5 | + } | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,7 @@ | |||
| 1 | + // Performs a relative dynamic import of a sibling in the same subdirectory. | ||
| 2 | + // The specifier "./sibling.mjs" must resolve against this module's directory, | ||
| 3 | + // not the application root. | ||
| 4 | + export async function loadSibling() { | ||
| 5 | + const sibling = await import("./sibling.mjs"); | ||
| 6 | + return sibling.value; | ||
| 7 | + } | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,2 @@ | |||
| 1 | + // Sibling module reached via a relative dynamic import from the same directory. | ||
| 2 | + export const value = "sibling-loaded"; | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,6 @@ | |||
| 1 | + // Control: a relative dynamic import from an app-root module, where the | ||
| 2 | + // referrer's directory is the application root. Must keep resolving. | ||
| 3 | + export async function loadRootSibling() { | ||
| 4 | + const mod = await import("./testSimpleESModule.mjs"); | ||
| 5 | + return mod.moduleType; | ||
| 6 | + } | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -31,4 +31,34 @@ describe("ES Modules", () => { | |||
| 31 | 31 | expect(workerResults.urlObjectSupported).toBe(true); | |
| 32 | 32 | expect(workerResults.tildePathSupported).toBe(true); | |
| 33 | 33 | }); | |
| 34 | + | ||
| 35 | + // These use the done-callback form: this Jasmine version only awaits a spec | ||
| 36 | + // when its function declares an argument (an async function returning a | ||
| 37 | + // promise is run synchronously and its result ignored). | ||
| 38 | + it("resolves a relative dynamic import from a subdirectory module", (done) => { | ||
| 39 | + import("~/esm-subdir/parent.mjs") | ||
| 40 | + .then((parent) => parent.loadSibling()) | ||
| 41 | + .then( | ||
| 42 | + (value) => { expect(value).toBe("sibling-loaded"); done(); }, | ||
| 43 | + (err) => { expect(err).toBeUndefined(); done(); } | ||
| 44 | + ); | ||
| 45 | + }); | ||
| 46 | + | ||
| 47 | + it("resolves a '../' relative dynamic import from a nested module", (done) => { | ||
| 48 | + import("~/esm-subdir/nested/child.mjs") | ||
| 49 | + .then((child) => child.loadParentSibling()) | ||
| 50 | + .then( | ||
| 51 | + (value) => { expect(value).toBe("sibling-loaded"); done(); }, | ||
| 52 | + (err) => { expect(err).toBeUndefined(); done(); } | ||
| 53 | + ); | ||
| 54 | + }); | ||
| 55 | + | ||
| 56 | + it("still resolves a relative dynamic import from an app-root module", (done) => { | ||
| 57 | + import("~/testRelativeDynamicImport.mjs") | ||
| 58 | + .then((root) => root.loadRootSibling()) | ||
| 59 | + .then( | ||
| 60 | + (value) => { expect(value).toBe("ES Module"); done(); }, | ||
| 61 | + (err) => { expect(err).toBeUndefined(); done(); } | ||
| 62 | + ); | ||
| 63 | + }); | ||
| 34 | 64 | }); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -98,6 +98,32 @@ static void LogHttpCompileDiagnostics(v8::Isolate* isolate, | |||
| 98 | 98 | snippet.c_str()); | |
| 99 | 99 | } | |
| 100 | 100 | ||
| 101 | + // Helper: collapse "." and ".." path segments, preserving a leading "/". | ||
| 102 | + static std::string NormalizeDotSegments(const std::string& path) { | ||
| 103 | + std::vector<std::string> stack; | ||
| 104 | + bool absolute = !path.empty() && path[0] == '/'; | ||
| 105 | + size_t i = 0; | ||
| 106 | + while (i <= path.size()) { | ||
| 107 | + size_t j = path.find('/', i); | ||
| 108 | + std::string seg = (j == std::string::npos) ? path.substr(i) : path.substr(i, j - i); | ||
| 109 | + if (seg.empty() || seg == ".") { | ||
| 110 | + // skip | ||
| 111 | + } else if (seg == "..") { | ||
| 112 | + if (!stack.empty()) stack.pop_back(); | ||
| 113 | + } else { | ||
| 114 | + stack.push_back(seg); | ||
| 115 | + } | ||
| 116 | + if (j == std::string::npos) break; | ||
| 117 | + i = j + 1; | ||
| 118 | + } | ||
| 119 | + std::string norm = absolute ? "/" : std::string(); | ||
| 120 | + for (size_t k = 0; k < stack.size(); k++) { | ||
| 121 | + if (k > 0) norm += "/"; | ||
| 122 | + norm += stack[k]; | ||
| 123 | + } | ||
| 124 | + return norm; | ||
| 125 | + } | ||
| 126 | + | ||
| 101 | 127 | // Helper: resolve relative or root-absolute spec against an HTTP(S) referrer URL. | |
| 102 | 128 | // Returns empty string if resolution is not possible. | |
| 103 | 129 | static std::string ResolveHttpRelative(const std::string& referrerUrl, const std::string& spec) { | |
@@ -158,28 +184,30 @@ static std::string ResolveHttpRelative(const std::string& referrerUrl, const std | |||
| 158 | 184 | } | |
| 159 | 185 | ||
| 160 | 186 | // Normalize "." and ".." segments | |
| 161 | - std::vector<std::string> stack; | ||
| 162 | - bool absolute = !newPath.empty() && newPath[0] == '/'; | ||
| 163 | - size_t i = 0; | ||
| 164 | - while (i <= newPath.size()) { | ||
| 165 | - size_t j = newPath.find('/', i); | ||
| 166 | - std::string seg = (j == std::string::npos) ? newPath.substr(i) : newPath.substr(i, j - i); | ||
| 167 | - if (seg.empty() || seg == ".") { | ||
| 168 | - // skip | ||
| 169 | - } else if (seg == "..") { | ||
| 170 | - if (!stack.empty()) stack.pop_back(); | ||
| 171 | - } else { | ||
| 172 | - stack.push_back(seg); | ||
| 173 | - } | ||
| 174 | - if (j == std::string::npos) break; | ||
| 175 | - i = j + 1; | ||
| 187 | + std::string normPath = NormalizeDotSegments(newPath); | ||
| 188 | + return origin + normPath + specSuffix; | ||
| 189 | + } | ||
| 190 | + | ||
| 191 | + // Helper: resolve a relative "./" or "../" specifier against a file:// referrer | ||
| 192 | + // URL, returning an absolute file:// URL. Returns empty if not applicable. | ||
| 193 | + static std::string ResolveFileRelative(const std::string& referrerUrl, const std::string& spec) { | ||
| 194 | + const std::string filePrefix = "file://"; | ||
| 195 | + if (referrerUrl.rfind(filePrefix, 0) != 0) { | ||
| 196 | + return std::string(); | ||
| 176 | 197 | } | |
| 177 | - std::string normPath = absolute ? "/" : std::string(); | ||
| 178 | - for (size_t k = 0; k < stack.size(); k++) { | ||
| 179 | - if (k > 0) normPath += "/"; | ||
| 180 | - normPath += stack[k]; | ||
| 198 | + if (spec.empty() || spec[0] != '.') { | ||
| 199 | + return std::string(); | ||
| 181 | 200 | } | |
| 182 | - return origin + normPath + specSuffix; | ||
| 201 | + // Referrer path: strip scheme, drop query and fragment | ||
| 202 | + std::string refPath = referrerUrl.substr(filePrefix.size()); | ||
| 203 | + size_t hashPos = refPath.find('#'); | ||
| 204 | + if (hashPos != std::string::npos) refPath = refPath.substr(0, hashPos); | ||
| 205 | + size_t qPos = refPath.find('?'); | ||
| 206 | + if (qPos != std::string::npos) refPath = refPath.substr(0, qPos); | ||
| 207 | + | ||
| 208 | + size_t lastSlash = refPath.find_last_of('/'); | ||
| 209 | + std::string baseDir = (lastSlash == std::string::npos) ? std::string("/") : refPath.substr(0, lastSlash + 1); | ||
| 210 | + return filePrefix + NormalizeDotSegments(baseDir + spec); | ||
| 183 | 211 | } | |
| 184 | 212 | ||
| 185 | 213 | // Import meta callback to support import.meta.url and import.meta.dirname | |
@@ -928,12 +956,28 @@ v8::MaybeLocal<v8::Promise> ImportModuleDynamicallyCallback( | |||
| 928 | 956 | ||
| 929 | 957 | // Re-use the static resolver to locate / compile the module for non-HTTP cases. | |
| 930 | 958 | try { | |
| 931 | - // Pass empty referrer since this V8 version doesn't expose GetModule() on | ||
| 932 | - // ScriptOrModule. The resolver will fall back to absolute-path heuristics. | ||
| 959 | + // V8 exposes only the referrer's URL here (resource_name), not its Module, | ||
| 960 | + // so anchor a relative specifier at the referrer's directory and hand the | ||
| 961 | + // resolver an absolute file:// URL. Other specifiers pass through unchanged | ||
| 962 | + // (the resolver applies its own ~/, bare and absolute heuristics). | ||
| 963 | + v8::Local<v8::String> resolvedSpecifier = specifier; | ||
| 964 | + if (specIsRelative) { | ||
| 965 | + std::string fileResolved = ResolveFileRelative(referrerUrl, spec); | ||
| 966 | + if (!fileResolved.empty()) { | ||
| 967 | + resolvedSpecifier = ArgConverter::ConvertToV8String(isolate, fileResolved); | ||
| 968 | + if (IsScriptLoadingLogEnabled()) { | ||
| 969 | + DEBUG_WRITE("[esm][dyn][file-rel] base=%s spec=%s -> %s", | ||
| 970 | + referrerUrl.c_str(), spec.c_str(), fileResolved.c_str()); | ||
| 971 | + } | ||
| 972 | + } | ||
| 973 | + } | ||
| 974 | + | ||
| 975 | + // Pass empty referrer: this V8 version does not expose GetModule() on | ||
| 976 | + // ScriptOrModule, and the specifier above is already absolute when needed. | ||
| 933 | 977 | v8::Local<v8::Module> refMod; | |
| 934 | 978 | ||
| 935 | 979 | v8::MaybeLocal<v8::Module> maybeModule = | |
| 936 | - ResolveModuleCallback(context, specifier, import_assertions, refMod); | ||
| 980 | + ResolveModuleCallback(context, resolvedSpecifier, import_assertions, refMod); | ||
| 937 | 981 | ||
| 938 | 982 | v8::Local<v8::Module> module; | |
| 939 | 983 | if (!maybeModule.ToLocal(&module)) { | |
| Back | FazBrowse Home | New Git URL |
0 commit comments