metro 0.71.2 → 0.72.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/package.json +21 -21
  2. package/src/Assets.js +3 -2
  3. package/src/Assets.js.flow +3 -2
  4. package/src/Bundler.js +11 -2
  5. package/src/Bundler.js.flow +7 -1
  6. package/src/DeltaBundler/DeltaCalculator.js +85 -21
  7. package/src/DeltaBundler/DeltaCalculator.js.flow +63 -8
  8. package/src/DeltaBundler/Serializers/hmrJSBundle.js.flow +1 -3
  9. package/src/DeltaBundler/Transformer.js +27 -4
  10. package/src/DeltaBundler/Transformer.js.flow +18 -2
  11. package/src/DeltaBundler/Worker.flow.js +45 -1
  12. package/src/DeltaBundler/Worker.flow.js.flow +42 -1
  13. package/src/DeltaBundler/WorkerFarm.js +3 -2
  14. package/src/DeltaBundler/WorkerFarm.js.flow +5 -3
  15. package/src/DeltaBundler/graphOperations.js +170 -63
  16. package/src/DeltaBundler/graphOperations.js.flow +144 -64
  17. package/src/DeltaBundler/types.flow.js.flow +11 -5
  18. package/src/HmrServer.js +2 -0
  19. package/src/HmrServer.js.flow +2 -0
  20. package/src/IncrementalBundler.js +6 -0
  21. package/src/IncrementalBundler.js.flow +6 -0
  22. package/src/ModuleGraph/node-haste/HasteFS.js.flow +1 -1
  23. package/src/ModuleGraph/node-haste/node-haste.js +14 -7
  24. package/src/ModuleGraph/node-haste/node-haste.js.flow +35 -10
  25. package/src/ModuleGraph/output/indexed-ram-bundle.js.flow +5 -13
  26. package/src/ModuleGraph/output/multiple-files-ram-bundle.js.flow +4 -14
  27. package/src/ModuleGraph/output/util.js +1 -0
  28. package/src/ModuleGraph/output/util.js.flow +4 -3
  29. package/src/ModuleGraph/silent-console.js +5 -4
  30. package/src/ModuleGraph/silent-console.js.flow +8 -4
  31. package/src/ModuleGraph/worker/collectDependencies.js +19 -30
  32. package/src/ModuleGraph/worker/collectDependencies.js.flow +28 -43
  33. package/src/Server.js +8 -0
  34. package/src/Server.js.flow +48 -12
  35. package/src/cli-utils.js.flow +1 -1
  36. package/src/commands/build.js +1 -2
  37. package/src/commands/build.js.flow +6 -9
  38. package/src/commands/dependencies.js +1 -1
  39. package/src/commands/serve.js +2 -1
  40. package/src/commands/serve.js.flow +7 -8
  41. package/src/index.flow.js +11 -8
  42. package/src/index.flow.js.flow +10 -7
  43. package/src/integration_tests/basic_bundle/require-context/conflict.js +25 -0
  44. package/src/integration_tests/basic_bundle/require-context/conflict.js.flow +27 -0
  45. package/src/integration_tests/basic_bundle/require-context/empty.js +29 -0
  46. package/src/integration_tests/basic_bundle/require-context/empty.js.flow +26 -0
  47. package/src/integration_tests/basic_bundle/require-context/matching.js +26 -0
  48. package/src/integration_tests/basic_bundle/require-context/matching.js.flow +27 -0
  49. package/src/integration_tests/basic_bundle/require-context/mode-eager.js +22 -0
  50. package/src/integration_tests/basic_bundle/require-context/mode-eager.js.flow +24 -0
  51. package/src/integration_tests/basic_bundle/require-context/mode-lazy-once.js +22 -0
  52. package/src/integration_tests/basic_bundle/require-context/mode-lazy-once.js.flow +24 -0
  53. package/src/integration_tests/basic_bundle/require-context/mode-lazy.js +22 -0
  54. package/src/integration_tests/basic_bundle/require-context/mode-lazy.js.flow +24 -0
  55. package/src/integration_tests/basic_bundle/require-context/mode-sync.js +20 -0
  56. package/src/integration_tests/basic_bundle/require-context/mode-sync.js.flow +22 -0
  57. package/src/integration_tests/basic_bundle/require-context/subdir/a.js +12 -0
  58. package/src/integration_tests/basic_bundle/require-context/subdir/a.js.flow +11 -0
  59. package/src/integration_tests/basic_bundle/require-context/subdir/b.js +18 -0
  60. package/src/integration_tests/basic_bundle/require-context/subdir/b.js.flow +11 -0
  61. package/src/integration_tests/basic_bundle/require-context/subdir/c.js +12 -0
  62. package/src/integration_tests/basic_bundle/require-context/subdir/c.js.flow +11 -0
  63. package/src/integration_tests/basic_bundle/require-context/subdir/nested/d.js +12 -0
  64. package/src/integration_tests/basic_bundle/require-context/subdir/nested/d.js.flow +11 -0
  65. package/src/integration_tests/basic_bundle/require-context/subdir-conflict/index.js +12 -0
  66. package/src/integration_tests/basic_bundle/require-context/subdir-conflict/index.js.flow +11 -0
  67. package/src/integration_tests/basic_bundle/require-context/utils.js +29 -0
  68. package/src/integration_tests/basic_bundle/require-context/utils.js.flow +44 -0
  69. package/src/lib/CountingSet.js +1 -0
  70. package/src/lib/CountingSet.js.flow +1 -0
  71. package/src/lib/contextModule.js +80 -0
  72. package/src/lib/contextModule.js.flow +86 -0
  73. package/src/lib/contextModuleTemplates.js +186 -0
  74. package/src/lib/contextModuleTemplates.js.flow +148 -0
  75. package/src/lib/getGraphId.js +2 -1
  76. package/src/lib/getGraphId.js.flow +3 -0
  77. package/src/lib/getPrependedScripts.js +2 -0
  78. package/src/lib/getPrependedScripts.js.flow +2 -0
  79. package/src/lib/parseOptionsFromUrl.js.flow +7 -18
  80. package/src/lib/transformHelpers.js +41 -9
  81. package/src/lib/transformHelpers.js.flow +46 -9
  82. package/src/node-haste/DependencyGraph/ModuleResolution.js +1 -0
  83. package/src/node-haste/DependencyGraph/ModuleResolution.js.flow +3 -2
  84. package/src/node-haste/DependencyGraph/createHasteMap.js +7 -1
  85. package/src/node-haste/DependencyGraph/createHasteMap.js.flow +8 -2
  86. package/src/node-haste/DependencyGraph.js +7 -0
  87. package/src/node-haste/DependencyGraph.js.flow +17 -2
  88. package/src/shared/output/bundle.flow.js +67 -0
  89. package/src/shared/output/bundle.flow.js.flow +89 -0
  90. package/src/shared/output/bundle.js +8 -55
  91. package/src/shared/output/bundle.js.flow +8 -75
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true,
5
+ });
6
+ exports.deriveAbsolutePathFromContext = deriveAbsolutePathFromContext;
7
+ exports.fileMatchesContext = fileMatchesContext;
8
+
9
+ var _crypto = _interopRequireDefault(require("crypto"));
10
+
11
+ var _path = _interopRequireDefault(require("path"));
12
+
13
+ var _nullthrows = _interopRequireDefault(require("nullthrows"));
14
+
15
+ function _interopRequireDefault(obj) {
16
+ return obj && obj.__esModule ? obj : { default: obj };
17
+ }
18
+
19
+ /**
20
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
21
+ *
22
+ * This source code is licensed under the MIT license found in the
23
+ * LICENSE file in the root directory of this source tree.
24
+ *
25
+ *
26
+ * @format
27
+ */
28
+ function toHash(value) {
29
+ // Use `hex` to ensure filepath safety.
30
+ return _crypto.default.createHash("sha1").update(value).digest("hex");
31
+ }
32
+ /** Given a fully qualified require context, return a virtual file path that ensures uniqueness between paths with different contexts. */
33
+
34
+ function deriveAbsolutePathFromContext(from, context) {
35
+ // Drop the trailing slash, require.context should always be matched against a folder
36
+ // and we want to normalize the folder name as much as possible to prevent duplicates.
37
+ // This also makes the files show up in the correct location when debugging in Chrome.
38
+ const filePath = from.endsWith(_path.default.sep) ? from.slice(0, -1) : from;
39
+ return (
40
+ filePath +
41
+ "?ctx=" +
42
+ toHash(
43
+ [
44
+ context.mode,
45
+ context.recursive ? "recursive" : "",
46
+ new RegExp(context.filter.pattern, context.filter.flags).toString(),
47
+ ]
48
+ .filter(Boolean)
49
+ .join(" ")
50
+ )
51
+ );
52
+ }
53
+ /** Match a file against a require context. */
54
+
55
+ function fileMatchesContext(testPath, context) {
56
+ // NOTE(EvanBacon): Ensure this logic is synchronized with the similar
57
+ // functionality in `metro-file-map/src/HasteFS.js` (`matchFilesWithContext()`)
58
+ const filePath = _path.default.relative(
59
+ (0, _nullthrows.default)(context.from),
60
+ testPath
61
+ );
62
+
63
+ const filter = context.filter;
64
+
65
+ if (
66
+ // Ignore everything outside of the provided `root`.
67
+ !(filePath && !filePath.startsWith("..")) || // Prevent searching in child directories during a non-recursive search.
68
+ (!context.recursive && filePath.includes(_path.default.sep)) || // Test against the filter.
69
+ !filter.test(
70
+ // NOTE(EvanBacon): Ensure files start with `./` for matching purposes
71
+ // this ensures packages work across Metro and Webpack (ex: Storybook for React DOM / React Native).
72
+ // `a/b.js` -> `./a/b.js`
73
+ "./" + filePath.replace(/\\/g, "/")
74
+ )
75
+ ) {
76
+ return false;
77
+ }
78
+
79
+ return true;
80
+ }
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow strict-local
8
+ * @format
9
+ */
10
+
11
+ import crypto from 'crypto';
12
+ import path from 'path';
13
+ import type {
14
+ ContextMode,
15
+ RequireContextParams,
16
+ } from '../ModuleGraph/worker/collectDependencies';
17
+ import nullthrows from 'nullthrows';
18
+
19
+ export type RequireContext = $ReadOnly<{
20
+ /* Should search for files recursively. Optional, default `true` when `require.context` is used */
21
+ recursive: boolean,
22
+ /* Filename filter pattern for use in `require.context`. Optional, default `.*` (any file) when `require.context` is used */
23
+ filter: RegExp,
24
+ /** Mode for resolving dynamic dependencies. Defaults to `sync` */
25
+ mode: ContextMode,
26
+ /** Absolute path of the directory to search in */
27
+ from: string,
28
+ }>;
29
+
30
+ function toHash(value: string): string {
31
+ // Use `hex` to ensure filepath safety.
32
+ return crypto.createHash('sha1').update(value).digest('hex');
33
+ }
34
+
35
+ /** Given a fully qualified require context, return a virtual file path that ensures uniqueness between paths with different contexts. */
36
+ export function deriveAbsolutePathFromContext(
37
+ from: string,
38
+ context: RequireContextParams,
39
+ ): string {
40
+ // Drop the trailing slash, require.context should always be matched against a folder
41
+ // and we want to normalize the folder name as much as possible to prevent duplicates.
42
+ // This also makes the files show up in the correct location when debugging in Chrome.
43
+ const filePath = from.endsWith(path.sep) ? from.slice(0, -1) : from;
44
+ return (
45
+ filePath +
46
+ '?ctx=' +
47
+ toHash(
48
+ [
49
+ context.mode,
50
+ context.recursive ? 'recursive' : '',
51
+ new RegExp(context.filter.pattern, context.filter.flags).toString(),
52
+ ]
53
+ .filter(Boolean)
54
+ .join(' '),
55
+ )
56
+ );
57
+ }
58
+
59
+ /** Match a file against a require context. */
60
+ export function fileMatchesContext(
61
+ testPath: string,
62
+ context: RequireContext,
63
+ ): boolean {
64
+ // NOTE(EvanBacon): Ensure this logic is synchronized with the similar
65
+ // functionality in `metro-file-map/src/HasteFS.js` (`matchFilesWithContext()`)
66
+
67
+ const filePath = path.relative(nullthrows(context.from), testPath);
68
+ const filter = context.filter;
69
+ if (
70
+ // Ignore everything outside of the provided `root`.
71
+ !(filePath && !filePath.startsWith('..')) ||
72
+ // Prevent searching in child directories during a non-recursive search.
73
+ (!context.recursive && filePath.includes(path.sep)) ||
74
+ // Test against the filter.
75
+ !filter.test(
76
+ // NOTE(EvanBacon): Ensure files start with `./` for matching purposes
77
+ // this ensures packages work across Metro and Webpack (ex: Storybook for React DOM / React Native).
78
+ // `a/b.js` -> `./a/b.js`
79
+ './' + filePath.replace(/\\/g, '/'),
80
+ )
81
+ ) {
82
+ return false;
83
+ }
84
+
85
+ return true;
86
+ }
@@ -0,0 +1,186 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true,
5
+ });
6
+ exports.getContextModuleTemplate = getContextModuleTemplate;
7
+
8
+ var path = _interopRequireWildcard(require("path"));
9
+
10
+ function _getRequireWildcardCache(nodeInterop) {
11
+ if (typeof WeakMap !== "function") return null;
12
+ var cacheBabelInterop = new WeakMap();
13
+ var cacheNodeInterop = new WeakMap();
14
+ return (_getRequireWildcardCache = function (nodeInterop) {
15
+ return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
16
+ })(nodeInterop);
17
+ }
18
+
19
+ function _interopRequireWildcard(obj, nodeInterop) {
20
+ if (!nodeInterop && obj && obj.__esModule) {
21
+ return obj;
22
+ }
23
+ if (obj === null || (typeof obj !== "object" && typeof obj !== "function")) {
24
+ return { default: obj };
25
+ }
26
+ var cache = _getRequireWildcardCache(nodeInterop);
27
+ if (cache && cache.has(obj)) {
28
+ return cache.get(obj);
29
+ }
30
+ var newObj = {};
31
+ var hasPropertyDescriptor =
32
+ Object.defineProperty && Object.getOwnPropertyDescriptor;
33
+ for (var key in obj) {
34
+ if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
35
+ var desc = hasPropertyDescriptor
36
+ ? Object.getOwnPropertyDescriptor(obj, key)
37
+ : null;
38
+ if (desc && (desc.get || desc.set)) {
39
+ Object.defineProperty(newObj, key, desc);
40
+ } else {
41
+ newObj[key] = obj[key];
42
+ }
43
+ }
44
+ }
45
+ newObj.default = obj;
46
+ if (cache) {
47
+ cache.set(obj, newObj);
48
+ }
49
+ return newObj;
50
+ }
51
+
52
+ /**
53
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
54
+ *
55
+ * This source code is licensed under the MIT license found in the
56
+ * LICENSE file in the root directory of this source tree.
57
+ *
58
+ *
59
+ * @format
60
+ */
61
+ function createFileMap(modulePath, files, processModule) {
62
+ let mapString = "\n";
63
+ files
64
+ .slice() // Sort for deterministic output
65
+ .sort()
66
+ .forEach((file) => {
67
+ let filePath = path.relative(modulePath, file); // NOTE(EvanBacon): I'd prefer we prevent the ability for a module to require itself (`require.context('./')`)
68
+ // but Webpack allows this, keeping it here provides better parity between bundlers.
69
+ // Ensure relative file paths start with `./` so they match the
70
+ // patterns (filters) used to include them.
71
+
72
+ if (!filePath.startsWith(".")) {
73
+ filePath = `.${path.sep}` + filePath;
74
+ }
75
+
76
+ const key = JSON.stringify(filePath); // NOTE(EvanBacon): Webpack uses `require.resolve` in order to load modules on demand,
77
+ // Metro doesn't have this functionality so it will use getters instead. Modules need to
78
+ // be loaded on demand because if we imported directly then users would get errors from importing
79
+ // a file without exports as soon as they create a new file and the context module is updated.
80
+ // NOTE: The values are set to `enumerable` so the `context.keys()` method works as expected.
81
+
82
+ mapString += ` ${key}: { enumerable: true, get() { return ${processModule(
83
+ file
84
+ )}; } },\n`;
85
+ });
86
+ return `Object.defineProperties({}, {${mapString}})`;
87
+ }
88
+
89
+ function getEmptyContextModuleTemplate(modulePath) {
90
+ return `
91
+ function metroEmptyContext(request) {
92
+ let e = new Error('No modules in context');
93
+ e.code = 'MODULE_NOT_FOUND';
94
+ throw e;
95
+ }
96
+
97
+ // Return the keys that can be resolved.
98
+ metroEmptyContext.keys = () => ([]);
99
+
100
+ // Return the module identifier for a user request.
101
+ metroEmptyContext.resolve = function metroContextResolve(request) {
102
+ throw new Error('Unimplemented Metro module context functionality');
103
+ }
104
+
105
+ module.exports = metroEmptyContext;`;
106
+ }
107
+
108
+ function getLoadableContextModuleTemplate(
109
+ modulePath,
110
+ files,
111
+ importSyntax,
112
+ getContextTemplate
113
+ ) {
114
+ return `// All of the requested modules are loaded behind enumerable getters.
115
+ const map = ${createFileMap(
116
+ modulePath,
117
+ files,
118
+ (moduleId) => `${importSyntax}(${JSON.stringify(moduleId)})`
119
+ )};
120
+
121
+ function metroContext(request) {
122
+ ${getContextTemplate}
123
+ }
124
+
125
+ // Return the keys that can be resolved.
126
+ metroContext.keys = function metroContextKeys() {
127
+ return Object.keys(map);
128
+ };
129
+
130
+ // Return the module identifier for a user request.
131
+ metroContext.resolve = function metroContextResolve(request) {
132
+ throw new Error('Unimplemented Metro module context functionality');
133
+ }
134
+
135
+ module.exports = metroContext;`;
136
+ }
137
+ /**
138
+ * Generate a context module as a virtual file string.
139
+ *
140
+ * @prop {ContextMode} mode indicates how the modules should be loaded.
141
+ * @prop {string} modulePath virtual file path for the virtual module. Example: `require.context('./src')` -> `'/path/to/project/src'`.
142
+ * @prop {string[]} files list of absolute file paths that must be exported from the context module. Example: `['/path/to/project/src/index.js']`.
143
+ *
144
+ * @returns a string representing a context module (virtual file contents).
145
+ */
146
+
147
+ function getContextModuleTemplate(mode, modulePath, files) {
148
+ if (!files.length) {
149
+ return getEmptyContextModuleTemplate(modulePath);
150
+ }
151
+
152
+ switch (mode) {
153
+ case "eager":
154
+ return getLoadableContextModuleTemplate(
155
+ modulePath,
156
+ files, // NOTE(EvanBacon): It's unclear if we should use `import` or `require` here so sticking
157
+ // with the more stable option (`require`) for now.
158
+ "require",
159
+ [
160
+ " // Here Promise.resolve().then() is used instead of new Promise() to prevent",
161
+ " // uncaught exception popping up in devtools",
162
+ " return Promise.resolve().then(() => map[request]);",
163
+ ].join("\n")
164
+ );
165
+
166
+ case "sync":
167
+ return getLoadableContextModuleTemplate(
168
+ modulePath,
169
+ files,
170
+ "require",
171
+ " return map[request];"
172
+ );
173
+
174
+ case "lazy":
175
+ case "lazy-once":
176
+ return getLoadableContextModuleTemplate(
177
+ modulePath,
178
+ files,
179
+ "import",
180
+ " return map[request];"
181
+ );
182
+
183
+ default:
184
+ throw new Error(`Metro context mode "${mode}" is unimplemented`);
185
+ }
186
+ }
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow strict-local
8
+ * @format
9
+ */
10
+
11
+ import * as path from 'path';
12
+ import type {ContextMode} from '../ModuleGraph/worker/collectDependencies';
13
+
14
+ function createFileMap(
15
+ modulePath: string,
16
+ files: string[],
17
+ processModule: (moduleId: string) => string,
18
+ ): string {
19
+ let mapString = '\n';
20
+
21
+ files
22
+ .slice()
23
+ // Sort for deterministic output
24
+ .sort()
25
+ .forEach(file => {
26
+ let filePath = path.relative(modulePath, file);
27
+
28
+ // NOTE(EvanBacon): I'd prefer we prevent the ability for a module to require itself (`require.context('./')`)
29
+ // but Webpack allows this, keeping it here provides better parity between bundlers.
30
+
31
+ // Ensure relative file paths start with `./` so they match the
32
+ // patterns (filters) used to include them.
33
+ if (!filePath.startsWith('.')) {
34
+ filePath = `.${path.sep}` + filePath;
35
+ }
36
+ const key = JSON.stringify(filePath);
37
+ // NOTE(EvanBacon): Webpack uses `require.resolve` in order to load modules on demand,
38
+ // Metro doesn't have this functionality so it will use getters instead. Modules need to
39
+ // be loaded on demand because if we imported directly then users would get errors from importing
40
+ // a file without exports as soon as they create a new file and the context module is updated.
41
+
42
+ // NOTE: The values are set to `enumerable` so the `context.keys()` method works as expected.
43
+ mapString += ` ${key}: { enumerable: true, get() { return ${processModule(
44
+ file,
45
+ )}; } },\n`;
46
+ });
47
+ return `Object.defineProperties({}, {${mapString}})`;
48
+ }
49
+
50
+ function getEmptyContextModuleTemplate(modulePath: string): string {
51
+ return `
52
+ function metroEmptyContext(request) {
53
+ let e = new Error('No modules in context');
54
+ e.code = 'MODULE_NOT_FOUND';
55
+ throw e;
56
+ }
57
+
58
+ // Return the keys that can be resolved.
59
+ metroEmptyContext.keys = () => ([]);
60
+
61
+ // Return the module identifier for a user request.
62
+ metroEmptyContext.resolve = function metroContextResolve(request) {
63
+ throw new Error('Unimplemented Metro module context functionality');
64
+ }
65
+
66
+ module.exports = metroEmptyContext;`;
67
+ }
68
+
69
+ function getLoadableContextModuleTemplate(
70
+ modulePath: string,
71
+ files: string[],
72
+ importSyntax: string,
73
+ getContextTemplate: string,
74
+ ): string {
75
+ return `// All of the requested modules are loaded behind enumerable getters.
76
+ const map = ${createFileMap(
77
+ modulePath,
78
+ files,
79
+ moduleId => `${importSyntax}(${JSON.stringify(moduleId)})`,
80
+ )};
81
+
82
+ function metroContext(request) {
83
+ ${getContextTemplate}
84
+ }
85
+
86
+ // Return the keys that can be resolved.
87
+ metroContext.keys = function metroContextKeys() {
88
+ return Object.keys(map);
89
+ };
90
+
91
+ // Return the module identifier for a user request.
92
+ metroContext.resolve = function metroContextResolve(request) {
93
+ throw new Error('Unimplemented Metro module context functionality');
94
+ }
95
+
96
+ module.exports = metroContext;`;
97
+ }
98
+
99
+ /**
100
+ * Generate a context module as a virtual file string.
101
+ *
102
+ * @prop {ContextMode} mode indicates how the modules should be loaded.
103
+ * @prop {string} modulePath virtual file path for the virtual module. Example: `require.context('./src')` -> `'/path/to/project/src'`.
104
+ * @prop {string[]} files list of absolute file paths that must be exported from the context module. Example: `['/path/to/project/src/index.js']`.
105
+ *
106
+ * @returns a string representing a context module (virtual file contents).
107
+ */
108
+ export function getContextModuleTemplate(
109
+ mode: ContextMode,
110
+ modulePath: string,
111
+ files: string[],
112
+ ): string {
113
+ if (!files.length) {
114
+ return getEmptyContextModuleTemplate(modulePath);
115
+ }
116
+ switch (mode) {
117
+ case 'eager':
118
+ return getLoadableContextModuleTemplate(
119
+ modulePath,
120
+ files,
121
+ // NOTE(EvanBacon): It's unclear if we should use `import` or `require` here so sticking
122
+ // with the more stable option (`require`) for now.
123
+ 'require',
124
+ [
125
+ ' // Here Promise.resolve().then() is used instead of new Promise() to prevent',
126
+ ' // uncaught exception popping up in devtools',
127
+ ' return Promise.resolve().then(() => map[request]);',
128
+ ].join('\n'),
129
+ );
130
+ case 'sync':
131
+ return getLoadableContextModuleTemplate(
132
+ modulePath,
133
+ files,
134
+ 'require',
135
+ ' return map[request];',
136
+ );
137
+ case 'lazy':
138
+ case 'lazy-once':
139
+ return getLoadableContextModuleTemplate(
140
+ modulePath,
141
+ files,
142
+ 'import',
143
+ ' return map[request];',
144
+ );
145
+ default:
146
+ throw new Error(`Metro context mode "${mode}" is unimplemented`);
147
+ }
148
+ }
@@ -14,7 +14,7 @@ const canonicalize = require("metro-core/src/canonicalize");
14
14
  function getGraphId(
15
15
  entryFile,
16
16
  options,
17
- { shallow, experimentalImportBundleSupport }
17
+ { shallow, experimentalImportBundleSupport, unstable_allowRequireContext }
18
18
  ) {
19
19
  return JSON.stringify(
20
20
  {
@@ -33,6 +33,7 @@ function getGraphId(
33
33
  runtimeBytecodeVersion: options.runtimeBytecodeVersion,
34
34
  type: options.type,
35
35
  experimentalImportBundleSupport,
36
+ unstable_allowRequireContext,
36
37
  shallow,
37
38
  unstable_transformProfile:
38
39
  options.unstable_transformProfile || "default",
@@ -22,9 +22,11 @@ function getGraphId(
22
22
  {
23
23
  shallow,
24
24
  experimentalImportBundleSupport,
25
+ unstable_allowRequireContext,
25
26
  }: {
26
27
  +shallow: boolean,
27
28
  +experimentalImportBundleSupport: boolean,
29
+ +unstable_allowRequireContext: boolean,
28
30
  ...
29
31
  },
30
32
  ): GraphId {
@@ -45,6 +47,7 @@ function getGraphId(
45
47
  runtimeBytecodeVersion: options.runtimeBytecodeVersion,
46
48
  type: options.type,
47
49
  experimentalImportBundleSupport,
50
+ unstable_allowRequireContext,
48
51
  shallow,
49
52
  unstable_transformProfile:
50
53
  options.unstable_transformProfile || 'default',
@@ -48,6 +48,8 @@ async function getPrependedScripts(config, options, bundler, deltaBundler) {
48
48
  config,
49
49
  transformOptions
50
50
  ),
51
+ unstable_allowRequireContext:
52
+ config.transformer.unstable_allowRequireContext,
51
53
  transformOptions,
52
54
  onProgress: null,
53
55
  experimentalImportBundleSupport:
@@ -59,6 +59,8 @@ async function getPrependedScripts(
59
59
  config,
60
60
  transformOptions,
61
61
  ),
62
+ unstable_allowRequireContext:
63
+ config.transformer.unstable_allowRequireContext,
62
64
  transformOptions,
63
65
  onProgress: null,
64
66
  experimentalImportBundleSupport:
@@ -11,6 +11,7 @@
11
11
  'use strict';
12
12
 
13
13
  import type {BundleOptions} from '../shared/types.flow';
14
+ import type {TransformProfile} from 'metro-babel-transformer';
14
15
 
15
16
  const parsePlatformFilePath = require('../node-haste/lib/parsePlatformFilePath');
16
17
  const parseCustomTransformOptions = require('./parseCustomTransformOptions');
@@ -19,15 +20,8 @@ const path = require('path');
19
20
  const url = require('url');
20
21
 
21
22
  const getBoolean = (
22
- query: {[string]: string},
23
- opt:
24
- | $TEMPORARY$string<'dev'>
25
- | $TEMPORARY$string<'excludeSource'>
26
- | $TEMPORARY$string<'inlineSourceMap'>
27
- | $TEMPORARY$string<'minify'>
28
- | $TEMPORARY$string<'modulesOnly'>
29
- | $TEMPORARY$string<'runModule'>
30
- | $TEMPORARY$string<'shallow'>,
23
+ query: $ReadOnly<{[opt: string]: string}>,
24
+ opt: string,
31
25
  defaultValue: boolean,
32
26
  ) =>
33
27
  query[opt] == null
@@ -35,23 +29,18 @@ const getBoolean = (
35
29
  : query[opt] === 'true' || query[opt] === '1';
36
30
 
37
31
  const getNumber = (
38
- query: {[string]: string},
39
- opt: $TEMPORARY$string<'runtimeBytecodeVersion'>,
32
+ query: $ReadOnly<{[opt: string]: string}>,
33
+ opt: string,
40
34
  defaultValue: null,
41
35
  ) => {
42
36
  const number = parseInt(query[opt], 10);
43
37
  return Number.isNaN(number) ? defaultValue : number;
44
38
  };
45
39
 
46
- const getBundleType = (bundleType: string | $TEMPORARY$string<'map'>) =>
40
+ const getBundleType = (bundleType: string): 'map' | 'bundle' =>
47
41
  bundleType === 'map' ? bundleType : 'bundle';
48
42
 
49
- const getTransformProfile = (
50
- transformProfile:
51
- | string
52
- | $TEMPORARY$string<'hermes-canary'>
53
- | $TEMPORARY$string<'hermes-stable'>,
54
- ) =>
43
+ const getTransformProfile = (transformProfile: string): TransformProfile =>
55
44
  transformProfile === 'hermes-stable' || transformProfile === 'hermes-canary'
56
45
  ? transformProfile
57
46
  : 'default';
@@ -9,6 +9,8 @@
9
9
  */
10
10
  "use strict";
11
11
 
12
+ var _contextModuleTemplates = require("./contextModuleTemplates");
13
+
12
14
  const path = require("path");
13
15
 
14
16
  const baseIgnoredInlineRequires = ["React", "react", "react-native"];
@@ -49,6 +51,8 @@ async function calcTransformerOptions(
49
51
  onProgress: null,
50
52
  experimentalImportBundleSupport:
51
53
  config.transformer.experimentalImportBundleSupport,
54
+ unstable_allowRequireContext:
55
+ config.transformer.unstable_allowRequireContext,
52
56
  shallow: false,
53
57
  });
54
58
  return Array.from(dependencies.keys());
@@ -97,15 +101,43 @@ async function getTransformFn(
97
101
  config,
98
102
  options
99
103
  );
100
- return async (path) => {
101
- return await bundler.transformFile(path, {
102
- ...transformOptions,
103
- type: getType(transformOptions.type, path, config.resolver.assetExts),
104
- inlineRequires: removeInlineRequiresBlockListFromOptions(
105
- path,
106
- inlineRequires
107
- ),
108
- });
104
+ return async (modulePath, requireContext) => {
105
+ let templateBuffer;
106
+
107
+ if (requireContext) {
108
+ const graph = await bundler.getDependencyGraph(); // TODO: Check delta changes to avoid having to look over all files each time
109
+ // this is a massive performance boost.
110
+ // Search against all files, this is very expensive.
111
+ // TODO: Maybe we could let the user specify which root to check against.
112
+
113
+ const files = graph.matchFilesWithContext(requireContext.from, {
114
+ filter: requireContext.filter,
115
+ recursive: requireContext.recursive,
116
+ });
117
+ const template = (0, _contextModuleTemplates.getContextModuleTemplate)(
118
+ requireContext.mode,
119
+ requireContext.from,
120
+ files
121
+ );
122
+ templateBuffer = Buffer.from(template);
123
+ }
124
+
125
+ return await bundler.transformFile(
126
+ modulePath,
127
+ {
128
+ ...transformOptions,
129
+ type: getType(
130
+ transformOptions.type,
131
+ modulePath,
132
+ config.resolver.assetExts
133
+ ),
134
+ inlineRequires: removeInlineRequiresBlockListFromOptions(
135
+ modulePath,
136
+ inlineRequires
137
+ ),
138
+ },
139
+ templateBuffer
140
+ );
109
141
  };
110
142
  }
111
143