Skip to content

Commit

Permalink
[0.71] Add saveAssetPlugin to fix long path assets (#12006)
Browse files Browse the repository at this point in the history
* [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

* fix
  • Loading branch information
acoates-ms authored Aug 11, 2023
1 parent a69f755 commit 8195305
Show file tree
Hide file tree
Showing 8 changed files with 129 additions and 5 deletions.
Original file line number Diff line number Diff line change
@@ -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"
}
2 changes: 2 additions & 0 deletions packages/@office-iss/react-native-win32/metro.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -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')];
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// @ts-check
/**
* @typedef {import("metro").AssetData} AssetData;
**/

/**
* @param {AssetData & {__useShortPath: boolean}} asset
* @returns {Promise<AssetData>}
*/
async function metroShortPathAssetDataPlugin(asset) {
asset.__useShortPath = true;
return Promise.resolve(asset);
}

module.exports = metroShortPathAssetDataPlugin;
4 changes: 4 additions & 0 deletions packages/@office-iss/react-native-win32/overrides.json
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,10 @@
"baseHash": "37908d71eb9a61c619f700795ff2d692410c5a57",
"issue": 5170
},
{
"type": "platform",
"file": "src/Libraries/Image/assetPaths.js"
},
{
"type": "derived",
"file": "src/Libraries/Image/Image.win32.js",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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'
},
},
};
45 changes: 45 additions & 0 deletions packages/@office-iss/react-native-win32/saveAssetPlugin.js
Original file line number Diff line number Diff line change
@@ -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<AssetData>} 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;
Original file line number Diff line number Diff line change
@@ -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;
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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));
}

/**
Expand All @@ -66,7 +67,7 @@ class AssetResolverLateScaleResolution {
_assetServerURL() {
return this._fromSource(
this._resolver.serverUrl +
this._getAssetPath() +
this._getAssetPath(false) +
'?platform=' +
Platform.OS +
'&hash=' +
Expand All @@ -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 +
'/' +
Expand All @@ -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);
Expand Down

0 comments on commit 8195305

Please sign in to comment.