main
js 182 lines 5.17 KB
Raw
1 import semver from 'semver';
2
3 /**
4 * Take a version from the window query string and load a specific
5 * version of React.
6 *
7 * @example
8 * http://localhost:3000?version=15.4.1
9 * (Loads React 15.4.1)
10 */
11
12 function parseQuery(qstr) {
13 var query = {};
14 var a = qstr.slice(1).split('&');
15
16 for (var i = 0; i < a.length; i++) {
17 var b = a[i].split('=');
18 query[decodeURIComponent(b[0])] = decodeURIComponent(b[1] || '');
19 }
20 return query;
21 }
22
23 function loadScript(src) {
24 let firstScript = document.getElementsByTagName('script')[0];
25 let scriptNode;
26
27 return new Promise((resolve, reject) => {
28 scriptNode = document.createElement('script');
29 scriptNode.async = 1;
30 scriptNode.src = src;
31
32 scriptNode.onload = () => resolve();
33 scriptNode.onerror = () => reject(new Error(`failed to load: ${src}`));
34
35 firstScript.parentNode.insertBefore(scriptNode, firstScript);
36 });
37 }
38
39 function loadModules(SymbolSrcPairs) {
40 let firstScript = document.getElementsByTagName('script')[0];
41
42 let imports = '';
43 SymbolSrcPairs.map(([symbol, src]) => {
44 imports += `import ${symbol} from "${src}";\n`;
45 imports += `window.${symbol} = ${symbol};\n`;
46 });
47
48 return new Promise((resolve, reject) => {
49 const timeout = setTimeout(
50 () => reject(new Error('Timed out loading react modules over esm')),
51 5000
52 );
53 window.__loaded = () => {
54 clearTimeout(timeout);
55 resolve();
56 };
57
58 const moduleScript = document.createElement('script');
59 moduleScript.type = 'module';
60 moduleScript.textContent = imports + 'window.__loaded();';
61
62 firstScript.parentNode.insertBefore(moduleScript, firstScript);
63 });
64 }
65
66 function getVersion() {
67 let query = parseQuery(window.location.search);
68 return query.version || 'local';
69 }
70
71 export function isLocal() {
72 return getVersion() === 'local';
73 }
74
75 export function reactPaths(version = getVersion()) {
76 let query = parseQuery(window.location.search);
77 let isProduction = query.production === 'true';
78 let environment = isProduction ? 'production.min' : 'development';
79 let reactPath = `react.${environment}.js`;
80 let reactDOMPath = `react-dom.${environment}.js`;
81 let reactDOMClientPath = `react-dom.${environment}.js`;
82 let reactDOMServerPath = `react-dom-server.browser.${environment}.js`;
83 let needsCreateElement = true;
84 let needsReactDOM = true;
85 let usingModules = false;
86
87 if (version !== 'local') {
88 const {major, minor, prerelease} = semver(version);
89 console.log('semver', semver(version));
90
91 if (major === 0) {
92 needsCreateElement = minor >= 12;
93 needsReactDOM = minor >= 14;
94 }
95
96 const [preReleaseStage] = prerelease;
97 // The file structure was updated in 16. This wasn't the case for alphas.
98 // Load the old module location for anything less than 16 RC
99 if (major >= 19) {
100 usingModules = true;
101 const devQuery = environment === 'development' ? '?dev' : '';
102 reactPath = 'https://esm.sh/react@' + version + '/' + devQuery;
103 reactDOMPath = 'https://esm.sh/react-dom@' + version + '/' + devQuery;
104 reactDOMClientPath =
105 'https://esm.sh/react-dom@' + version + '/client' + devQuery;
106 reactDOMServerPath =
107 'https://esm.sh/react-dom@' + version + '/server.browser' + devQuery;
108 } else if (major >= 16 && !(minor === 0 && preReleaseStage === 'alpha')) {
109 reactPath =
110 'https://unpkg.com/react@' +
111 version +
112 '/umd/react.' +
113 environment +
114 '.js';
115 reactDOMPath =
116 'https://unpkg.com/react-dom@' +
117 version +
118 '/umd/react-dom.' +
119 environment +
120 '.js';
121 reactDOMServerPath =
122 'https://unpkg.com/react-dom@' +
123 version +
124 '/umd/react-dom-server.browser' +
125 environment;
126 } else if (major > 0 || minor > 11) {
127 reactPath = 'https://unpkg.com/react@' + version + '/dist/react.js';
128 reactDOMPath =
129 'https://unpkg.com/react-dom@' + version + '/dist/react-dom.js';
130 reactDOMServerPath =
131 'https://unpkg.com/react-dom@' + version + '/dist/react-dom-server.js';
132 } else {
133 reactPath =
134 'https://cdnjs.cloudflare.com/ajax/libs/react/' + version + '/react.js';
135 }
136 } else {
137 throw new Error(
138 'This fixture no longer works with local versions. Provide a version query parameter that matches a version published to npm to use the fixture.'
139 );
140 }
141
142 return {
143 reactPath,
144 reactDOMPath,
145 reactDOMClientPath,
146 reactDOMServerPath,
147 needsCreateElement,
148 needsReactDOM,
149 usingModules,
150 };
151 }
152
153 export default function loadReact() {
154 console.log('reactPaths', reactPaths());
155 const {
156 reactPath,
157 reactDOMPath,
158 reactDOMClientPath,
159 needsReactDOM,
160 usingModules,
161 } = reactPaths();
162
163 if (usingModules) {
164 return loadModules([
165 ['React', reactPath],
166 ['ReactDOM', reactDOMPath],
167 ['ReactDOMClient', reactDOMClientPath],
168 ]);
169 } else {
170 let request = loadScript(reactPath, usingModules);
171
172 if (needsReactDOM) {
173 request = request.then(() => loadScript(reactDOMPath, usingModules));
174 } else {
175 // Aliasing React to ReactDOM for compatibility.
176 request = request.then(() => {
177 window.ReactDOM = window.React;
178 });
179 }
180 return request;
181 }
182 }