Skip to content

Commit 902290e

Browse files
authored
Add support for Windows in the 0.69 PackageTest project (#448)
* Add back custom metro.config and specify version. * Add windows support to the PackageTest project, and update windows peerDependencies.
1 parent fa177df commit 902290e

37 files changed

+3329
-427
lines changed

Apps/PackageTest/0.69.0/.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,6 @@ buck-out/
6161
# Ruby / CocoaPods
6262
/ios/Pods/
6363
/vendor/bundle/
64+
65+
# Windows
66+
/msbuild.binlog

Apps/PackageTest/0.69.0/metro.config.js

+103-23
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,106 @@
44
*
55
* @format
66
*/
7-
const path = require('path');
8-
const exclusionList = require('metro-config/src/defaults/exclusionList');
9-
10-
module.exports = {
11-
resolver: {
12-
blockList: exclusionList([
13-
// This stops "react-native run-windows" from causing the metro server to crash if its already running
14-
new RegExp(
15-
`${path.resolve(__dirname, 'windows').replace(/[/\\]/g, '/')}.*`,
16-
),
17-
// This prevents "react-native run-windows" from hitting: EBUSY: resource busy or locked, open msbuild.ProjectImports.zip
18-
/.*\.ProjectImports\.zip/,
19-
]),
20-
},
21-
transformer: {
22-
getTransformOptions: async () => ({
23-
transform: {
24-
experimentalImportSupport: false,
25-
inlineRequires: true,
26-
},
27-
}),
28-
},
29-
};
7+
const path = require('path');
8+
const fs = require('fs');
9+
const exclusionList = require('metro-config/src/defaults/exclusionList');
10+
11+
// Escape function taken from the MDN documentation: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#Escaping
12+
function escapeRegExp(string) {
13+
return string.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
14+
}
15+
16+
// NOTE: The Metro bundler does not support symlinks (see https://github.com/facebook/metro/issues/1), which NPM uses for local packages.
17+
// To work around this, we explicity tell the metro bundler where to find local/linked packages.
18+
19+
// Create a mapping of package ids to linked directories.
20+
function processModuleSymLinks() {
21+
const nodeModulesPath = path.resolve(__dirname, 'node_modules');
22+
let moduleMappings = {};
23+
let moduleExclusions = [];
24+
25+
function findPackageDirs(directory) {
26+
fs.readdirSync(directory).forEach(item => {
27+
const itemPath = path.resolve(directory, item);
28+
const itemStat = fs.lstatSync(itemPath);
29+
if (itemStat.isSymbolicLink()) {
30+
let linkPath = fs.readlinkSync(itemPath);
31+
// Sym links are relative in Unix, absolute in Windows.
32+
if (!path.isAbsolute(linkPath)) {
33+
linkPath = path.resolve(directory, linkPath);
34+
}
35+
const linkStat = fs.statSync(linkPath);
36+
if (linkStat.isDirectory()) {
37+
const packagePath = path.resolve(linkPath, "package.json");
38+
if (fs.existsSync(packagePath)) {
39+
const packageId = path.relative(nodeModulesPath, itemPath);
40+
moduleMappings[packageId] = linkPath;
41+
42+
const packageInfoData = fs.readFileSync(packagePath);
43+
const packageInfo = JSON.parse(packageInfoData);
44+
45+
const dependencies = packageInfo.dependencies ? Object.keys(packageInfo.dependencies) : [];
46+
const peerDependencies = packageInfo.peerDependencies ? Object.keys(packageInfo.peerDependencies) : [];
47+
const devDependencies = packageInfo.devDependencies ? Object.keys(packageInfo.devDependencies) : [];
48+
49+
// Exclude dependencies that appear in devDependencies or peerDependencies but not in dependencies. Otherwise,
50+
// the metro bundler will package those devDependencies/peerDependencies as unintended copies.
51+
for (const devDependency of devDependencies.concat(peerDependencies).filter(dependency => !dependencies.includes(dependency))) {
52+
moduleExclusions.push(new RegExp(escapeRegExp(path.join(linkPath, "node_modules", devDependency)) + "\/.*"));
53+
}
54+
}
55+
}
56+
} else if (itemStat.isDirectory()) {
57+
findPackageDirs(itemPath);
58+
}
59+
});
60+
}
61+
62+
findPackageDirs(nodeModulesPath);
63+
64+
return [moduleMappings, moduleExclusions];
65+
}
66+
67+
const [moduleMappings, moduleExclusions] = processModuleSymLinks();
68+
console.log("Mapping the following sym linked packages:");
69+
console.log(moduleMappings);
70+
71+
module.exports = {
72+
transformer: {
73+
getTransformOptions: async () => ({
74+
transform: {
75+
experimentalImportSupport: false,
76+
inlineRequires: true,
77+
},
78+
}),
79+
},
80+
81+
resolver: {
82+
// Register an "extra modules proxy" for resolving modules outside of the normal resolution logic.
83+
extraNodeModules: new Proxy(
84+
// Provide the set of known local package mappings.
85+
moduleMappings,
86+
{
87+
// Provide a mapper function, which uses the above mappings for associated package ids,
88+
// otherwise fall back to the standard behavior and just look in the node_modules directory.
89+
get: (target, name) => name in target ? target[name] : path.join(__dirname, `node_modules/${name}`),
90+
},
91+
),
92+
93+
blockList: exclusionList(moduleExclusions.concat([
94+
// Avoid error EBUSY: resource busy or locked, open 'D:\a\1\s\packages\playground\msbuild.ProjectImports.zip' in pipeline
95+
/.*\.ProjectImports\.zip/,
96+
97+
// This stops "react-native run-windows" from causing the metro server to crash if its already running
98+
new RegExp(
99+
`${path.resolve(__dirname, 'windows').replace(/[/\\]/g, '/')}.*`,
100+
),
101+
])),
102+
},
103+
104+
projectRoot: path.resolve(__dirname),
105+
106+
// Also additionally watch all the mapped local directories for changes to support live updates.
107+
watchFolders: Object.values(moduleMappings),
108+
};
109+

0 commit comments

Comments
 (0)