Repository URL to install this package:
|
Version:
1.2.7 ▾
|
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _react = _interopRequireDefault(require("react"));
var _exotic = require("../../exotic");
var _propTypes = _interopRequireDefault(require("prop-types"));
var _jsxFileName = "SSR/async-component/asyncComponent.js";
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
const validSSRModes = ['resolve', 'defer', 'boundary']; // eslint-disable-next-line
function asyncComponent(config) {
const name = config.name,
resolve = config.resolve,
_config$autoResolveES = config.autoResolveES2015Default,
autoResolveES2015Default = _config$autoResolveES === void 0 ? true : _config$autoResolveES,
_config$serverMode = config.serverMode,
serverMode = _config$serverMode === void 0 ? 'resolve' : _config$serverMode,
LoadingComponent = config.LoadingComponent,
ErrorComponent = config.ErrorComponent;
if (validSSRModes.indexOf(serverMode) === -1) {
throw new Error('Invalid serverMode provided to asyncComponent');
}
const env = ['node', 'browser'].indexOf(config.env) > -1 ? config.env : typeof window === 'undefined' ? 'node' : 'browser';
const sharedState = {
// A unique id we will assign to our async component which is especially
// useful when rehydrating server side rendered async components.
id: null,
// This will be use to hold the resolved module allowing sharing across
// instances.
// NOTE: When using React Hot Loader this reference will become null.
module: null,
// If an error occurred during a resolution it will be stored here.
error: null,
// Allows us to share the resolver promise across instances.
resolver: null // Takes the given module and if it has a ".default" the ".default" will
// be returned. i.e. handy when you could be dealing with es6 imports.
};
const es6Resolve = x => autoResolveES2015Default && (0, _exotic.isObj)(x) && x.default ? x.default : x;
const getResolver = () => {
if (sharedState.resolver == null) {
try {
// Wrap whatever the user returns in Promise.resolve to ensure a Promise
// is always returned.
const resolver = resolve();
sharedState.resolver = Promise.resolve(resolver);
} catch (resolverException) {
sharedState.resolver = Promise.reject(resolverException);
}
}
return sharedState.resolver;
};
let AsyncComponent = class AsyncComponent extends _react.default.Component {
constructor(props, context) {
super(props, context); // We have to set the id in the constructor because a RHL seems
// to recycle the module and therefore the id closure will be null.
// We can't put it in componentWillMount as RHL hot swaps the new code
// so the mount call will not happen (but the ctor does).
if (this.context.asyncComponents && !sharedState.id) {
sharedState.id = this.context.asyncComponents.getNextId();
}
} // @see react-async-bootstrapper
asyncBootstrap() {
const _context = this.context,
asyncComponents = _context.asyncComponents,
asyncComponentsAncestor = _context.asyncComponentsAncestor;
const shouldRehydrate = asyncComponents.shouldRehydrate;
const doResolve = () => this.resolveModule().then(module => module !== undefined);
if (env === 'browser') {
return shouldRehydrate(sharedState.id) ? doResolve() : false;
} // node
const isChildOfBoundary = asyncComponentsAncestor && asyncComponentsAncestor.isBoundary;
return serverMode === 'defer' || isChildOfBoundary ? false : doResolve();
}
getChildContext() {
if (!this.context.asyncComponents) {
return undefined;
}
return {
asyncComponentsAncestor: {
isBoundary: serverMode === 'boundary'
}
};
}
componentWillMount() {
this.setState({
module: sharedState.module
});
if (sharedState.error) {
this.registerErrorState(sharedState.error);
}
}
componentDidMount() {
if (!this.state.module) {
this.resolveModule();
}
}
resolveModule() {
this.resolving = true;
return getResolver().then(module => {
if (this.unmounted) {
return undefined;
}
if (this.context.asyncComponents) {
this.context.asyncComponents.resolved(sharedState.id);
}
sharedState.module = module;
if (env === 'browser') {
this.setState({
module
});
}
this.resolving = false;
return module;
}).catch(error => {
if (this.unmounted) {
return undefined;
}
if (env === 'node' || env === 'browser' && !ErrorComponent) {
// We will at least log the error so that user isn't completely
// unaware of an error occurring.
// eslint-disable-next-line no-console
console.warn('Failed to resolve asyncComponent'); // eslint-disable-next-line no-console
console.warn(error);
}
sharedState.error = error;
this.registerErrorState(error);
this.resolving = false;
return undefined;
});
}
componentWillUnmount() {
this.unmounted = true;
}
registerErrorState(error) {
if (env === 'browser') {
setTimeout(() => {
if (!this.unmounted) {
this.setState({
error
});
}
}, 16);
}
}
render() {
const _state = this.state,
module = _state.module,
error = _state.error; // This is as workaround for React Hot Loader support. When using
// RHL the local component reference will be killed by any change
// to the component, this will be our signal to know that we need to
// re-resolve it.
if (sharedState.module == null && !this.resolving && typeof window !== 'undefined') {
this.resolveModule();
}
if (error) {
return ErrorComponent ? _react.default.createElement(ErrorComponent, _extends({}, this.props, {
error: error,
__source: {
fileName: _jsxFileName,
lineNumber: 197
},
__self: this
})) : null;
}
const Component = es6Resolve(module);
return Component ? _react.default.createElement(Component, _extends({}, this.props, {
__source: {
fileName: _jsxFileName,
lineNumber: 203
},
__self: this
})) : LoadingComponent ? _react.default.createElement(LoadingComponent, _extends({}, this.props, {
__source: {
fileName: _jsxFileName,
lineNumber: 204
},
__self: this
})) : null;
}
};
AsyncComponent.displayName = name || 'AsyncComponent';
AsyncComponent.contextTypes = {
asyncComponentsAncestor: _propTypes.default.shape({
isBoundary: _propTypes.default.bool
}),
asyncComponents: _propTypes.default.shape({
getNextId: _propTypes.default.func.isRequired,
resolved: _propTypes.default.func.isRequired,
shouldRehydrate: _propTypes.default.func.isRequired
})
};
AsyncComponent.childContextTypes = {
asyncComponentsAncestor: _propTypes.default.shape({
isBoundary: _propTypes.default.bool
})
};
return AsyncComponent;
}
var _default = asyncComponent;
exports.default = _default;