|
22 | 22 |
|
23 | 23 | namespace tns { |
24 | 24 |
|
25 | | -// require()-path policy only: import() rejects a missing bare specifier outright |
26 | | -// (ESM optionality is `try { await import(x) } catch {}` at the call site, and in |
27 | | -// dev sessions a bare specifier the import map doesn't cover is a config bug that |
28 | | -// must fail loudly — see docs/knowledge/hmr-simplification-pass.md §2). |
29 | | -static bool IsLikelyOptionalModule(const std::string& moduleName) { |
30 | | - // Node built-ins are handled by their own dedicated resolution path; never treat them as |
31 | | - // an optional external module. |
32 | | - if (moduleName.rfind("node:", 0) == 0) { |
33 | | - return false; |
34 | | - } |
35 | | - |
36 | | - // Check if it's a bare module name (no path separators) that could be an npm package. |
37 | | - // |
38 | | - // Bare specifiers that end in a recognizable script/data extension (e.g. "foo.js", |
39 | | - // "config.json") are explicit file references, not npm-style package names — real npm |
40 | | - // package names don't carry a file extension. Treating them as "likely optional" would |
41 | | - // swallow a genuine "module not found" failure behind a lazily-throwing placeholder |
42 | | - // instead of letting require()/import() fail immediately, which is what callers (and the |
43 | | - // existing "should throw error if cant find node module" test) expect for those names. |
44 | | - // |
45 | | - // This carve-out is deliberately narrow: a dotted bare name that doesn't end in one of |
46 | | - // these exact extensions (e.g. "lodash.debounce") is still treated as optional, same as |
47 | | - // before. See ModuleInternal.mm/ModuleInternalCallbacks.mm optional-module tests for the |
48 | | - // cases this boundary is expected to hold for. |
49 | | - static const char* kExplicitFileExtensions[] = {".js", ".mjs", ".cjs", ".json", ".node", ".ts"}; |
50 | | - |
51 | | - if (moduleName.find('/') == std::string::npos && moduleName.find('\\') == std::string::npos && |
52 | | - moduleName[0] != '.' && moduleName[0] != '~' && moduleName[0] != '/') { |
53 | | - for (const char* ext : kExplicitFileExtensions) { |
54 | | - size_t extLen = strlen(ext); |
55 | | - if (moduleName.size() > extLen && |
56 | | - moduleName.compare(moduleName.size() - extLen, extLen, ext) == 0) { |
57 | | - return false; |
58 | | - } |
59 | | - } |
60 | | - return true; |
61 | | - } |
62 | | - return false; |
63 | | -} |
64 | | - |
65 | 25 | // Helper function to check if a file path is an ES module (.mjs) but not a source map (.mjs.map) |
66 | 26 | bool IsESModule(const std::string& path) { |
67 | 27 | return path.size() >= 4 && path.compare(path.size() - 4, 4, ".mjs") == 0 && |
@@ -624,14 +584,6 @@ static inline void SetOutErrorMessage(std::string* outErrorMessage, const std::s |
624 | 584 | } |
625 | 585 |
|
626 | 586 | if (path.empty()) { |
627 | | - // A bare specifier shaped like an npm package name resolves to a |
628 | | - // lazily-throwing placeholder, so an app can ship without an optional |
629 | | - // dependency installed and only fail if it actually touches it. A |
630 | | - // specifier resolved against "/" is an explicit absolute path, never an |
631 | | - // optional package, so it always hard-fails. |
632 | | - if (baseDir != "/" && IsLikelyOptionalModule(moduleName)) { |
633 | | - return this->CreatePlaceholderModule(isolate, moduleName, cacheKey); |
634 | | - } |
635 | 587 | throw NativeScriptException(isolate, "Cannot find module '" + moduleName + "'", "Error"); |
636 | 588 | } |
637 | 589 |
|
@@ -1615,12 +1567,6 @@ throw NativeScriptException( |
1615 | 1567 | } |
1616 | 1568 |
|
1617 | 1569 | if (exists == NO) { |
1618 | | - // No path for an optional-looking package: LoadImpl turns the empty result |
1619 | | - // into the placeholder module rather than a hard failure. |
1620 | | - if (IsLikelyOptionalModule(moduleName)) { |
1621 | | - return std::string(); |
1622 | | - } |
1623 | | - |
1624 | 1570 | // Create a detailed error message with context |
1625 | 1571 | std::string errorMsg = "Cannot find module '" + moduleName + "'"; |
1626 | 1572 | errorMsg += "\n Base directory: " + baseDir; |
@@ -1724,94 +1670,6 @@ throw NativeScriptException( |
1724 | 1670 | return std::string([[basePath stringByAppendingPathExtension:@"js"] UTF8String]); |
1725 | 1671 | } |
1726 | 1672 |
|
1727 | | -Local<Object> ModuleInternal::CreatePlaceholderModule(Isolate* isolate, |
1728 | | - const std::string& moduleName, |
1729 | | - const std::string& cacheKey) { |
1730 | | - Local<Context> context = isolate->GetCurrentContext(); |
1731 | | - |
1732 | | - // Create a module object with exports that throws when accessed |
1733 | | - Local<Object> moduleObj = Object::New(isolate); |
1734 | | - |
1735 | | - // Create a Proxy that throws an error when any property is accessed. |
1736 | | - // |
1737 | | - // The message is passed to a constant factory script as a real V8 string |
1738 | | - // (never interpolated into the script source). Interpolation is a JS |
1739 | | - // injection hazard: the message itself contains single quotes |
1740 | | - // ("Module 'zip' is not available...") which terminated the string literal |
1741 | | - // early and made the generated script a guaranteed SyntaxError |
1742 | | - // ("missing ) after argument list") that propagated out of require() with |
1743 | | - // no hint of its origin. |
1744 | | - std::string errorMessage = |
1745 | | - "Module '" + moduleName + "' is not available. This is an optional module."; |
1746 | | - static const char* kProxyFactorySource = "(function(msg) {" |
1747 | | - " const error = new Error(msg);" |
1748 | | - " return new Proxy({}, {" |
1749 | | - " get: function(target, prop) {" |
1750 | | - " throw error;" |
1751 | | - " }," |
1752 | | - " set: function(target, prop, value) {" |
1753 | | - " throw error;" |
1754 | | - " }," |
1755 | | - " has: function(target, prop) {" |
1756 | | - " return false;" |
1757 | | - " }," |
1758 | | - " ownKeys: function(target) {" |
1759 | | - " return [];" |
1760 | | - " }," |
1761 | | - " getPrototypeOf: function(target) {" |
1762 | | - " return null;" |
1763 | | - " }" |
1764 | | - " });" |
1765 | | - "})"; |
1766 | | - |
1767 | | - TryCatch tc(isolate); |
1768 | | - Local<Script> proxyScript; |
1769 | | - if (Script::Compile(context, tns::ToV8String(isolate, kProxyFactorySource)) |
1770 | | - .ToLocal(&proxyScript)) { |
1771 | | - Local<Value> factoryValue; |
1772 | | - if (proxyScript->Run(context).ToLocal(&factoryValue) && factoryValue->IsFunction()) { |
1773 | | - Local<Value> args[]{tns::ToV8String(isolate, errorMessage.c_str())}; |
1774 | | - Local<Value> proxyObject; |
1775 | | - if (factoryValue.As<v8::Function>() |
1776 | | - ->Call(context, v8::Undefined(isolate), 1, args) |
1777 | | - .ToLocal(&proxyObject)) { |
1778 | | - // Set the exports to the proxy object |
1779 | | - bool success = moduleObj->Set(context, tns::ToV8String(isolate, "exports"), proxyObject) |
1780 | | - .FromMaybe(false); |
1781 | | - if (!success) { |
1782 | | - Log(@"Warning: Failed to set exports property on proxy module object"); |
1783 | | - } |
1784 | | - } |
1785 | | - } |
1786 | | - } |
1787 | | - if (tc.HasCaught()) { |
1788 | | - // The placeholder is best-effort. Never let its construction machinery |
1789 | | - // leak an exception that masks the real "module not found" condition. |
1790 | | - Log(@"Warning: placeholder module construction failed for %s", moduleName.c_str()); |
1791 | | - } |
1792 | | - |
1793 | | - // Set up the module object |
1794 | | - bool success = moduleObj |
1795 | | - ->Set(context, tns::ToV8String(isolate, "id"), |
1796 | | - tns::ToV8String(isolate, moduleName.c_str())) |
1797 | | - .FromMaybe(false); |
1798 | | - if (!success) { |
1799 | | - Log(@"Warning: Failed to set id property on module object"); |
1800 | | - } |
1801 | | - |
1802 | | - success = |
1803 | | - moduleObj->Set(context, tns::ToV8String(isolate, "loaded"), v8::Boolean::New(isolate, true)) |
1804 | | - .FromMaybe(false); |
1805 | | - if (!success) { |
1806 | | - Log(@"Warning: Failed to set loaded property on module object"); |
1807 | | - } |
1808 | | - |
1809 | | - // Cache the placeholder module |
1810 | | - this->loadedModules_[cacheKey] = std::make_shared<Persistent<Object>>(isolate, moduleObj); |
1811 | | - |
1812 | | - return moduleObj; |
1813 | | -} |
1814 | | - |
1815 | 1673 | ScriptCompiler::CachedData* ModuleInternal::LoadScriptCache(const std::string& path) { |
1816 | 1674 | std::string canonicalPath = NormalizePath(path); |
1817 | 1675 | if (RuntimeConfig.IsDebug) { |
|
0 commit comments