Skip to content

Commit 86be09c

Browse files
committed
Add Scratch.importDependency
See TurboWarp/extensions#2267
1 parent bf7ae82 commit 86be09c

File tree

2 files changed

+99
-1
lines changed

2 files changed

+99
-1
lines changed

src/extension-support/tw-extension-api-common.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@ const BlockType = require('./block-type');
33
const BlockShape = require('./tw-block-shape');
44
const TargetType = require('./target-type');
55
const Cast = require('../util/cast');
6+
const importDependency = require('./tw-import-dependency');
67

78
const Scratch = {
89
ArgumentType,
910
BlockType,
1011
BlockShape,
1112
TargetType,
12-
Cast
13+
Cast,
14+
importDependency
1315
};
1416

1517
module.exports = Scratch;
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/**
2+
* @param {string} url
3+
* @returns {void} if URL is supported
4+
* @throws if URL is unsupported
5+
*/
6+
const checkURL = url => {
7+
// URL might be a very long data: URL, so try to avoid fully parsing it if we can.
8+
// The notable requirement here is that the URL must be an absolute URL, not something
9+
// relative to where the extension is loaded from or where the extension is running.
10+
// This ensures that the same extension file will always load resources from the same
11+
// place, regardless of how it is running or packaged or whatever else.
12+
if (
13+
!url.startsWith('http:') &&
14+
!url.startsWith('https:') &&
15+
!url.startsWith('data:') &&
16+
!url.startsWith('blob:')
17+
) {
18+
throw new Error(`Unsupported URL: ${url}`);
19+
}
20+
};
21+
22+
const importDependency = {};
23+
24+
/**
25+
* @param {string} url
26+
* @template T
27+
* @returns {Promise<T>}
28+
*/
29+
importDependency.asModule = url => {
30+
checkURL(url);
31+
// Need to specify webpackIgnore so that webpack compiles this directly to a call to import()
32+
// instead of trying making it try to use the webpack dependency system.
33+
return import(/* webpackIgnore: true */ url);
34+
};
35+
36+
/**
37+
* @param {string} url
38+
* @returns {Promise<Response>|Response}
39+
*/
40+
importDependency.asFetch = url => {
41+
checkURL(url);
42+
return fetch(url);
43+
};
44+
45+
/**
46+
* @param {string} url
47+
* @returns {Promise<string>|string}
48+
*/
49+
importDependency.asDataURL = async url => {
50+
checkURL(url);
51+
const res = await fetch(url);
52+
const blob = await res.blob();
53+
return new Promise((resolve, reject) => {
54+
const fr = new FileReader();
55+
fr.onload = () => resolve(fr.result);
56+
fr.onerror = () => reject(fr.error);
57+
fr.readAsDataURL(blob);
58+
});
59+
};
60+
61+
/**
62+
* @param {string} url
63+
* @returns {Promise<void>}
64+
*/
65+
importDependency.asScriptTag = url => {
66+
checkURL(url);
67+
return new Promise((resolve, reject) => {
68+
const script = document.createElement('script');
69+
script.onload = () => resolve();
70+
script.onerror = () => reject(new Error('Script error'));
71+
script.src = url;
72+
return script;
73+
});
74+
};
75+
76+
/**
77+
* @param {string} url
78+
* @param {string} returnExpression
79+
* @template T
80+
* @returns {Promise<T>|T}
81+
*/
82+
importDependency.asEval = async (url, returnExpression) => {
83+
checkURL(url);
84+
85+
const res = await fetch(url);
86+
if (!res.ok) {
87+
throw new Error(`HTTP ${res.status} fetching ${url}`);
88+
}
89+
90+
const text = await res.text();
91+
const js = `${text};return ${returnExpression}`;
92+
const fn = new Function(js);
93+
return fn();
94+
};
95+
96+
module.exports = importDependency;

0 commit comments

Comments
 (0)