Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 0 additions & 83 deletions packages/metro/src/Server/__tests__/Server-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1436,87 +1436,4 @@ describe('processRequest', () => {
},
);
});

describe('watchFolder prefix resolution', () => {
let watchFolderServer: $FlowFixMe;

beforeEach(() => {
watchFolderServer = new Server(
mergeConfig(getDefaultValues('/'), {
projectRoot: '/project',
watchFolders: ['/project', '/external/packages'],
resolver: {blockList: []},
cacheVersion: '',
serializer: {
getRunModuleStatement: moduleId =>
`require(${JSON.stringify(moduleId)});`,
polyfillModuleNames: [],
getModulesRunBeforeMainModule: () => ['InitializeCore'],
},
reporter: require('../../lib/reporting').nullReporter,
} as InputConfigT),
);
});

test('resolves [metro-watchFolders]/N/ prefix against the Nth watch folder', () => {
expect(
watchFolderServer._resolveWatchFolderPrefix(
'./[metro-watchFolders]/1/expo-router/entry',
),
).toEqual({
rootDir: '/external/packages',
filePath: './expo-router/entry',
});
});

test('resolves [metro-watchFolders]/0/ prefix against the first watch folder', () => {
expect(
watchFolderServer._resolveWatchFolderPrefix(
'./[metro-watchFolders]/0/app/index',
),
).toEqual({
rootDir: '/project',
filePath: './app/index',
});
});

test('resolves [metro-project]/ prefix against projectRoot', () => {
expect(
watchFolderServer._resolveWatchFolderPrefix(
'./[metro-project]/src/App',
),
).toEqual({
rootDir: '/project',
filePath: './src/App',
});
});

test('returns null for paths without a recognized prefix', () => {
expect(
watchFolderServer._resolveWatchFolderPrefix('./mybundle'),
).toBeNull();
});

test('returns null for out-of-bounds watchFolder index', () => {
expect(
watchFolderServer._resolveWatchFolderPrefix(
'./[metro-watchFolders]/99/mybundle',
),
).toBeNull();
});

test('_getEntryPointAbsolutePath resolves prefixed entry against the corresponding watch folder', () => {
expect(
watchFolderServer._getEntryPointAbsolutePath(
'./[metro-watchFolders]/1/expo-router/entry',
),
).toBe('/external/packages/expo-router/entry');
});

test('_getEntryPointAbsolutePath resolves non-prefixed entry against server root', () => {
expect(watchFolderServer._getEntryPointAbsolutePath('./mybundle')).toBe(
'/project/mybundle',
);
});
});
});
35 changes: 35 additions & 0 deletions packages/metro/src/integration_tests/__tests__/build-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,41 @@ test('allows specifying paths to save bundle and maps', async () => {
);
});

// $FlowFixMe[prop-missing] - test.failing is not in Flow's Jest types
test.failing(
'builds a bundle from a file in a directory literally named [metro-project]',
async () => {
const config = await Metro.loadConfig({
config: require.resolve('../metro.config.js'),
});

const result = await Metro.runBuild(config, {
entry: './[metro-project]/LiteralDir.js',
});

expect(execBundle(result.code)).toBe('from-literal-dir');
},
);

// $FlowFixMe[prop-missing] - test.failing is not in Flow's Jest types
test.failing(
'runBuild resolves entry against projectRoot, not unstable_serverRoot',
async () => {
const baseConfig = await Metro.loadConfig({
config: require.resolve('../metro.config.js'),
});
const config = MetroConfig.mergeConfig(baseConfig, {
server: {unstable_serverRoot: path.resolve(INPUT_PATH, '..')},
});

const result = await Metro.runBuild(config, {
entry: 'TestBundle.js',
});

expect(execBundle(result.code)).toBeDefined();
},
);

test('(unstable) allows specifying a transform profile', async () => {
const config = await Metro.loadConfig({
config: require.resolve('../metro.config.js'),
Expand Down
30 changes: 22 additions & 8 deletions packages/metro/src/integration_tests/__tests__/rambundle-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,32 +19,46 @@ const vm = require('vm');

jest.setTimeout(30 * 1000);

test('builds and executes a RAM bundle', async () => {
const config = await Metro.loadConfig({
let config;

beforeAll(async () => {
config = await Metro.loadConfig({
config: require.resolve('../metro.config.js'),
});
const bundlePath = path.join(os.tmpdir(), 'rambundle.js');
});

async function buildAndExecRamBundle(entry: string): mixed {
const bundlePath = path.join(os.tmpdir(), `rambundle-${Date.now()}.js`);
try {
await Metro.runBuild(config, {
entry: 'TestBundle.js',
entry,
output: ramBundleOutput,
out: bundlePath,
});

const bundleBuffer = fs.readFileSync(bundlePath);
const parser = new RamBundleParser(bundleBuffer);

// Create a context with a global nativeRequire function, which reads the
// module code from the RAM bundle and injects it into the VM.
const context = vm.createContext({
nativeRequire(id) {
vm.runInContext(parser.getModule(id), context);
},
});

expect(vm.runInContext(parser.getStartupCode(), context)).toMatchSnapshot();
return vm.runInContext(parser.getStartupCode(), context);
} finally {
fs.unlinkSync(bundlePath);
if (fs.existsSync(bundlePath)) {
fs.unlinkSync(bundlePath);
}
}
}

test('builds and executes a RAM bundle', async () => {
expect(await buildAndExecRamBundle('TestBundle.js')).toMatchSnapshot();
});

test('rejects [metro-project] virtual prefix in runBuild entry', async () => {
await expect(
buildAndExecRamBundle('./[metro-project]/TestBundle.js'),
).rejects.toThrow('was not found');
});
79 changes: 79 additions & 0 deletions packages/metro/src/integration_tests/__tests__/server-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,85 @@ describe('Metro development server serves bundles via HTTP', () => {
);
});

// TODO(T000000): Fix virtual-prefix URL resolution on Windows.
// path.sep differences cause entry point resolution to fail.
(process.platform === 'win32' ? test.skip : test)(
'should serve bundles with [metro-watchFolders] entry point',
async () => {
expect(
await downloadAndExec(
'/[metro-watchFolders]/1/metro/src/integration_tests/basic_bundle/TestBundle.bundle?platform=ios&dev=true&minify=false',
),
).toBeDefined();
},
);

(process.platform === 'win32' ? test.skip : test)(
'should serve bundles with [metro-project] entry point',
async () => {
expect(
await downloadAndExec(
'/[metro-project]/TestBundle.bundle?platform=ios&dev=true&minify=false',
),
).toBeDefined();
},
);

(process.platform === 'win32' ? test.skip : test)(
'[metro-project] source map resolves same modules as non-prefixed',
async () => {
const directResponse = await fetchAndClose(
'http://localhost:' +
httpServer.address().port +
'/TestBundle.map?platform=ios&dev=true&minify=false',
);
const prefixedResponse = await fetchAndClose(
'http://localhost:' +
httpServer.address().port +
'/[metro-project]/TestBundle.map?platform=ios&dev=true&minify=false',
);
expect(directResponse.ok).toBe(true);
expect(prefixedResponse.ok).toBe(true);
const directMap = await directResponse.json();
const prefixedMap = await prefixedResponse.json();
expect([...prefixedMap.sources].sort()).toEqual(
[...directMap.sources].sort(),
);
},
);

(process.platform === 'win32' ? test.skip : test)(
'[metro-watchFolders] source map resolves same modules as non-prefixed',
async () => {
const directResponse = await fetchAndClose(
'http://localhost:' +
httpServer.address().port +
'/TestBundle.map?platform=ios&dev=true&minify=false',
);
const watchFolderResponse = await fetchAndClose(
'http://localhost:' +
httpServer.address().port +
'/[metro-watchFolders]/1/metro/src/integration_tests/basic_bundle/TestBundle.map?platform=ios&dev=true&minify=false',
);
expect(directResponse.ok).toBe(true);
expect(watchFolderResponse.ok).toBe(true);
const directMap = await directResponse.json();
const watchFolderMap = await watchFolderResponse.json();
expect([...watchFolderMap.sources].sort()).toEqual(
[...directMap.sources].sort(),
);
},
);

test('responds with 404 for [metro-watchFolders] with out-of-bounds index', async () => {
const response = await fetchAndClose(
'http://localhost:' +
httpServer.address().port +
'/[metro-watchFolders]/99/TestBundle.bundle?platform=ios&dev=true&minify=false',
);
expect(response.status).toBe(404);
});

test('responds with 404 when the bundle cannot be resolved', async () => {
const response = await fetchAndClose(
'http://localhost:' + httpServer.address().port + '/doesnotexist.bundle',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/

module.exports = 'from-literal-dir';
Loading