@expo/cli
Version:
343 lines (342 loc) • 14.4 kB
JavaScript
Object.defineProperty(exports, "__esModule", {
value: true
});
function _export(target, all) {
for(var name in all)Object.defineProperty(target, name, {
enumerable: true,
get: all[name]
});
}
_export(exports, {
DEVELOPER_TOOL: function() {
return DEVELOPER_TOOL;
},
ManifestMiddleware: function() {
return ManifestMiddleware;
},
getEntryWithServerRoot: function() {
return getEntryWithServerRoot;
},
resolveMainModuleName: function() {
return resolveMainModuleName;
}
});
function _config() {
const data = require("@expo/config");
_config = function() {
return data;
};
return data;
}
function _paths() {
const data = require("@expo/config/paths");
_paths = function() {
return data;
};
return data;
}
function _path() {
const data = /*#__PURE__*/ _interop_require_default(require("path"));
_path = function() {
return data;
};
return data;
}
function _url() {
const data = require("url");
_url = function() {
return data;
};
return data;
}
const _ExpoMiddleware = require("./ExpoMiddleware");
const _metroOptions = require("./metroOptions");
const _resolveAssets = require("./resolveAssets");
const _resolvePlatform = require("./resolvePlatform");
const _exportHermes = require("../../../export/exportHermes");
const _log = /*#__PURE__*/ _interop_require_wildcard(require("../../../log"));
const _errors = require("../../../utils/errors");
const _url1 = require("../../../utils/url");
const _devices = /*#__PURE__*/ _interop_require_wildcard(require("../../project/devices"));
const _router = require("../metro/router");
const _platformBundlers = require("../platformBundlers");
const _webTemplate = require("../webTemplate");
function _interop_require_default(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
function _getRequireWildcardCache(nodeInterop) {
if (typeof WeakMap !== "function") return null;
var cacheBabelInterop = new WeakMap();
var cacheNodeInterop = new WeakMap();
return (_getRequireWildcardCache = function(nodeInterop) {
return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
})(nodeInterop);
}
function _interop_require_wildcard(obj, nodeInterop) {
if (!nodeInterop && obj && obj.__esModule) {
return obj;
}
if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
return {
default: obj
};
}
var cache = _getRequireWildcardCache(nodeInterop);
if (cache && cache.has(obj)) {
return cache.get(obj);
}
var newObj = {
__proto__: null
};
var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
for(var key in obj){
if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
if (desc && (desc.get || desc.set)) {
Object.defineProperty(newObj, key, desc);
} else {
newObj[key] = obj[key];
}
}
}
newObj.default = obj;
if (cache) {
cache.set(obj, newObj);
}
return newObj;
}
const debug = require('debug')('expo:start:server:middleware:manifest');
const supportedPlatforms = [
'ios',
'android',
'web',
'none'
];
function getEntryWithServerRoot(projectRoot, props) {
if (!supportedPlatforms.includes(props.platform)) {
throw new _errors.CommandError(`Failed to resolve the project's entry file: The platform "${props.platform}" is not supported.`);
}
return (0, _metroOptions.convertPathToModuleSpecifier)(_path().default.relative((0, _paths().getMetroServerRoot)(projectRoot), (0, _paths().resolveEntryPoint)(projectRoot, props)));
}
function resolveMainModuleName(projectRoot, props) {
const entryPoint = getEntryWithServerRoot(projectRoot, props);
debug(`Resolved entry point: ${entryPoint} (project root: ${projectRoot})`);
return (0, _metroOptions.convertPathToModuleSpecifier)((0, _url1.stripExtension)(entryPoint, 'js'));
}
const DEVELOPER_TOOL = 'expo-cli';
class ManifestMiddleware extends _ExpoMiddleware.ExpoMiddleware {
constructor(projectRoot, options){
super(projectRoot, /**
* Only support `/`, `/manifest`, `/index.exp` for the manifest middleware.
*/ [
'/',
'/manifest',
'/index.exp'
]), this.projectRoot = projectRoot, this.options = options;
this.initialProjectConfig = (0, _config().getConfig)(projectRoot);
this.platformBundlers = (0, _platformBundlers.getPlatformBundlers)(projectRoot, this.initialProjectConfig.exp);
}
/** Exposed for testing. */ async _resolveProjectSettingsAsync({ platform, hostname, protocol }) {
var _projectConfig_exp_experiments;
// Read the config
const projectConfig = (0, _config().getConfig)(this.projectRoot);
// Read from headers
const mainModuleName = this.resolveMainModuleName({
pkg: projectConfig.pkg,
platform
});
const isHermesEnabled = (0, _exportHermes.isEnableHermesManaged)(projectConfig.exp, platform);
// Create the manifest and set fields within it
const expoGoConfig = this.getExpoGoConfig({
mainModuleName,
hostname
});
const hostUri = this.options.constructUrl({
scheme: '',
hostname
});
const bundleUrl = this._getBundleUrl({
platform,
mainModuleName,
hostname,
engine: isHermesEnabled ? 'hermes' : undefined,
baseUrl: (0, _metroOptions.getBaseUrlFromExpoConfig)(projectConfig.exp),
asyncRoutes: (0, _metroOptions.getAsyncRoutesFromExpoConfig)(projectConfig.exp, this.options.mode ?? 'development', platform),
routerRoot: (0, _router.getRouterDirectoryModuleIdWithManifest)(this.projectRoot, projectConfig.exp),
protocol,
reactCompiler: !!((_projectConfig_exp_experiments = projectConfig.exp.experiments) == null ? void 0 : _projectConfig_exp_experiments.reactCompiler)
});
// Resolve all assets and set them on the manifest as URLs
await this.mutateManifestWithAssetsAsync(projectConfig.exp, bundleUrl);
return {
expoGoConfig,
hostUri,
bundleUrl,
exp: projectConfig.exp
};
}
/** Get the main entry module ID (file) relative to the project root. */ resolveMainModuleName(props) {
let entryPoint = getEntryWithServerRoot(this.projectRoot, props);
debug(`Resolved entry point: ${entryPoint} (project root: ${this.projectRoot})`);
// NOTE(Bacon): Webpack is currently hardcoded to index.bundle on native
// in the future (TODO) we should move this logic into a Webpack plugin and use
// a generated file name like we do on web.
// const server = getDefaultDevServer();
// // TODO: Move this into BundlerDevServer and read this info from self.
// const isNativeWebpack = server instanceof WebpackBundlerDevServer && server.isTargetingNative();
if (this.options.isNativeWebpack) {
entryPoint = 'index.js';
}
return (0, _url1.stripExtension)(entryPoint, 'js');
}
/** Store device IDs that were sent in the request headers. */ async saveDevicesAsync(req) {
var _req_headers;
const deviceIds = (_req_headers = req.headers) == null ? void 0 : _req_headers['expo-dev-client-id'];
if (deviceIds) {
await _devices.saveDevicesAsync(this.projectRoot, deviceIds).catch((e)=>_log.exception(e));
}
}
/** Create the bundle URL (points to the single JS entry file). Exposed for testing. */ _getBundleUrl({ platform, mainModuleName, hostname, engine, baseUrl, isExporting, asyncRoutes, routerRoot, protocol, reactCompiler }) {
const path = (0, _metroOptions.createBundleUrlPath)({
mode: this.options.mode ?? 'development',
minify: this.options.minify,
platform,
mainModuleName,
lazy: (0, _metroOptions.shouldEnableAsyncImports)(this.projectRoot),
engine,
bytecode: engine === 'hermes',
baseUrl,
isExporting: !!isExporting,
asyncRoutes,
routerRoot,
reactCompiler
});
return this.options.constructUrl({
scheme: protocol ?? 'http',
// hostType: this.options.location.hostType,
hostname
}) + path;
}
getExpoGoConfig({ mainModuleName, hostname }) {
return {
// localhost:8081
debuggerHost: this.options.constructUrl({
scheme: '',
hostname
}),
// Required for Expo Go to function.
developer: {
tool: DEVELOPER_TOOL,
projectRoot: this.projectRoot
},
packagerOpts: {
// Required for dev client.
dev: this.options.mode !== 'production'
},
// Indicates the name of the main bundle.
mainModuleName,
// Add this string to make Flipper register React Native / Metro as "running".
// Can be tested by running:
// `METRO_SERVER_PORT=8081 open -a flipper.app`
// Where 8081 is the port where the Expo project is being hosted.
__flipperHack: 'React Native packager is running'
};
}
/** Resolve all assets and set them on the manifest as URLs */ async mutateManifestWithAssetsAsync(manifest, bundleUrl) {
await (0, _resolveAssets.resolveManifestAssets)(this.projectRoot, {
manifest,
resolver: async (path)=>{
if (this.options.isNativeWebpack) {
// When using our custom dev server, just do assets normally
// without the `assets/` subpath redirect.
return (0, _url().resolve)(bundleUrl.match(/^https?:\/\/.*?\//)[0], path);
}
return bundleUrl.match(/^https?:\/\/.*?\//)[0] + 'assets/' + path;
}
});
// The server normally inserts this but if we're offline we'll do it here
await (0, _resolveAssets.resolveGoogleServicesFile)(this.projectRoot, manifest);
}
getWebBundleUrl() {
const platform = 'web';
// Read from headers
const mainModuleName = this.resolveMainModuleName({
pkg: this.initialProjectConfig.pkg,
platform
});
return (0, _metroOptions.createBundleUrlPathFromExpoConfig)(this.projectRoot, this.initialProjectConfig.exp, {
platform,
mainModuleName,
minify: this.options.minify,
lazy: (0, _metroOptions.shouldEnableAsyncImports)(this.projectRoot),
mode: this.options.mode ?? 'development',
// Hermes doesn't support more modern JS features than most, if not all, modern browser.
engine: 'hermes',
isExporting: false,
bytecode: false
});
}
/**
* Web platforms should create an index.html response using the same script resolution as native.
*
* Instead of adding a `bundleUrl` to a `manifest.json` (native) we'll add a `<script src="">`
* to an `index.html`, this enables the web platform to load JavaScript from the server.
*/ async handleWebRequestAsync(req, res) {
res.setHeader('Content-Type', 'text/html');
res.end(await this.getSingleHtmlTemplateAsync());
}
getSingleHtmlTemplateAsync() {
// Read from headers
const bundleUrl = this.getWebBundleUrl();
return (0, _webTemplate.createTemplateHtmlFromExpoConfigAsync)(this.projectRoot, {
exp: this.initialProjectConfig.exp,
scripts: [
bundleUrl
]
});
}
/** Exposed for testing. */ async checkBrowserRequestAsync(req, res, next) {
var _this_initialProjectConfig_exp_platforms;
if (this.platformBundlers.web === 'metro' && ((_this_initialProjectConfig_exp_platforms = this.initialProjectConfig.exp.platforms) == null ? void 0 : _this_initialProjectConfig_exp_platforms.includes('web'))) {
// NOTE(EvanBacon): This effectively disables the safety check we do on custom runtimes to ensure
// the `expo-platform` header is included. When `web.bundler=web`, if the user has non-standard Expo
// code loading then they'll get a web bundle without a clear assertion of platform support.
const platform = (0, _resolvePlatform.parsePlatformHeader)(req);
// On web, serve the public folder
if (!platform || platform === 'web') {
var _this_initialProjectConfig_exp_web;
if ([
'static',
'server'
].includes(((_this_initialProjectConfig_exp_web = this.initialProjectConfig.exp.web) == null ? void 0 : _this_initialProjectConfig_exp_web.output) ?? '')) {
// Skip the spa-styled index.html when static generation is enabled.
next();
return true;
} else {
await this.handleWebRequestAsync(req, res);
return true;
}
}
}
return false;
}
async handleRequestAsync(req, res, next) {
// First check for standard JavaScript runtimes (aka legacy browsers like Chrome).
if (await this.checkBrowserRequestAsync(req, res, next)) {
return;
}
// Save device IDs for dev client.
await this.saveDevicesAsync(req);
// Read from headers
const options = this.getParsedHeaders(req);
const { body, headers } = await this._getManifestResponseAsync(options);
for (const [headerName, headerValue] of headers){
res.setHeader(headerName, headerValue);
}
res.end(body);
}
}
//# sourceMappingURL=ManifestMiddleware.js.map
;