-
Notifications
You must be signed in to change notification settings - Fork 5.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[asset] Support fromModule in Snack web environment #14435
Conversation
@@ -37,7 +37,7 @@ function getScaledAssetPath(asset): string { | |||
const scale = AssetSourceResolver.pickScale(asset.scales, getScale()); | |||
const scaleSuffix = scale === 1 ? '' : '@' + scale + 'x'; | |||
const assetDir = getBasePath(asset); | |||
return assetDir + '/' + asset.name + scaleSuffix + '.' + asset.type; | |||
return assetDir + '/' + asset.name + scaleSuffix + (asset.type ? `.${asset.type}` : ''); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is needed to support Snack assets served from S3 (which don't have an extension)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so a trailing .
crashes it? Or is the issue that we are trying to deal with some strange handling of null
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not crashing it, but it produces the wrong url. asset.type is optional and when omitted this code produces the wrong url-suffix: .undefined
for reference, we've also been patching react-native-web
(for as long as I can remember) in snack to support this:
https://github.com/expo/snack/blob/main/runtime/patches/react-native-web%2B0.13.18.patch#L10
function _coerceLocalScriptURL(scriptURL: string | undefined | null): string | null { | ||
if (scriptURL) { | ||
if (scriptURL.startsWith('assets://')) { | ||
// android: running from within assets, no offline path to use | ||
return null; | ||
} | ||
scriptURL = scriptURL.substring(0, scriptURL.lastIndexOf('/') + 1); | ||
if (!scriptURL.includes('://')) { | ||
// Add file protocol in case we have an absolute file path and not a URL. | ||
// This shouldn't really be necessary. scriptURL should be a URL. | ||
scriptURL = 'file://' + scriptURL; | ||
} | ||
} | ||
return null; | ||
} | ||
|
||
function getScriptURL(): string | null { | ||
if (_scriptURL === undefined) { | ||
_scriptURL = _coerceLocalScriptURL(getSourceCodeScriptURL()); | ||
} | ||
return _scriptURL; | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
_coerceLocalScriptURL
always returns null
causing getScriptURL
to also always return null.
@EvanBacon Do you know whether this was ever intentional?
@@ -84,7 +84,7 @@ export default class AssetSourceResolver { | |||
return this.fromSource(getScaledAssetPath(this.asset)); | |||
} | |||
scaledAssetURLNearBundle(): ResolvedAssetSource { | |||
const path = this.jsbundleUrl || 'file://'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Prepending file://
to the web asset path actually breaks it. This is because getScaledAssetPath
returns the httpServerLocation
which already has a protocol prefix
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, but I'm also not super familiar with the web stuff.
let _sourceCodeScriptURL: string | undefined | null; | ||
|
||
function getSourceCodeScriptURL(): string | undefined | null { | ||
if (_sourceCodeScriptURL) { | ||
return _sourceCodeScriptURL; | ||
} | ||
|
||
let sourceCode = nativeExtensions && nativeExtensions.SourceCode; | ||
let sourceCode = typeof nativeExtensions !== 'undefined' ? nativeExtensions.SourceCode : null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let sourceCode = typeof nativeExtensions !== 'undefined' ? nativeExtensions.SourceCode : null; | |
let sourceCode = nativeExtensions?.SourceCode ?? null; |
or did you want an undefined
to leak through if SourceCode
was undefined
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The suggestion would break stuff as nativeExtensions
is not a declared global in the Snack environment. The PR makes the change to not depend on this nativeExtensions
global
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand the point about "declared global"?
seems to make logical sense and to pass ts analysis in vscode. I also read through some documentation: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html
Can you explain this typescript danger you are seeing a bit more so I can learn to not make the mistake in the future?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On native, nativeExtensions
is injected by react-native into the global namespace. On web this global is however not available and produces the following runtime error, when accessed:
resolveAssetSource.web.ts:18 Uncaught (in promise) ReferenceError: nativeExtensions is not defined
at u (resolveAssetSource.web.ts:18)
at resolveAssetSource.web.ts:28
...
When using nativeExtensions?.SourceCode
, it would still try to access the global nativeExtensions
variable and produce the runtime error. By using typeof nativeExtensions !== 'undefined'
you can check whether the global exists before accessing it. Another way of doing that is by using global.nativeExtensions
to check whether the global exists.
This is the same kind of error that you get when for instance accessing the window
global from your server rendered page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Anyway, many thanks for the review @jkhales 👍 👍
} | ||
_sourceCodeScriptURL = sourceCode.scriptURL; | ||
_sourceCodeScriptURL = sourceCode?.scriptURL; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
_sourceCodeScriptURL = sourceCode?.scriptURL; | |
return (nativeExtensions?.SourceCode ?? NativeModules?.SourceCode)?.scriptURL |
Would let you get rid of 5 lines
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would produce the same runtime error and also no longer cache the _sourceCodeScriptURL
for subsequent calls
eeda53f
to
c7d9eec
Compare
* [asset] Fix fromModule exception on web * [asset] Support Snack web assets * [asset] Support Snack web assets * update build files * fix * update changelog * Add nativeExtension typing * Update build files
These changes ship with expo-asset by default now expo/expo#14435
* [runtime] Upgrade to Expo SDK 43 stable * [runtime] Remove expo-asset patch These changes ship with expo-asset by default now expo/expo#14435 * [runtime] Update web customization files with cli * [snack-sdk] Remove bundled native modules for SDK 39 * [snack-sdk] Update bundled native modules for sdk 43 * [snack-sdk] Drop Expo SDK 39 from sdk * [snack-sdk] Update tests and snapshots * [snack-sdk] Add changelog message about sdk 43 upgrade * [website] Update the visible SDK versions * [website] Update tests and snapshots * [snackager] Update tests and snapshots * [snackager] Set bundler test timeout to 2 minutes * [ci] Force integration tests to run in band without blocking fast tests
# Why - fix expo/router#131 by using the correct web base URL, previously was failing to resolve the origin using a number of naive checks. - drop all incorrect offline checks for web assets - utilize the newer @react-native/assets/registry package - reuse upstream types - fix file system type error - Possibly breaks web snack support introduced here #14435 - ~~Also might break some assumptions about Expo Webpack, will need to do some testing.~~ Webpack just ignores all of this code. <!-- Please describe the motivation for this PR, and link to relevant GitHub issues, forums posts, or feature requests. --> # How <!-- How did you build this feature or fix this bug and why? --> # Test Plan <!-- Please describe how you tested this change and how a reviewer could reproduce your test, especially if this PR does not include automated tests! If possible, please also provide terminal output and/or screenshots demonstrating your test/reproduction. --> # Checklist <!-- Please check the appropriate items below if they apply to your diff. This is required for changes to Expo modules. --> - [ ] Documentation is up to date to reflect these changes (eg: https://docs.expo.dev and README.md). - [ ] Conforms with the [Documentation Writing Style Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md) - [ ] This diff will work correctly for `expo prebuild` & EAS Build (eg: updated a module plugin). Co-authored-by: Expo Bot <34669131+expo-bot@users.noreply.github.com>
Why
See expo/snack#213
The
Asset.fromModule
method fails in the Snack environment due to various reasons. This PR makes some straightforward fixes toAsset.fromModule
and adds some changes to support the Snack environment.This is important to support examples such as
ImageManipulator
and all other examples that depend onAsset.fromModule
on web.How
nativeExtensions
var before using it (this is not available in the Snack env)getScriptURL
and_coerceLocalScriptURL
which always returnednull
file://
Test Plan