From 38d0fb685931238488b5138fa6577321e4c6ba10 Mon Sep 17 00:00:00 2001 From: Andrew Coates <30809111+acoates-ms@users.noreply.github.com> Date: Thu, 10 Aug 2023 15:55:49 -0700 Subject: [PATCH 1/2] [Win32] Reduce usage of long paths in assets which can cause long path issues (#11839) * Add saveAssetPlugin to fix long path assets * Change files * comment * add back compat * fix * turn off win32 assetPlugin for now * fix * fix * fix * Change files * fix --- ...-43b80e6f-96e4-479c-b4d7-46c1fb056647.json | 7 +++ .../react-native-win32/metro.config.js | 2 + .../metroShortPathAssetDataPlugin.js | 15 +++++++ .../react-native-win32/overrides.json | 10 +++++ .../react-native-win32/react-native.config.js | 2 + .../react-native-win32/saveAssetPlugin.js | 45 +++++++++++++++++++ .../src/Libraries/Image/assetPaths.js | 36 +++++++++++++++ .../Image/resolveAssetSource.win32.js | 23 +++++++--- 8 files changed, 135 insertions(+), 5 deletions(-) create mode 100644 change/@office-iss-react-native-win32-43b80e6f-96e4-479c-b4d7-46c1fb056647.json create mode 100644 packages/@office-iss/react-native-win32/metroShortPathAssetDataPlugin.js create mode 100644 packages/@office-iss/react-native-win32/saveAssetPlugin.js create mode 100644 packages/@office-iss/react-native-win32/src/Libraries/Image/assetPaths.js diff --git a/change/@office-iss-react-native-win32-43b80e6f-96e4-479c-b4d7-46c1fb056647.json b/change/@office-iss-react-native-win32-43b80e6f-96e4-479c-b4d7-46c1fb056647.json new file mode 100644 index 00000000000..73373eff8c9 --- /dev/null +++ b/change/@office-iss-react-native-win32-43b80e6f-96e4-479c-b4d7-46c1fb056647.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "Add saveAssetPlugin to fix long path assets", + "packageName": "@office-iss/react-native-win32", + "email": "30809111+acoates-ms@users.noreply.github.com", + "dependentChangeType": "patch" +} diff --git a/packages/@office-iss/react-native-win32/metro.config.js b/packages/@office-iss/react-native-win32/metro.config.js index 3cbf6a97b3d..c27321c5ad2 100644 --- a/packages/@office-iss/react-native-win32/metro.config.js +++ b/packages/@office-iss/react-native-win32/metro.config.js @@ -14,3 +14,5 @@ if ( const {makeMetroConfig} = require('@rnw-scripts/metro-dev-config'); module.exports = makeMetroConfig(); +// Enable this when RN CLI gets support for saveAssetPlugins: https://github.com/react-native-community/cli/pull/2002 +// module.exports.transformer.assetPlugins = [require.resolve('./metroShortPathAssetDataPlugin.js')]; diff --git a/packages/@office-iss/react-native-win32/metroShortPathAssetDataPlugin.js b/packages/@office-iss/react-native-win32/metroShortPathAssetDataPlugin.js new file mode 100644 index 00000000000..8e0f9fe19f5 --- /dev/null +++ b/packages/@office-iss/react-native-win32/metroShortPathAssetDataPlugin.js @@ -0,0 +1,15 @@ +// @ts-check +/** + * @typedef {import("metro").AssetData} AssetData; + **/ + +/** + * @param {AssetData & {__useShortPath: boolean}} asset + * @returns {Promise} + */ +async function metroShortPathAssetDataPlugin(asset) { + asset.__useShortPath = true; + return Promise.resolve(asset); + } + +module.exports = metroShortPathAssetDataPlugin; \ No newline at end of file diff --git a/packages/@office-iss/react-native-win32/overrides.json b/packages/@office-iss/react-native-win32/overrides.json index ce95ec3fd4a..0168eeb974b 100644 --- a/packages/@office-iss/react-native-win32/overrides.json +++ b/packages/@office-iss/react-native-win32/overrides.json @@ -221,6 +221,16 @@ "baseHash": "37908d71eb9a61c619f700795ff2d692410c5a57", "issue": 5170 }, + { + "type": "copy", + "file": "src/Libraries/DevToolsSettings/DevToolsSettingsManager.win32.js", + "baseFile": "packages/react-native/Libraries/DevToolsSettings/DevToolsSettingsManager.android.js", + "baseHash": "1c9eb481e8ed077fa650e3ea34837e2a31310366" + }, + { + "type": "platform", + "file": "src/Libraries/Image/assetPaths.js" + }, { "type": "derived", "file": "src/Libraries/Image/Image.win32.js", diff --git a/packages/@office-iss/react-native-win32/react-native.config.js b/packages/@office-iss/react-native-win32/react-native.config.js index dacf5861050..6d3aeb41517 100644 --- a/packages/@office-iss/react-native-win32/react-native.config.js +++ b/packages/@office-iss/react-native-win32/react-native.config.js @@ -7,6 +7,8 @@ module.exports = { projectConfig: (projectRoot, projectParams) => null, dependencyConfig: (projectRoot, dependencyParams) => null, npmPackageName: '@office-iss/react-native-win32', + // Enable once CLI config supports it - https://github.com/react-native-community/cli/pull/2002 + // saveAssetsPlugin: '@office-iss/react-native-win32/saveAssetPlugin' }, }, }; diff --git a/packages/@office-iss/react-native-win32/saveAssetPlugin.js b/packages/@office-iss/react-native-win32/saveAssetPlugin.js new file mode 100644 index 00000000000..c559cdd21a2 --- /dev/null +++ b/packages/@office-iss/react-native-win32/saveAssetPlugin.js @@ -0,0 +1,45 @@ +// @ts-check +const path = require('path'); +const ensureShortPath = require('./Libraries/Image/assetPaths'); + +/** + * @typedef {import("metro").AssetData} AssetData; + **/ + +/** + * @param {AssetData} asset + * @param {number} scale + * @returns {string} + */ +function getAssetDestPath(asset, scale) { + const suffix = scale === 1 ? '' : `@${scale}x`; + const fileName = `${asset.name + suffix}.${asset.type}`; + return path.join( + // Assets can have relative paths outside of the project root. + // Replace `../` with `_` to make sure they don't end up outside of + // the expected assets directory. + ensureShortPath(asset.httpServerLocation.substr(1).replace(/\.\.\//g, '_')), + fileName, + ); +} + +/** + * @param {ReadonlyArray} assets + * @param {string} _platform + * @param {string | undefined} _assetsDest + * @param {string | undefined} _assetCatalogDest + * @param {(asset: AssetData, allowedScales: number[] | undefined, getAssetDestPath: (asset: AssetData, scale: number) => string) => void} addAssetToCopy + */ +function saveAssetsWin32( + assets, + _platform, + _assetsDest, + _assetCatalogDest, + addAssetToCopy, +) { + assets.forEach((asset) => + addAssetToCopy(asset, undefined, getAssetDestPath), + ); +} + +module.exports = saveAssetsWin32; \ No newline at end of file diff --git a/packages/@office-iss/react-native-win32/src/Libraries/Image/assetPaths.js b/packages/@office-iss/react-native-win32/src/Libraries/Image/assetPaths.js new file mode 100644 index 00000000000..101f3b25af0 --- /dev/null +++ b/packages/@office-iss/react-native-win32/src/Libraries/Image/assetPaths.js @@ -0,0 +1,36 @@ +/** + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT License. + * + * @flow strict-local + * @format + */ + +'use strict'; + +// Some windows machines may not have long paths enabled +// https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation +// Assets in nested node_modules (common when using pnpm) - end up creating very long paths +// Using this function we shorten longer paths to prevent paths from hitting the path limit +function ensureShortPath(str: string): string { + if (str.length < 40) return str; + + const assetsPrefix = 'assets/'; + + if (!str.startsWith(assetsPrefix)) { + console.warn(`Unexpected asset uri - ${str} may not load correctly.`); + } + + const postStr = str.slice(assetsPrefix.length); + var hash = 0, + i, + chr; + for (i = 0; i < postStr.length; i++) { + chr = postStr.charCodeAt(i); + hash = (hash << 5) - hash + chr; + hash |= 0; // Convert to 32bit integer + } + return assetsPrefix + hash.toString(); +} + +module.exports = ensureShortPath; diff --git a/packages/@office-iss/react-native-win32/src/Libraries/Image/resolveAssetSource.win32.js b/packages/@office-iss/react-native-win32/src/Libraries/Image/resolveAssetSource.win32.js index f6e29548395..00dcd56cb70 100644 --- a/packages/@office-iss/react-native-win32/src/Libraries/Image/resolveAssetSource.win32.js +++ b/packages/@office-iss/react-native-win32/src/Libraries/Image/resolveAssetSource.win32.js @@ -10,6 +10,7 @@ const resolveAssetSource = require('./resolveAssetSource.js'); // Get base impl const Platform = require('../Utilities/Platform'); +const ensureShortPath = require('./assetPaths.js'); type IPackagerAsset = { __packager_asset: boolean, @@ -56,7 +57,7 @@ class AssetResolverLateScaleResolution { */ _scaledAssetURLInBundle() { const path = this._resolver.bundleUrl || 'file://'; - return this._fromSource(path + this._getAssetPath()); + return this._fromSource(path + this._getAssetPath(true)); } /** @@ -66,7 +67,7 @@ class AssetResolverLateScaleResolution { _assetServerURL() { return this._fromSource( this._resolver.serverUrl + - this._getAssetPath() + + this._getAssetPath(false) + '?platform=' + Platform.OS + '&hash=' + @@ -77,8 +78,8 @@ class AssetResolverLateScaleResolution { /** * Returns a path like 'assets/AwesomeModule/icon.png' */ - _getAssetPath(): string { - const assetDir = this._getBasePath(); + _getAssetPath(local: boolean): string { + const assetDir = this._getBasePath(local); return ( assetDir + '/' + @@ -88,7 +89,19 @@ class AssetResolverLateScaleResolution { ); } - _getBasePath() { + _getBasePath(local: boolean) { + if (local) { + const safePath = this._resolver.asset.httpServerLocation + .substr(1) + .replace(/\.\.\//g, '_'); + // If this asset was created with the newer saveAssetPlugin, then we should shorten the path + // This conditional is added to allow back compat of older bundles which might have been created without the saveAssetPlugin + if (this._resolver.asset.__useShortPath) { + return ensureShortPath(safePath); + } + return safePath; + } + let basePath = this._resolver.asset.httpServerLocation; if (basePath[0] === '/') { basePath = basePath.substr(1); From e2f32724f78277b209d3f3d9fbe2bc7747c18c9a Mon Sep 17 00:00:00 2001 From: Andrew Coates Date: Thu, 10 Aug 2023 16:27:23 -0700 Subject: [PATCH 2/2] fix --- packages/@office-iss/react-native-win32/overrides.json | 6 ------ 1 file changed, 6 deletions(-) diff --git a/packages/@office-iss/react-native-win32/overrides.json b/packages/@office-iss/react-native-win32/overrides.json index 0168eeb974b..0237e9c34ec 100644 --- a/packages/@office-iss/react-native-win32/overrides.json +++ b/packages/@office-iss/react-native-win32/overrides.json @@ -221,12 +221,6 @@ "baseHash": "37908d71eb9a61c619f700795ff2d692410c5a57", "issue": 5170 }, - { - "type": "copy", - "file": "src/Libraries/DevToolsSettings/DevToolsSettingsManager.win32.js", - "baseFile": "packages/react-native/Libraries/DevToolsSettings/DevToolsSettingsManager.android.js", - "baseHash": "1c9eb481e8ed077fa650e3ea34837e2a31310366" - }, { "type": "platform", "file": "src/Libraries/Image/assetPaths.js"