@garfish/loader
Advanced tools
Comparing version 0.0.58 to 0.0.59-alpha.0.2
@@ -1,2 +0,2 @@ | ||
import { PluginManager } from './pluginSystem'; | ||
import { LoaderPlugin } from '@garfish/hooks'; | ||
import { FileTypes } from './appCache'; | ||
@@ -37,11 +37,9 @@ import { StyleManager } from './managers/style'; | ||
JavaScriptManager: typeof JavaScriptManager; | ||
/** | ||
* @deprecated | ||
*/ | ||
personalId: symbol; | ||
/** @deprecated */ | ||
requestConfig: RequestInit | ((url: string) => RequestInit); | ||
personalId: symbol; | ||
lifecycle: { | ||
clear: PluginManager<ClearPluginArgs>; | ||
loaded: PluginManager<LoadedPluginArgs<Manager>>; | ||
beforeLoad: PluginManager<BeforeLoadPluginArgs>; | ||
clear: LoaderPlugin<ClearPluginArgs>; | ||
loaded: LoaderPlugin<LoadedPluginArgs<Manager>>; | ||
beforeLoad: LoaderPlugin<BeforeLoadPluginArgs>; | ||
}; | ||
@@ -48,0 +46,0 @@ private options; |
@@ -5,27 +5,2 @@ 'use strict'; | ||
/*! ***************************************************************************** | ||
Copyright (c) Microsoft Corporation. | ||
Permission to use, copy, modify, and/or distribute this software for any | ||
purpose with or without fee is hereby granted. | ||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH | ||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY | ||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, | ||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM | ||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR | ||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR | ||
PERFORMANCE OF THIS SOFTWARE. | ||
***************************************************************************** */ | ||
function __awaiter(thisArg, _arguments, P, generator) { | ||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
} | ||
const objectToString = Object.prototype.toString; | ||
@@ -65,2 +40,9 @@ function isPlainObject(val) { | ||
}; | ||
function warn(msg) { | ||
processError(msg, (e, isString) => { | ||
const warnMsg = isString ? e : e.message; | ||
if (false) ; | ||
console.warn(warnMsg); | ||
}); | ||
} | ||
function error(error) { | ||
@@ -76,2 +58,5 @@ processError(error, (e, isString) => { | ||
} | ||
// 有些测试 jest.mock 不好测,可用这个工具方法 | ||
function callTestCallback(obj, ...args) { | ||
} | ||
// 数组去重,不保证顺序 | ||
@@ -219,4 +204,2 @@ function unique(list) { | ||
const __ELEMENT_DELETE_TAG__ = '__ELEMENT_DELETE_TAG__'; | ||
const xChar = 120; // "x" char | ||
@@ -314,3 +297,2 @@ const colonChar = 58; // ":" char | ||
parentNode.removeChild(el); | ||
el[__ELEMENT_DELETE_TAG__] = true; | ||
} | ||
@@ -461,2 +443,27 @@ } | ||
/*! ***************************************************************************** | ||
Copyright (c) Microsoft Corporation. | ||
Permission to use, copy, modify, and/or distribute this software for any | ||
purpose with or without fee is hereby granted. | ||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH | ||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY | ||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, | ||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM | ||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR | ||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR | ||
PERFORMANCE OF THIS SOFTWARE. | ||
***************************************************************************** */ | ||
function __awaiter(thisArg, _arguments, P, generator) { | ||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
} | ||
var ElementType; | ||
@@ -539,39 +546,27 @@ (function (ElementType) { | ||
const ONCE_FLAG = Symbol('once'); | ||
const REGISTER_PLUGINS = new Set(); | ||
class PluginManager { | ||
class LoaderPlugin { | ||
constructor(type) { | ||
if (REGISTER_PLUGINS.has(type)) { | ||
throw new Error(`"${type}" has been created.`); | ||
} | ||
this.type = type; | ||
this.onerror = error; | ||
this.plugins = new Set(); | ||
this.type = type; | ||
} | ||
add(plugin) { | ||
on(plugin) { | ||
if (this.plugins.has(plugin)) { | ||
console.error('repeat add plugin.'); | ||
return false; | ||
warn('Repeat add plugin.'); | ||
return; | ||
} | ||
this.plugins.add(plugin); | ||
return true; | ||
} | ||
addOnce(plugin) { | ||
plugin._onceFlag = ONCE_FLAG; | ||
return this.add(plugin); | ||
once(plugin) { | ||
const self = this; | ||
return this.once(function wrapper(...args) { | ||
self.remove(wrapper); | ||
return plugin.apply(null, args); | ||
}); | ||
} | ||
remove(plugin) { | ||
if (this.plugins.has(plugin)) { | ||
this.plugins.delete(plugin); | ||
return true; | ||
} | ||
return false; | ||
} | ||
run(result) { | ||
for (const fn of this.plugins) { | ||
emit(result) { | ||
for (const plugin of this.plugins) { | ||
try { | ||
let illegalResult = false; | ||
const tempResult = fn(result); | ||
if (fn._onceFlag === ONCE_FLAG) | ||
this.remove(fn); | ||
const tempResult = plugin(result); | ||
for (const key in result) { | ||
@@ -583,9 +578,12 @@ if (!hasOwn(key, tempResult)) { | ||
} | ||
illegalResult | ||
? this.onerror(`The "${this.type}" type has a plugin return value error.`) | ||
: (result = tempResult); | ||
if (illegalResult) { | ||
this.onerror(`The "${this.type}" type has a plugin return value error.`); | ||
} | ||
else { | ||
result = tempResult; | ||
} | ||
} | ||
catch (err) { | ||
warn(err); | ||
this.onerror(err); | ||
console.error(err); | ||
} | ||
@@ -595,2 +593,10 @@ } | ||
} | ||
remove(plugin) { | ||
if (this.plugins.has(plugin)) { | ||
this.plugins.delete(plugin); | ||
} | ||
} | ||
removeAll() { | ||
this.plugins.clear(); | ||
} | ||
} | ||
@@ -971,5 +977,5 @@ | ||
this.lifecycle = { | ||
clear: new PluginManager('clear'), | ||
loaded: new PluginManager('loaded'), | ||
beforeLoad: new PluginManager('beforeLoad'), | ||
clear: new LoaderPlugin('clear'), | ||
loaded: new LoaderPlugin('loaded'), | ||
beforeLoad: new LoaderPlugin('beforeLoad'), | ||
}; | ||
@@ -984,3 +990,3 @@ this.options = options || {}; | ||
appCacheContainer.clear(fileType); | ||
this.lifecycle.clear.run({ scope, fileType }); | ||
this.lifecycle.clear.emit({ scope, fileType }); | ||
} | ||
@@ -1024,3 +1030,3 @@ } | ||
const requestConfig = mergeConfig(this, url); | ||
const resOpts = this.lifecycle.beforeLoad.run({ url, requestConfig }); | ||
const resOpts = this.lifecycle.beforeLoad.emit({ url, requestConfig }); | ||
loadingList[url] = request(resOpts.url, resOpts.requestConfig) | ||
@@ -1030,3 +1036,3 @@ .finally(() => { | ||
}) | ||
.then(({ code, mimeType, result }) => __awaiter(this, void 0, void 0, function* () { | ||
.then(({ code, mimeType, result }) => { | ||
let managerCtor, fileType; | ||
@@ -1055,3 +1061,3 @@ if (isModule) { | ||
// So, you can transform the request result. | ||
const data = this.lifecycle.loaded.run({ | ||
const data = this.lifecycle.loaded.emit({ | ||
result, | ||
@@ -1067,3 +1073,3 @@ value: { | ||
return copyResult(data.value); | ||
})); | ||
}); | ||
return loadingList[url]; | ||
@@ -1070,0 +1076,0 @@ } |
@@ -5,27 +5,2 @@ 'use strict'; | ||
/*! ***************************************************************************** | ||
Copyright (c) Microsoft Corporation. | ||
Permission to use, copy, modify, and/or distribute this software for any | ||
purpose with or without fee is hereby granted. | ||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH | ||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY | ||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, | ||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM | ||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR | ||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR | ||
PERFORMANCE OF THIS SOFTWARE. | ||
***************************************************************************** */ | ||
function __awaiter(thisArg, _arguments, P, generator) { | ||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
} | ||
const objectToString = Object.prototype.toString; | ||
@@ -217,4 +192,2 @@ function isPlainObject(val) { | ||
const __ELEMENT_DELETE_TAG__ = '__ELEMENT_DELETE_TAG__'; | ||
const xChar = 120; // "x" char | ||
@@ -312,3 +285,2 @@ const colonChar = 58; // ":" char | ||
parentNode.removeChild(el); | ||
el[__ELEMENT_DELETE_TAG__] = true; | ||
} | ||
@@ -459,2 +431,27 @@ } | ||
/*! ***************************************************************************** | ||
Copyright (c) Microsoft Corporation. | ||
Permission to use, copy, modify, and/or distribute this software for any | ||
purpose with or without fee is hereby granted. | ||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH | ||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY | ||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, | ||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM | ||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR | ||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR | ||
PERFORMANCE OF THIS SOFTWARE. | ||
***************************************************************************** */ | ||
function __awaiter(thisArg, _arguments, P, generator) { | ||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
} | ||
var ElementType; | ||
@@ -537,38 +534,26 @@ (function (ElementType) { | ||
const ONCE_FLAG = Symbol('once'); | ||
const REGISTER_PLUGINS = new Set(); | ||
class PluginManager { | ||
class LoaderPlugin { | ||
constructor(type) { | ||
if (REGISTER_PLUGINS.has(type)) { | ||
throw new Error(`"${type}" has been created.`); | ||
} | ||
this.type = type; | ||
this.onerror = error; | ||
this.plugins = new Set(); | ||
this.type = type; | ||
} | ||
add(plugin) { | ||
on(plugin) { | ||
if (this.plugins.has(plugin)) { | ||
return false; | ||
return; | ||
} | ||
this.plugins.add(plugin); | ||
return true; | ||
} | ||
addOnce(plugin) { | ||
plugin._onceFlag = ONCE_FLAG; | ||
return this.add(plugin); | ||
once(plugin) { | ||
const self = this; | ||
return this.once(function wrapper(...args) { | ||
self.remove(wrapper); | ||
return plugin.apply(null, args); | ||
}); | ||
} | ||
remove(plugin) { | ||
if (this.plugins.has(plugin)) { | ||
this.plugins.delete(plugin); | ||
return true; | ||
} | ||
return false; | ||
} | ||
run(result) { | ||
for (const fn of this.plugins) { | ||
emit(result) { | ||
for (const plugin of this.plugins) { | ||
try { | ||
let illegalResult = false; | ||
const tempResult = fn(result); | ||
if (fn._onceFlag === ONCE_FLAG) | ||
this.remove(fn); | ||
const tempResult = plugin(result); | ||
for (const key in result) { | ||
@@ -580,5 +565,8 @@ if (!hasOwn(key, tempResult)) { | ||
} | ||
illegalResult | ||
? this.onerror(`The "${this.type}" type has a plugin return value error.`) | ||
: (result = tempResult); | ||
if (illegalResult) { | ||
this.onerror(`The "${this.type}" type has a plugin return value error.`); | ||
} | ||
else { | ||
result = tempResult; | ||
} | ||
} | ||
@@ -591,2 +579,10 @@ catch (err) { | ||
} | ||
remove(plugin) { | ||
if (this.plugins.has(plugin)) { | ||
this.plugins.delete(plugin); | ||
} | ||
} | ||
removeAll() { | ||
this.plugins.clear(); | ||
} | ||
} | ||
@@ -967,5 +963,5 @@ | ||
this.lifecycle = { | ||
clear: new PluginManager('clear'), | ||
loaded: new PluginManager('loaded'), | ||
beforeLoad: new PluginManager('beforeLoad'), | ||
clear: new LoaderPlugin('clear'), | ||
loaded: new LoaderPlugin('loaded'), | ||
beforeLoad: new LoaderPlugin('beforeLoad'), | ||
}; | ||
@@ -980,3 +976,3 @@ this.options = options || {}; | ||
appCacheContainer.clear(fileType); | ||
this.lifecycle.clear.run({ scope, fileType }); | ||
this.lifecycle.clear.emit({ scope, fileType }); | ||
} | ||
@@ -1020,3 +1016,3 @@ } | ||
const requestConfig = mergeConfig(this, url); | ||
const resOpts = this.lifecycle.beforeLoad.run({ url, requestConfig }); | ||
const resOpts = this.lifecycle.beforeLoad.emit({ url, requestConfig }); | ||
loadingList[url] = request(resOpts.url, resOpts.requestConfig) | ||
@@ -1026,3 +1022,3 @@ .finally(() => { | ||
}) | ||
.then(({ code, mimeType, result }) => __awaiter(this, void 0, void 0, function* () { | ||
.then(({ code, mimeType, result }) => { | ||
let managerCtor, fileType; | ||
@@ -1051,3 +1047,3 @@ if (isModule) { | ||
// So, you can transform the request result. | ||
const data = this.lifecycle.loaded.run({ | ||
const data = this.lifecycle.loaded.emit({ | ||
result, | ||
@@ -1063,3 +1059,3 @@ value: { | ||
return copyResult(data.value); | ||
})); | ||
}); | ||
return loadingList[url]; | ||
@@ -1066,0 +1062,0 @@ } |
@@ -1,26 +0,1 @@ | ||
/*! ***************************************************************************** | ||
Copyright (c) Microsoft Corporation. | ||
Permission to use, copy, modify, and/or distribute this software for any | ||
purpose with or without fee is hereby granted. | ||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH | ||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY | ||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, | ||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM | ||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR | ||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR | ||
PERFORMANCE OF THIS SOFTWARE. | ||
***************************************************************************** */ | ||
function __awaiter(thisArg, _arguments, P, generator) { | ||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
} | ||
const objectToString = Object.prototype.toString; | ||
@@ -60,2 +35,9 @@ function isPlainObject(val) { | ||
}; | ||
function warn(msg) { | ||
processError(msg, (e, isString) => { | ||
const warnMsg = isString ? e : e.message; | ||
if (false) ; | ||
console.warn(warnMsg); | ||
}); | ||
} | ||
function error(error) { | ||
@@ -71,2 +53,5 @@ processError(error, (e, isString) => { | ||
} | ||
// 有些测试 jest.mock 不好测,可用这个工具方法 | ||
function callTestCallback(obj, ...args) { | ||
} | ||
// 数组去重,不保证顺序 | ||
@@ -214,4 +199,2 @@ function unique(list) { | ||
const __ELEMENT_DELETE_TAG__ = '__ELEMENT_DELETE_TAG__'; | ||
const xChar = 120; // "x" char | ||
@@ -309,3 +292,2 @@ const colonChar = 58; // ":" char | ||
parentNode.removeChild(el); | ||
el[__ELEMENT_DELETE_TAG__] = true; | ||
} | ||
@@ -456,2 +438,27 @@ } | ||
/*! ***************************************************************************** | ||
Copyright (c) Microsoft Corporation. | ||
Permission to use, copy, modify, and/or distribute this software for any | ||
purpose with or without fee is hereby granted. | ||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH | ||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY | ||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, | ||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM | ||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR | ||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR | ||
PERFORMANCE OF THIS SOFTWARE. | ||
***************************************************************************** */ | ||
function __awaiter(thisArg, _arguments, P, generator) { | ||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
} | ||
var ElementType; | ||
@@ -534,39 +541,27 @@ (function (ElementType) { | ||
const ONCE_FLAG = Symbol('once'); | ||
const REGISTER_PLUGINS = new Set(); | ||
class PluginManager { | ||
class LoaderPlugin { | ||
constructor(type) { | ||
if (REGISTER_PLUGINS.has(type)) { | ||
throw new Error(`"${type}" has been created.`); | ||
} | ||
this.type = type; | ||
this.onerror = error; | ||
this.plugins = new Set(); | ||
this.type = type; | ||
} | ||
add(plugin) { | ||
on(plugin) { | ||
if (this.plugins.has(plugin)) { | ||
console.error('repeat add plugin.'); | ||
return false; | ||
warn('Repeat add plugin.'); | ||
return; | ||
} | ||
this.plugins.add(plugin); | ||
return true; | ||
} | ||
addOnce(plugin) { | ||
plugin._onceFlag = ONCE_FLAG; | ||
return this.add(plugin); | ||
once(plugin) { | ||
const self = this; | ||
return this.once(function wrapper(...args) { | ||
self.remove(wrapper); | ||
return plugin.apply(null, args); | ||
}); | ||
} | ||
remove(plugin) { | ||
if (this.plugins.has(plugin)) { | ||
this.plugins.delete(plugin); | ||
return true; | ||
} | ||
return false; | ||
} | ||
run(result) { | ||
for (const fn of this.plugins) { | ||
emit(result) { | ||
for (const plugin of this.plugins) { | ||
try { | ||
let illegalResult = false; | ||
const tempResult = fn(result); | ||
if (fn._onceFlag === ONCE_FLAG) | ||
this.remove(fn); | ||
const tempResult = plugin(result); | ||
for (const key in result) { | ||
@@ -578,9 +573,12 @@ if (!hasOwn(key, tempResult)) { | ||
} | ||
illegalResult | ||
? this.onerror(`The "${this.type}" type has a plugin return value error.`) | ||
: (result = tempResult); | ||
if (illegalResult) { | ||
this.onerror(`The "${this.type}" type has a plugin return value error.`); | ||
} | ||
else { | ||
result = tempResult; | ||
} | ||
} | ||
catch (err) { | ||
warn(err); | ||
this.onerror(err); | ||
console.error(err); | ||
} | ||
@@ -590,2 +588,10 @@ } | ||
} | ||
remove(plugin) { | ||
if (this.plugins.has(plugin)) { | ||
this.plugins.delete(plugin); | ||
} | ||
} | ||
removeAll() { | ||
this.plugins.clear(); | ||
} | ||
} | ||
@@ -966,5 +972,5 @@ | ||
this.lifecycle = { | ||
clear: new PluginManager('clear'), | ||
loaded: new PluginManager('loaded'), | ||
beforeLoad: new PluginManager('beforeLoad'), | ||
clear: new LoaderPlugin('clear'), | ||
loaded: new LoaderPlugin('loaded'), | ||
beforeLoad: new LoaderPlugin('beforeLoad'), | ||
}; | ||
@@ -979,3 +985,3 @@ this.options = options || {}; | ||
appCacheContainer.clear(fileType); | ||
this.lifecycle.clear.run({ scope, fileType }); | ||
this.lifecycle.clear.emit({ scope, fileType }); | ||
} | ||
@@ -1019,3 +1025,3 @@ } | ||
const requestConfig = mergeConfig(this, url); | ||
const resOpts = this.lifecycle.beforeLoad.run({ url, requestConfig }); | ||
const resOpts = this.lifecycle.beforeLoad.emit({ url, requestConfig }); | ||
loadingList[url] = request(resOpts.url, resOpts.requestConfig) | ||
@@ -1025,3 +1031,3 @@ .finally(() => { | ||
}) | ||
.then(({ code, mimeType, result }) => __awaiter(this, void 0, void 0, function* () { | ||
.then(({ code, mimeType, result }) => { | ||
let managerCtor, fileType; | ||
@@ -1050,3 +1056,3 @@ if (isModule) { | ||
// So, you can transform the request result. | ||
const data = this.lifecycle.loaded.run({ | ||
const data = this.lifecycle.loaded.emit({ | ||
result, | ||
@@ -1062,3 +1068,3 @@ value: { | ||
return copyResult(data.value); | ||
})); | ||
}); | ||
return loadingList[url]; | ||
@@ -1065,0 +1071,0 @@ } |
@@ -1,26 +0,1 @@ | ||
/*! ***************************************************************************** | ||
Copyright (c) Microsoft Corporation. | ||
Permission to use, copy, modify, and/or distribute this software for any | ||
purpose with or without fee is hereby granted. | ||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH | ||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY | ||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, | ||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM | ||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR | ||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR | ||
PERFORMANCE OF THIS SOFTWARE. | ||
***************************************************************************** */ | ||
function __awaiter(thisArg, _arguments, P, generator) { | ||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
} | ||
const objectToString = Object.prototype.toString; | ||
@@ -60,2 +35,9 @@ function isPlainObject(val) { | ||
}; | ||
function warn(msg) { | ||
processError(msg, (e, isString) => { | ||
const warnMsg = isString ? e : e.message; | ||
if (false) ; | ||
console.warn(warnMsg); | ||
}); | ||
} | ||
function error(error) { | ||
@@ -71,2 +53,5 @@ processError(error, (e, isString) => { | ||
} | ||
// 有些测试 jest.mock 不好测,可用这个工具方法 | ||
function callTestCallback(obj, ...args) { | ||
} | ||
// 数组去重,不保证顺序 | ||
@@ -214,4 +199,2 @@ function unique(list) { | ||
const __ELEMENT_DELETE_TAG__ = '__ELEMENT_DELETE_TAG__'; | ||
const xChar = 120; // "x" char | ||
@@ -309,3 +292,2 @@ const colonChar = 58; // ":" char | ||
parentNode.removeChild(el); | ||
el[__ELEMENT_DELETE_TAG__] = true; | ||
} | ||
@@ -456,2 +438,27 @@ } | ||
/*! ***************************************************************************** | ||
Copyright (c) Microsoft Corporation. | ||
Permission to use, copy, modify, and/or distribute this software for any | ||
purpose with or without fee is hereby granted. | ||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH | ||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY | ||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, | ||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM | ||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR | ||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR | ||
PERFORMANCE OF THIS SOFTWARE. | ||
***************************************************************************** */ | ||
function __awaiter(thisArg, _arguments, P, generator) { | ||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
} | ||
var ElementType; | ||
@@ -534,39 +541,27 @@ (function (ElementType) { | ||
const ONCE_FLAG = Symbol('once'); | ||
const REGISTER_PLUGINS = new Set(); | ||
class PluginManager { | ||
class LoaderPlugin { | ||
constructor(type) { | ||
if (REGISTER_PLUGINS.has(type)) { | ||
throw new Error(`"${type}" has been created.`); | ||
} | ||
this.type = type; | ||
this.onerror = error; | ||
this.plugins = new Set(); | ||
this.type = type; | ||
} | ||
add(plugin) { | ||
on(plugin) { | ||
if (this.plugins.has(plugin)) { | ||
(process.env.NODE_ENV !== "production") && console.error('repeat add plugin.'); | ||
return false; | ||
(process.env.NODE_ENV !== "production") && warn('Repeat add plugin.'); | ||
return; | ||
} | ||
this.plugins.add(plugin); | ||
return true; | ||
} | ||
addOnce(plugin) { | ||
plugin._onceFlag = ONCE_FLAG; | ||
return this.add(plugin); | ||
once(plugin) { | ||
const self = this; | ||
return this.once(function wrapper(...args) { | ||
self.remove(wrapper); | ||
return plugin.apply(null, args); | ||
}); | ||
} | ||
remove(plugin) { | ||
if (this.plugins.has(plugin)) { | ||
this.plugins.delete(plugin); | ||
return true; | ||
} | ||
return false; | ||
} | ||
run(result) { | ||
for (const fn of this.plugins) { | ||
emit(result) { | ||
for (const plugin of this.plugins) { | ||
try { | ||
let illegalResult = false; | ||
const tempResult = fn(result); | ||
if (fn._onceFlag === ONCE_FLAG) | ||
this.remove(fn); | ||
const tempResult = plugin(result); | ||
for (const key in result) { | ||
@@ -578,9 +573,12 @@ if (!hasOwn(key, tempResult)) { | ||
} | ||
illegalResult | ||
? this.onerror(`The "${this.type}" type has a plugin return value error.`) | ||
: (result = tempResult); | ||
if (illegalResult) { | ||
this.onerror(`The "${this.type}" type has a plugin return value error.`); | ||
} | ||
else { | ||
result = tempResult; | ||
} | ||
} | ||
catch (err) { | ||
(process.env.NODE_ENV !== "production") && warn(err); | ||
this.onerror(err); | ||
(process.env.NODE_ENV !== "production") && console.error(err); | ||
} | ||
@@ -590,2 +588,10 @@ } | ||
} | ||
remove(plugin) { | ||
if (this.plugins.has(plugin)) { | ||
this.plugins.delete(plugin); | ||
} | ||
} | ||
removeAll() { | ||
this.plugins.clear(); | ||
} | ||
} | ||
@@ -966,5 +972,5 @@ | ||
this.lifecycle = { | ||
clear: new PluginManager('clear'), | ||
loaded: new PluginManager('loaded'), | ||
beforeLoad: new PluginManager('beforeLoad'), | ||
clear: new LoaderPlugin('clear'), | ||
loaded: new LoaderPlugin('loaded'), | ||
beforeLoad: new LoaderPlugin('beforeLoad'), | ||
}; | ||
@@ -979,3 +985,3 @@ this.options = options || {}; | ||
appCacheContainer.clear(fileType); | ||
this.lifecycle.clear.run({ scope, fileType }); | ||
this.lifecycle.clear.emit({ scope, fileType }); | ||
} | ||
@@ -1019,3 +1025,3 @@ } | ||
const requestConfig = mergeConfig(this, url); | ||
const resOpts = this.lifecycle.beforeLoad.run({ url, requestConfig }); | ||
const resOpts = this.lifecycle.beforeLoad.emit({ url, requestConfig }); | ||
loadingList[url] = request(resOpts.url, resOpts.requestConfig) | ||
@@ -1025,3 +1031,3 @@ .finally(() => { | ||
}) | ||
.then(({ code, mimeType, result }) => __awaiter(this, void 0, void 0, function* () { | ||
.then(({ code, mimeType, result }) => { | ||
let managerCtor, fileType; | ||
@@ -1050,3 +1056,3 @@ if (isModule) { | ||
// So, you can transform the request result. | ||
const data = this.lifecycle.loaded.run({ | ||
const data = this.lifecycle.loaded.emit({ | ||
result, | ||
@@ -1062,3 +1068,3 @@ value: { | ||
return copyResult(data.value); | ||
})); | ||
}); | ||
return loadingList[url]; | ||
@@ -1065,0 +1071,0 @@ } |
(function (global, factory) { | ||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : | ||
typeof define === 'function' && define.amd ? define(['exports'], factory) : | ||
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.Loader = {})); | ||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : | ||
typeof define === 'function' && define.amd ? define(['exports'], factory) : | ||
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.Loader = {})); | ||
}(this, (function (exports) { 'use strict'; | ||
/*! ***************************************************************************** | ||
Copyright (c) Microsoft Corporation. | ||
const objectToString = Object.prototype.toString; | ||
function isPlainObject(val) { | ||
return objectToString.call(val) === '[object Object]'; | ||
} | ||
const hasOwnProperty = Object.prototype.hasOwnProperty; | ||
function hasOwn(obj, key) { | ||
return hasOwnProperty.call(obj, key); | ||
} | ||
// Array to Object `['a'] => { a: true }` | ||
function makeMap(list) { | ||
const map = Object.create(null); | ||
for (let i = 0; i < list.length; i++) { | ||
map[list[i]] = true; | ||
} | ||
return (val) => map[val]; | ||
} | ||
const warnPrefix = '[Garfish warning]'; | ||
const processError = (error, fn) => { | ||
try { | ||
if (typeof error === 'string') { | ||
error = `${warnPrefix}: ${error}\n\n`; | ||
fn(error, true); | ||
} | ||
else if (error instanceof Error) { | ||
if (!error.message.startsWith(warnPrefix)) { | ||
error.message = `${warnPrefix}: ${error.message}`; | ||
} | ||
fn(error, false); | ||
} | ||
} | ||
catch (e) { | ||
fn(error, typeof error === 'string'); | ||
} | ||
}; | ||
function warn(msg) { | ||
processError(msg, (e, isString) => { | ||
const warnMsg = isString ? e : e.message; | ||
if (false) ; | ||
console.warn(warnMsg); | ||
}); | ||
} | ||
function error(error) { | ||
processError(error, (e, isString) => { | ||
if (isString) { | ||
throw new Error(e); | ||
} | ||
else { | ||
throw e; | ||
} | ||
}); | ||
} | ||
// 有些测试 jest.mock 不好测,可用这个工具方法 | ||
function callTestCallback(obj, ...args) { | ||
} | ||
// 数组去重,不保证顺序 | ||
function unique(list) { | ||
const res = []; | ||
for (let i = 0, len = list.length; i < len; i++) { | ||
for (let j = i + 1; j < len; j++) { | ||
if (list[i] === list[j]) { | ||
j = ++i; | ||
} | ||
} | ||
res.push(list[i]); | ||
} | ||
return res; | ||
} | ||
function isPrimitive(val) { | ||
return (val === null || | ||
typeof val === 'string' || | ||
typeof val === 'number' || | ||
typeof val === 'bigint' || | ||
typeof val === 'symbol' || | ||
typeof val === 'boolean' || | ||
typeof val === 'undefined'); | ||
} | ||
// Deeply merge two objects, can handle circular references, the latter overwrite the previous | ||
function deepMerge(o, n, dp) { | ||
const leftRecord = new WeakMap(); | ||
const rightRecord = new WeakMap(); | ||
const valueRecord = new WeakMap(); | ||
const isArray = Array.isArray; | ||
const isAllRefs = (a, b) => { | ||
if (leftRecord.has(a) || rightRecord.has(a)) { | ||
return leftRecord.has(b) || rightRecord.has(b); | ||
} | ||
}; | ||
const clone = (v) => { | ||
// Deep clone | ||
if (isPrimitive(v) || typeof v === 'function') { | ||
return v; | ||
} | ||
else if (valueRecord.has(v)) { | ||
return valueRecord.get(v); | ||
} | ||
else if (leftRecord.has(v)) { | ||
return leftRecord.get(v); | ||
} | ||
else if (rightRecord.has(v)) { | ||
return rightRecord.get(v); | ||
} | ||
else if (isArray(v)) { | ||
if (dp) | ||
v = unique(v); | ||
const arr = []; | ||
valueRecord.set(v, arr); | ||
for (let i = 0, len = v.length; i < len; i++) { | ||
arr[i] = clone(v[i]); | ||
} | ||
return arr; | ||
} | ||
else if (typeof v === 'object') { | ||
const obj = {}; | ||
valueRecord.set(v, obj); | ||
const keys = Reflect.ownKeys(v); | ||
keys.forEach((key) => (obj[key] = clone(v[key]))); | ||
return obj; | ||
} | ||
}; | ||
const setValue = (r, k) => { | ||
if (r.has(k)) { | ||
return r.get(k); | ||
} | ||
else { | ||
const val = clone(k); | ||
if (!isPrimitive(val) && typeof val !== 'function') { | ||
r.set(k, val); | ||
} | ||
return val; | ||
} | ||
}; | ||
const mergeObject = (l, r) => { | ||
const res = {}; | ||
const leftKeys = Reflect.ownKeys(l); | ||
const rightKeys = Reflect.ownKeys(r); | ||
leftRecord.set(l, res); | ||
rightRecord.set(r, res); | ||
leftKeys.forEach((key) => { | ||
const lv = l[key]; | ||
const rv = r[key]; | ||
if (hasOwn(r, key)) { | ||
if (isArray(lv) && isArray(rv)) { | ||
const item = clone([].concat(lv, rv)); | ||
res[key] = dp ? unique(item) : item; | ||
} | ||
else if (isPlainObject(lv) && isPlainObject(rv)) { | ||
res[key] = isAllRefs(lv, rv) | ||
? leftRecord.get(lv) // The same value on the left and right, whichever is OK | ||
: mergeObject(lv, rv); | ||
} | ||
else { | ||
res[key] = setValue(rightRecord, rv); | ||
} | ||
} | ||
else { | ||
res[key] = setValue(leftRecord, lv); | ||
} | ||
}); | ||
rightKeys.forEach((key) => { | ||
if (hasOwn(res, key)) | ||
return; | ||
res[key] = setValue(rightRecord, r[key]); | ||
}); | ||
return res; | ||
}; | ||
return mergeObject(o, n); | ||
} | ||
// Scheme: https://tools.ietf.org/html/rfc3986#section-3.1 | ||
// Absolute URL: https://tools.ietf.org/html/rfc3986#section-4.3 | ||
function isAbsolute(url) { | ||
// `c:\\` 这种 case 返回 false,在浏览器中使用本地图片,应该用 file 协议 | ||
if (!/^[a-zA-Z]:\\/.test(url)) { | ||
if (/^[a-zA-Z][a-zA-Z\d+\-.]*:/.test(url)) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
function transformUrl(resolvePath, curPath) { | ||
// const baseUrl = new URL(resolvePath, location.href); | ||
// const realPath = new URL(curPath, baseUrl.href); | ||
// return realPath.href; | ||
return curPath; | ||
} | ||
Permission to use, copy, modify, and/or distribute this software for any | ||
purpose with or without fee is hereby granted. | ||
// copy from https://github.com/getsentry/sentry-javascript/blob/6.4.0/packages/browser/src/tracekit.ts | ||
const sourceListTags = [ | ||
'link', | ||
'style', | ||
'script', | ||
'img', | ||
'video', | ||
'audio', | ||
]; | ||
makeMap(sourceListTags); | ||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH | ||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY | ||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, | ||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM | ||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR | ||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR | ||
PERFORMANCE OF THIS SOFTWARE. | ||
***************************************************************************** */ | ||
const xChar = 120; // "x" char | ||
const colonChar = 58; // ":" char | ||
const ns = 'http://www.w3.org/2000/svg'; | ||
const xlinkNS = 'http://www.w3.org/1999/xlink'; // xmlns:xlink | ||
const xmlNS = 'http://www.w3.org/XML/1998/namespace'; // xmlns | ||
// https://developer.mozilla.org/en-US/docs/Web/SVG/Element | ||
const SVG_TAGS = 'svg,animate,animateMotion,animateTransform,circle,clipPath,color-profile,' + | ||
'defs,desc,discard,ellipse,feBlend,feColorMatrix,feComponentTransfer,' + | ||
'feComposite,feConvolveMatrix,feDiffuseLighting,feDisplacementMap,' + | ||
'feDistanceLight,feDropShadow,feFlood,feFuncA,feFuncB,feFuncG,feFuncR,' + | ||
'feGaussianBlur,feImage,feMerge,feMergeNode,feMorphology,feOffset,' + | ||
'fePointLight,feSpecularLighting,feSpotLight,feTile,feTurbulence,filter,' + | ||
'foreignObject,g,hatch,hatchpath,image,line,linearGradient,marker,mask,' + | ||
'mesh,meshgradient,meshpatch,meshrow,metadata,mpath,path,pattern,' + | ||
'polygon,polyline,radialGradient,rect,set,solidcolor,stop,switch,symbol,' + | ||
'text,textPath,title,tspan,unknown,use,view'; | ||
const isSVG = makeMap(SVG_TAGS.split(',')); | ||
function attributesString(attributes) { | ||
if (!attributes || attributes.length === 0) | ||
return ''; | ||
return attributes.reduce((total, { key, value }) => { | ||
return total + (value ? `${key}="${value}" ` : key); | ||
}, ''); | ||
} | ||
class DOMApis { | ||
constructor(cusDocument) { | ||
this.document = cusDocument || document; | ||
} | ||
isText(node) { | ||
return node && node.type === 'text'; | ||
} | ||
isNode(node) { | ||
return node && node.type === 'element'; | ||
} | ||
isCommentNode(node) { | ||
return node && node.type === 'comment'; | ||
} | ||
isCssLinkNode(node) { | ||
if (this.isNode(node) && node.tagName === 'link') { | ||
return !!node.attributes.find(({ key, value }) => key === 'rel' && value === 'stylesheet'); | ||
} | ||
return false; | ||
} | ||
isIconLinkNode(node) { | ||
if (this.isNode(node) && node.tagName === 'link') { | ||
return !!node.attributes.find(({ key, value }) => key === 'rel' && value === 'icon'); | ||
} | ||
return false; | ||
} | ||
isPrefetchJsLinkNode(node) { | ||
if (!this.isNode(node) || node.tagName !== 'link') | ||
return false; | ||
let hasRelAttr, hasAsAttr; | ||
for (const { key, value } of node.attributes) { | ||
if (key === 'rel') { | ||
hasRelAttr = true; | ||
if (value !== 'preload' && value !== 'prefetch') { | ||
return false; | ||
} | ||
} | ||
else if (key === 'as') { | ||
hasAsAttr = true; | ||
if (value !== 'script') | ||
return false; | ||
} | ||
} | ||
return Boolean(hasRelAttr && hasAsAttr); | ||
} | ||
isRemoteModule(node) { | ||
if (!this.isNode(node) || node.tagName !== 'meta') | ||
return false; | ||
let hasNameAttr, hasSrcAttr; | ||
for (const { key, value } of node.attributes) { | ||
if (key === 'name') { | ||
hasNameAttr = true; | ||
if (value !== 'garfish-remote-module') { | ||
return false; | ||
} | ||
} | ||
else if (key === 'src') { | ||
hasSrcAttr = true; | ||
if (typeof value === 'undefined' || value === '') { | ||
return false; | ||
} | ||
} | ||
} | ||
return Boolean(hasNameAttr && hasSrcAttr); | ||
} | ||
removeElement(el) { | ||
const parentNode = el && el.parentNode; | ||
if (parentNode) { | ||
parentNode.removeChild(el); | ||
} | ||
} | ||
createElement(node) { | ||
const { tagName, attributes } = node; | ||
const el = isSVG(tagName) | ||
? this.document.createElementNS(ns, tagName) | ||
: this.document.createElement(tagName); | ||
this.applyAttributes(el, attributes); | ||
return el; | ||
} | ||
createTextNode(node) { | ||
return this.document.createTextNode(node.content); | ||
} | ||
createStyleNode(content) { | ||
const el = this.document.createElement('style'); | ||
content && (el.textContent = content); | ||
this.applyAttributes(el, [{ key: 'type', value: 'text/css' }]); | ||
return el; | ||
} | ||
createLinkCommentNode(node) { | ||
if (this.isNode(node)) { | ||
const ps = attributesString(node.attributes); | ||
return `<link ${ps.slice(0, -1)}></link>`; | ||
} | ||
else { | ||
node = node ? `src="${node}" ` : ''; | ||
return this.document.createComment(`<link ${node}execute by garfish(dynamic)></link>`); | ||
} | ||
} | ||
createScriptCommentNode(node) { | ||
if (this.isNode(node)) { | ||
const { attributes, children } = node; | ||
const ps = attributesString(attributes); | ||
const code = (children === null || children === void 0 ? void 0 : children[0]) ? children[0].content : ''; | ||
return this.document.createComment(`<script ${ps} execute by garfish>${code}</script>`); | ||
} | ||
else { | ||
const { src, code } = node; | ||
const url = src ? `src="${src}" ` : ''; | ||
return this.document.createComment(`<script ${url}execute by garfish(dynamic)>${code}</script>`); | ||
} | ||
} | ||
applyAttributes(el, attributes) { | ||
if (!attributes || attributes.length === 0) | ||
return; | ||
for (const { key, value } of attributes) { | ||
if (value === null) { | ||
el.setAttribute(key, ''); | ||
} | ||
else if (typeof value === 'string') { | ||
if (key.charCodeAt(0) !== xChar) { | ||
el.setAttribute(key, value); | ||
} | ||
else if (key.charCodeAt(3) === colonChar) { | ||
el.setAttributeNS(xmlNS, key, value); | ||
} | ||
else if (key.charCodeAt(5) === colonChar) { | ||
el.setAttributeNS(xlinkNS, key, value); | ||
} | ||
else { | ||
el.setAttribute(key, value); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
function __awaiter(thisArg, _arguments, P, generator) { | ||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
} | ||
function parseContentType(input) { | ||
input = input === null || input === void 0 ? void 0 : input.trim(); | ||
if (!input) | ||
return null; | ||
let idx = 0; | ||
let type = ''; | ||
let subType = ''; | ||
while (idx < input.length && input[idx] !== '/') { | ||
type += input[idx]; | ||
idx++; | ||
} | ||
if (type.length === 0 || idx >= input.length) { | ||
return null; | ||
} | ||
// jump over '/' | ||
idx++; | ||
while (idx < input.length && input[idx] !== ';') { | ||
subType += input[idx]; | ||
idx++; | ||
} | ||
subType = subType.replace(/[ \t\n\r]+$/, ''); | ||
if (subType.length === 0) | ||
return null; | ||
return { | ||
type: type.toLocaleLowerCase(), | ||
subtype: subType.toLocaleLowerCase(), | ||
}; | ||
} | ||
function isCss(mt) { | ||
return mt ? mt.type === 'text' && mt.subtype === 'css' : false; | ||
} | ||
function isHtml(mt) { | ||
return mt ? mt.type === 'text' && mt.subtype === 'html' : false; | ||
} | ||
// https://mimesniff.spec.whatwg.org/#javascript-mime-type | ||
function isJs(mt) { | ||
const { type, subtype } = mt || {}; | ||
switch (type) { | ||
case 'text': { | ||
switch (subtype) { | ||
case 'ecmascript': | ||
case 'javascript': | ||
case 'javascript1.0': | ||
case 'javascript1.1': | ||
case 'javascript1.2': | ||
case 'javascript1.3': | ||
case 'javascript1.4': | ||
case 'javascript1.5': | ||
case 'jscript': | ||
case 'livescript': | ||
case 'x-ecmascript': | ||
case 'x-javascript': { | ||
return true; | ||
} | ||
default: { | ||
return false; | ||
} | ||
} | ||
} | ||
case 'application': { | ||
switch (subtype) { | ||
case 'ecmascript': | ||
case 'javascript': | ||
case 'x-ecmascript': | ||
case 'x-javascript': { | ||
return true; | ||
} | ||
default: { | ||
return false; | ||
} | ||
} | ||
} | ||
default: { | ||
return false; | ||
} | ||
} | ||
} | ||
const objectToString = Object.prototype.toString; | ||
function isPlainObject(val) { | ||
return objectToString.call(val) === '[object Object]'; | ||
} | ||
const hasOwnProperty = Object.prototype.hasOwnProperty; | ||
function hasOwn(obj, key) { | ||
return hasOwnProperty.call(obj, key); | ||
} | ||
// Array to Object `['a'] => { a: true }` | ||
function makeMap(list) { | ||
const map = Object.create(null); | ||
for (let i = 0; i < list.length; i++) { | ||
map[list[i]] = true; | ||
} | ||
return (val) => map[val]; | ||
} | ||
const warnPrefix = '[Garfish warning]'; | ||
const processError = (error, fn) => { | ||
try { | ||
if (typeof error === 'string') { | ||
error = `${warnPrefix}: ${error}\n\n`; | ||
fn(error, true); | ||
} | ||
else if (error instanceof Error) { | ||
if (!error.message.startsWith(warnPrefix)) { | ||
error.message = `${warnPrefix}: ${error.message}`; | ||
} | ||
fn(error, false); | ||
} | ||
} | ||
catch (e) { | ||
fn(error, typeof error === 'string'); | ||
} | ||
}; | ||
function error(error) { | ||
processError(error, (e, isString) => { | ||
if (isString) { | ||
throw new Error(e); | ||
} | ||
else { | ||
throw e; | ||
} | ||
}); | ||
} | ||
// 数组去重,不保证顺序 | ||
function unique(list) { | ||
const res = []; | ||
for (let i = 0, len = list.length; i < len; i++) { | ||
for (let j = i + 1; j < len; j++) { | ||
if (list[i] === list[j]) { | ||
j = ++i; | ||
} | ||
} | ||
res.push(list[i]); | ||
} | ||
return res; | ||
} | ||
function isPrimitive(val) { | ||
return (val === null || | ||
typeof val === 'string' || | ||
typeof val === 'number' || | ||
typeof val === 'bigint' || | ||
typeof val === 'symbol' || | ||
typeof val === 'boolean' || | ||
typeof val === 'undefined'); | ||
} | ||
// Deeply merge two objects, can handle circular references, the latter overwrite the previous | ||
function deepMerge(o, n, dp) { | ||
const leftRecord = new WeakMap(); | ||
const rightRecord = new WeakMap(); | ||
const valueRecord = new WeakMap(); | ||
const isArray = Array.isArray; | ||
const isAllRefs = (a, b) => { | ||
if (leftRecord.has(a) || rightRecord.has(a)) { | ||
return leftRecord.has(b) || rightRecord.has(b); | ||
} | ||
}; | ||
const clone = (v) => { | ||
// Deep clone | ||
if (isPrimitive(v) || typeof v === 'function') { | ||
return v; | ||
} | ||
else if (valueRecord.has(v)) { | ||
return valueRecord.get(v); | ||
} | ||
else if (leftRecord.has(v)) { | ||
return leftRecord.get(v); | ||
} | ||
else if (rightRecord.has(v)) { | ||
return rightRecord.get(v); | ||
} | ||
else if (isArray(v)) { | ||
if (dp) | ||
v = unique(v); | ||
const arr = []; | ||
valueRecord.set(v, arr); | ||
for (let i = 0, len = v.length; i < len; i++) { | ||
arr[i] = clone(v[i]); | ||
} | ||
return arr; | ||
} | ||
else if (typeof v === 'object') { | ||
const obj = {}; | ||
valueRecord.set(v, obj); | ||
const keys = Reflect.ownKeys(v); | ||
keys.forEach((key) => (obj[key] = clone(v[key]))); | ||
return obj; | ||
} | ||
}; | ||
const setValue = (r, k) => { | ||
if (r.has(k)) { | ||
return r.get(k); | ||
} | ||
else { | ||
const val = clone(k); | ||
if (!isPrimitive(val) && typeof val !== 'function') { | ||
r.set(k, val); | ||
} | ||
return val; | ||
} | ||
}; | ||
const mergeObject = (l, r) => { | ||
const res = {}; | ||
const leftKeys = Reflect.ownKeys(l); | ||
const rightKeys = Reflect.ownKeys(r); | ||
leftRecord.set(l, res); | ||
rightRecord.set(r, res); | ||
leftKeys.forEach((key) => { | ||
const lv = l[key]; | ||
const rv = r[key]; | ||
if (hasOwn(r, key)) { | ||
if (isArray(lv) && isArray(rv)) { | ||
const item = clone([].concat(lv, rv)); | ||
res[key] = dp ? unique(item) : item; | ||
} | ||
else if (isPlainObject(lv) && isPlainObject(rv)) { | ||
res[key] = isAllRefs(lv, rv) | ||
? leftRecord.get(lv) // The same value on the left and right, whichever is OK | ||
: mergeObject(lv, rv); | ||
} | ||
else { | ||
res[key] = setValue(rightRecord, rv); | ||
} | ||
} | ||
else { | ||
res[key] = setValue(leftRecord, lv); | ||
} | ||
}); | ||
rightKeys.forEach((key) => { | ||
if (hasOwn(res, key)) | ||
return; | ||
res[key] = setValue(rightRecord, r[key]); | ||
}); | ||
return res; | ||
}; | ||
return mergeObject(o, n); | ||
} | ||
// Scheme: https://tools.ietf.org/html/rfc3986#section-3.1 | ||
// Absolute URL: https://tools.ietf.org/html/rfc3986#section-4.3 | ||
function isAbsolute(url) { | ||
// `c:\\` 这种 case 返回 false,在浏览器中使用本地图片,应该用 file 协议 | ||
if (!/^[a-zA-Z]:\\/.test(url)) { | ||
if (/^[a-zA-Z][a-zA-Z\d+\-.]*:/.test(url)) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
function transformUrl(resolvePath, curPath) { | ||
// const baseUrl = new URL(resolvePath, location.href); | ||
// const realPath = new URL(curPath, baseUrl.href); | ||
// return realPath.href; | ||
return curPath; | ||
} | ||
/*! ***************************************************************************** | ||
Copyright (c) Microsoft Corporation. | ||
// copy from https://github.com/getsentry/sentry-javascript/blob/6.4.0/packages/browser/src/tracekit.ts | ||
const sourceListTags = [ | ||
'link', | ||
'style', | ||
'script', | ||
'img', | ||
'video', | ||
'audio', | ||
]; | ||
makeMap(sourceListTags); | ||
Permission to use, copy, modify, and/or distribute this software for any | ||
purpose with or without fee is hereby granted. | ||
const __ELEMENT_DELETE_TAG__ = '__ELEMENT_DELETE_TAG__'; | ||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH | ||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY | ||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, | ||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM | ||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR | ||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR | ||
PERFORMANCE OF THIS SOFTWARE. | ||
***************************************************************************** */ | ||
const xChar = 120; // "x" char | ||
const colonChar = 58; // ":" char | ||
const ns = 'http://www.w3.org/2000/svg'; | ||
const xlinkNS = 'http://www.w3.org/1999/xlink'; // xmlns:xlink | ||
const xmlNS = 'http://www.w3.org/XML/1998/namespace'; // xmlns | ||
// https://developer.mozilla.org/en-US/docs/Web/SVG/Element | ||
const SVG_TAGS = 'svg,animate,animateMotion,animateTransform,circle,clipPath,color-profile,' + | ||
'defs,desc,discard,ellipse,feBlend,feColorMatrix,feComponentTransfer,' + | ||
'feComposite,feConvolveMatrix,feDiffuseLighting,feDisplacementMap,' + | ||
'feDistanceLight,feDropShadow,feFlood,feFuncA,feFuncB,feFuncG,feFuncR,' + | ||
'feGaussianBlur,feImage,feMerge,feMergeNode,feMorphology,feOffset,' + | ||
'fePointLight,feSpecularLighting,feSpotLight,feTile,feTurbulence,filter,' + | ||
'foreignObject,g,hatch,hatchpath,image,line,linearGradient,marker,mask,' + | ||
'mesh,meshgradient,meshpatch,meshrow,metadata,mpath,path,pattern,' + | ||
'polygon,polyline,radialGradient,rect,set,solidcolor,stop,switch,symbol,' + | ||
'text,textPath,title,tspan,unknown,use,view'; | ||
const isSVG = makeMap(SVG_TAGS.split(',')); | ||
function attributesString(attributes) { | ||
if (!attributes || attributes.length === 0) | ||
return ''; | ||
return attributes.reduce((total, { key, value }) => { | ||
return total + (value ? `${key}="${value}" ` : key); | ||
}, ''); | ||
} | ||
class DOMApis { | ||
constructor(cusDocument) { | ||
this.document = cusDocument || document; | ||
} | ||
isText(node) { | ||
return node && node.type === 'text'; | ||
} | ||
isNode(node) { | ||
return node && node.type === 'element'; | ||
} | ||
isCommentNode(node) { | ||
return node && node.type === 'comment'; | ||
} | ||
isCssLinkNode(node) { | ||
if (this.isNode(node) && node.tagName === 'link') { | ||
return !!node.attributes.find(({ key, value }) => key === 'rel' && value === 'stylesheet'); | ||
} | ||
return false; | ||
} | ||
isIconLinkNode(node) { | ||
if (this.isNode(node) && node.tagName === 'link') { | ||
return !!node.attributes.find(({ key, value }) => key === 'rel' && value === 'icon'); | ||
} | ||
return false; | ||
} | ||
isPrefetchJsLinkNode(node) { | ||
if (!this.isNode(node) || node.tagName !== 'link') | ||
return false; | ||
let hasRelAttr, hasAsAttr; | ||
for (const { key, value } of node.attributes) { | ||
if (key === 'rel') { | ||
hasRelAttr = true; | ||
if (value !== 'preload' && value !== 'prefetch') { | ||
return false; | ||
} | ||
} | ||
else if (key === 'as') { | ||
hasAsAttr = true; | ||
if (value !== 'script') | ||
return false; | ||
} | ||
} | ||
return Boolean(hasRelAttr && hasAsAttr); | ||
} | ||
isRemoteModule(node) { | ||
if (!this.isNode(node) || node.tagName !== 'meta') | ||
return false; | ||
let hasNameAttr, hasSrcAttr; | ||
for (const { key, value } of node.attributes) { | ||
if (key === 'name') { | ||
hasNameAttr = true; | ||
if (value !== 'garfish-remote-module') { | ||
return false; | ||
} | ||
} | ||
else if (key === 'src') { | ||
hasSrcAttr = true; | ||
if (typeof value === 'undefined' || value === '') { | ||
return false; | ||
} | ||
} | ||
} | ||
return Boolean(hasNameAttr && hasSrcAttr); | ||
} | ||
removeElement(el) { | ||
const parentNode = el && el.parentNode; | ||
if (parentNode) { | ||
parentNode.removeChild(el); | ||
el[__ELEMENT_DELETE_TAG__] = true; | ||
} | ||
} | ||
createElement(node) { | ||
const { tagName, attributes } = node; | ||
const el = isSVG(tagName) | ||
? this.document.createElementNS(ns, tagName) | ||
: this.document.createElement(tagName); | ||
this.applyAttributes(el, attributes); | ||
return el; | ||
} | ||
createTextNode(node) { | ||
return this.document.createTextNode(node.content); | ||
} | ||
createStyleNode(content) { | ||
const el = this.document.createElement('style'); | ||
content && (el.textContent = content); | ||
this.applyAttributes(el, [{ key: 'type', value: 'text/css' }]); | ||
return el; | ||
} | ||
createLinkCommentNode(node) { | ||
if (this.isNode(node)) { | ||
const ps = attributesString(node.attributes); | ||
return `<link ${ps.slice(0, -1)}></link>`; | ||
} | ||
else { | ||
node = node ? `src="${node}" ` : ''; | ||
return this.document.createComment(`<link ${node}execute by garfish(dynamic)></link>`); | ||
} | ||
} | ||
createScriptCommentNode(node) { | ||
if (this.isNode(node)) { | ||
const { attributes, children } = node; | ||
const ps = attributesString(attributes); | ||
const code = (children === null || children === void 0 ? void 0 : children[0]) ? children[0].content : ''; | ||
return this.document.createComment(`<script ${ps} execute by garfish>${code}</script>`); | ||
} | ||
else { | ||
const { src, code } = node; | ||
const url = src ? `src="${src}" ` : ''; | ||
return this.document.createComment(`<script ${url}execute by garfish(dynamic)>${code}</script>`); | ||
} | ||
} | ||
applyAttributes(el, attributes) { | ||
if (!attributes || attributes.length === 0) | ||
return; | ||
for (const { key, value } of attributes) { | ||
if (value === null) { | ||
el.setAttribute(key, ''); | ||
} | ||
else if (typeof value === 'string') { | ||
if (key.charCodeAt(0) !== xChar) { | ||
el.setAttribute(key, value); | ||
} | ||
else if (key.charCodeAt(3) === colonChar) { | ||
el.setAttributeNS(xmlNS, key, value); | ||
} | ||
else if (key.charCodeAt(5) === colonChar) { | ||
el.setAttributeNS(xlinkNS, key, value); | ||
} | ||
else { | ||
el.setAttribute(key, value); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
function __awaiter(thisArg, _arguments, P, generator) { | ||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
} | ||
function parseContentType(input) { | ||
input = input === null || input === void 0 ? void 0 : input.trim(); | ||
if (!input) | ||
return null; | ||
let idx = 0; | ||
let type = ''; | ||
let subType = ''; | ||
while (idx < input.length && input[idx] !== '/') { | ||
type += input[idx]; | ||
idx++; | ||
} | ||
if (type.length === 0 || idx >= input.length) { | ||
return null; | ||
} | ||
// jump over '/' | ||
idx++; | ||
while (idx < input.length && input[idx] !== ';') { | ||
subType += input[idx]; | ||
idx++; | ||
} | ||
subType = subType.replace(/[ \t\n\r]+$/, ''); | ||
if (subType.length === 0) | ||
return null; | ||
return { | ||
type: type.toLocaleLowerCase(), | ||
subtype: subType.toLocaleLowerCase(), | ||
}; | ||
} | ||
function isCss(mt) { | ||
return mt ? mt.type === 'text' && mt.subtype === 'css' : false; | ||
} | ||
function isHtml(mt) { | ||
return mt ? mt.type === 'text' && mt.subtype === 'html' : false; | ||
} | ||
// https://mimesniff.spec.whatwg.org/#javascript-mime-type | ||
function isJs(mt) { | ||
const { type, subtype } = mt || {}; | ||
switch (type) { | ||
case 'text': { | ||
switch (subtype) { | ||
case 'ecmascript': | ||
case 'javascript': | ||
case 'javascript1.0': | ||
case 'javascript1.1': | ||
case 'javascript1.2': | ||
case 'javascript1.3': | ||
case 'javascript1.4': | ||
case 'javascript1.5': | ||
case 'jscript': | ||
case 'livescript': | ||
case 'x-ecmascript': | ||
case 'x-javascript': { | ||
return true; | ||
} | ||
default: { | ||
return false; | ||
} | ||
} | ||
} | ||
case 'application': { | ||
switch (subtype) { | ||
case 'ecmascript': | ||
case 'javascript': | ||
case 'x-ecmascript': | ||
case 'x-javascript': { | ||
return true; | ||
} | ||
default: { | ||
return false; | ||
} | ||
} | ||
} | ||
default: { | ||
return false; | ||
} | ||
} | ||
} | ||
var ElementType; | ||
(function (ElementType) { | ||
ElementType[ElementType["TEXT"] = 3] = "TEXT"; | ||
ElementType[ElementType["COMMENT"] = 8] = "COMMENT"; | ||
ElementType[ElementType["ELEMENT"] = 1] = "ELEMENT"; | ||
})(ElementType || (ElementType = {})); | ||
function Attributes({ name, value }) { | ||
this.key = name; | ||
this.value = value; | ||
} | ||
const generateAttributes = (el) => { | ||
const list = []; | ||
const attrs = el.attributes; | ||
const len = attrs.length; | ||
if (len > 0) { | ||
// Optimize for the most common cases | ||
if (len === 1) { | ||
list[0] = new Attributes(attrs[0]); | ||
} | ||
else if (len === 2) { | ||
list[0] = new Attributes(attrs[0]); | ||
list[1] = new Attributes(attrs[1]); | ||
} | ||
else { | ||
for (let i = 0; i < len; i++) { | ||
list[i] = new Attributes(attrs[i]); | ||
} | ||
} | ||
} | ||
return list; | ||
}; | ||
const createElement = (el, filter) => { | ||
switch (el.nodeType) { | ||
case ElementType.TEXT: | ||
return { | ||
type: 'text', | ||
content: el.textContent, | ||
}; | ||
case ElementType.COMMENT: | ||
return { | ||
type: 'comment', | ||
content: el.textContent, | ||
}; | ||
case ElementType.ELEMENT: | ||
return filter({ | ||
type: 'element', | ||
tagName: el.tagName.toLowerCase(), | ||
attributes: generateAttributes(el), | ||
children: Array.from(el.childNodes).map((node) => { | ||
return createElement(node, filter); | ||
}), | ||
}); | ||
default: | ||
error(`Invalid node type "${el.nodeType}"`); | ||
} | ||
}; | ||
// 1M text takes about time 60ms | ||
function templateParse(code, tags) { | ||
let astTree = []; | ||
const htmlNode = document.createElement('html'); | ||
const collectionEls = {}; | ||
const filter = (el) => { | ||
if (tags.includes(el.tagName)) { | ||
collectionEls[el.tagName].push(el); | ||
} | ||
return el; | ||
}; | ||
htmlNode.innerHTML = code; | ||
for (const tag of tags) { | ||
collectionEls[tag] = []; | ||
} | ||
astTree = Array.from(htmlNode.childNodes).map((node) => { | ||
return createElement(node, filter); | ||
}); | ||
return [astTree, collectionEls]; | ||
} | ||
var ElementType; | ||
(function (ElementType) { | ||
ElementType[ElementType["TEXT"] = 3] = "TEXT"; | ||
ElementType[ElementType["COMMENT"] = 8] = "COMMENT"; | ||
ElementType[ElementType["ELEMENT"] = 1] = "ELEMENT"; | ||
})(ElementType || (ElementType = {})); | ||
function Attributes({ name, value }) { | ||
this.key = name; | ||
this.value = value; | ||
} | ||
const generateAttributes = (el) => { | ||
const list = []; | ||
const attrs = el.attributes; | ||
const len = attrs.length; | ||
if (len > 0) { | ||
// Optimize for the most common cases | ||
if (len === 1) { | ||
list[0] = new Attributes(attrs[0]); | ||
} | ||
else if (len === 2) { | ||
list[0] = new Attributes(attrs[0]); | ||
list[1] = new Attributes(attrs[1]); | ||
} | ||
else { | ||
for (let i = 0; i < len; i++) { | ||
list[i] = new Attributes(attrs[i]); | ||
} | ||
} | ||
} | ||
return list; | ||
}; | ||
const createElement = (el, filter) => { | ||
switch (el.nodeType) { | ||
case ElementType.TEXT: | ||
return { | ||
type: 'text', | ||
content: el.textContent, | ||
}; | ||
case ElementType.COMMENT: | ||
return { | ||
type: 'comment', | ||
content: el.textContent, | ||
}; | ||
case ElementType.ELEMENT: | ||
return filter({ | ||
type: 'element', | ||
tagName: el.tagName.toLowerCase(), | ||
attributes: generateAttributes(el), | ||
children: Array.from(el.childNodes).map((node) => { | ||
return createElement(node, filter); | ||
}), | ||
}); | ||
default: | ||
error(`Invalid node type "${el.nodeType}"`); | ||
} | ||
}; | ||
// 1M text takes about time 60ms | ||
function templateParse(code, tags) { | ||
let astTree = []; | ||
const htmlNode = document.createElement('html'); | ||
const collectionEls = {}; | ||
const filter = (el) => { | ||
if (tags.includes(el.tagName)) { | ||
collectionEls[el.tagName].push(el); | ||
} | ||
return el; | ||
}; | ||
htmlNode.innerHTML = code; | ||
for (const tag of tags) { | ||
collectionEls[tag] = []; | ||
} | ||
astTree = Array.from(htmlNode.childNodes).map((node) => { | ||
return createElement(node, filter); | ||
}); | ||
return [astTree, collectionEls]; | ||
} | ||
class LoaderPlugin { | ||
constructor(type) { | ||
this.onerror = error; | ||
this.plugins = new Set(); | ||
this.type = type; | ||
} | ||
on(plugin) { | ||
if (this.plugins.has(plugin)) { | ||
warn('Repeat add plugin.'); | ||
return; | ||
} | ||
this.plugins.add(plugin); | ||
} | ||
once(plugin) { | ||
const self = this; | ||
return this.once(function wrapper(...args) { | ||
self.remove(wrapper); | ||
return plugin.apply(null, args); | ||
}); | ||
} | ||
emit(result) { | ||
for (const plugin of this.plugins) { | ||
try { | ||
let illegalResult = false; | ||
const tempResult = plugin(result); | ||
for (const key in result) { | ||
if (!hasOwn(key, tempResult)) { | ||
illegalResult = true; | ||
break; | ||
} | ||
} | ||
if (illegalResult) { | ||
this.onerror(`The "${this.type}" type has a plugin return value error.`); | ||
} | ||
else { | ||
result = tempResult; | ||
} | ||
} | ||
catch (err) { | ||
warn(err); | ||
this.onerror(err); | ||
} | ||
} | ||
return result; | ||
} | ||
remove(plugin) { | ||
if (this.plugins.has(plugin)) { | ||
this.plugins.delete(plugin); | ||
} | ||
} | ||
removeAll() { | ||
this.plugins.clear(); | ||
} | ||
} | ||
const ONCE_FLAG = Symbol('once'); | ||
const REGISTER_PLUGINS = new Set(); | ||
class PluginManager { | ||
constructor(type) { | ||
if (REGISTER_PLUGINS.has(type)) { | ||
throw new Error(`"${type}" has been created.`); | ||
} | ||
this.type = type; | ||
this.onerror = error; | ||
this.plugins = new Set(); | ||
} | ||
add(plugin) { | ||
if (this.plugins.has(plugin)) { | ||
console.error('repeat add plugin.'); | ||
return false; | ||
} | ||
this.plugins.add(plugin); | ||
return true; | ||
} | ||
addOnce(plugin) { | ||
plugin._onceFlag = ONCE_FLAG; | ||
return this.add(plugin); | ||
} | ||
remove(plugin) { | ||
if (this.plugins.has(plugin)) { | ||
this.plugins.delete(plugin); | ||
return true; | ||
} | ||
return false; | ||
} | ||
run(result) { | ||
for (const fn of this.plugins) { | ||
try { | ||
let illegalResult = false; | ||
const tempResult = fn(result); | ||
if (fn._onceFlag === ONCE_FLAG) | ||
this.remove(fn); | ||
for (const key in result) { | ||
if (!hasOwn(key, tempResult)) { | ||
illegalResult = true; | ||
break; | ||
} | ||
} | ||
illegalResult | ||
? this.onerror(`The "${this.type}" type has a plugin return value error.`) | ||
: (result = tempResult); | ||
} | ||
catch (err) { | ||
this.onerror(err); | ||
console.error(err); | ||
} | ||
} | ||
return result; | ||
} | ||
} | ||
function request(url, config) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const result = yield fetch(url, config || {}); | ||
// Response codes greater than "400" are regarded as errors | ||
if (result.status >= 400) { | ||
error(`"${url}" load failed with status "${result.status}"`); | ||
} | ||
const code = yield result.text(); | ||
const type = result.headers.get('content-type'); | ||
const mimeType = parseContentType(type); | ||
return { code, result, mimeType }; | ||
}); | ||
} | ||
function copyResult(result) { | ||
if (result.resourceManager) { | ||
result.resourceManager = result.resourceManager.clone(); | ||
} | ||
return result; | ||
} | ||
// Compatible with old api | ||
function mergeConfig(loader, url) { | ||
const extra = loader.requestConfig; | ||
const config = typeof extra === 'function' ? extra(url) : extra; | ||
return Object.assign({ mode: 'cors' }, config); | ||
} | ||
function calculateObjectSize(obj) { | ||
let size = 0; | ||
const valueSet = new WeakSet(); | ||
const add = (val) => { | ||
if (isPrimitive(val)) { | ||
size += new Blob([val]).size; | ||
} | ||
else if (isPlainObject(val)) { | ||
if (!valueSet.has(val)) { | ||
valueSet.add(val); | ||
for (const key in val) | ||
add(val[key]); | ||
} | ||
} | ||
else if (Array.isArray(val)) { | ||
if (!valueSet.has(val)) { | ||
valueSet.add(val); | ||
val.forEach(add); | ||
} | ||
} | ||
else { | ||
size += new Blob([val]).size; | ||
} | ||
}; | ||
add(obj); | ||
return size; | ||
} | ||
function request(url, config) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const result = yield fetch(url, config || {}); | ||
// Response codes greater than "400" are regarded as errors | ||
if (result.status >= 400) { | ||
error(`"${url}" load failed with status "${result.status}"`); | ||
} | ||
const code = yield result.text(); | ||
const type = result.headers.get('content-type'); | ||
const mimeType = parseContentType(type); | ||
return { code, result, mimeType }; | ||
}); | ||
} | ||
function copyResult(result) { | ||
if (result.resourceManager) { | ||
result.resourceManager = result.resourceManager.clone(); | ||
} | ||
return result; | ||
} | ||
// Compatible with old api | ||
function mergeConfig(loader, url) { | ||
const extra = loader.requestConfig; | ||
const config = typeof extra === 'function' ? extra(url) : extra; | ||
return Object.assign({ mode: 'cors' }, config); | ||
} | ||
function calculateObjectSize(obj) { | ||
let size = 0; | ||
const valueSet = new WeakSet(); | ||
const add = (val) => { | ||
if (isPrimitive(val)) { | ||
size += new Blob([val]).size; | ||
} | ||
else if (isPlainObject(val)) { | ||
if (!valueSet.has(val)) { | ||
valueSet.add(val); | ||
for (const key in val) | ||
add(val[key]); | ||
} | ||
} | ||
else if (Array.isArray(val)) { | ||
if (!valueSet.has(val)) { | ||
valueSet.add(val); | ||
val.forEach(add); | ||
} | ||
} | ||
else { | ||
size += new Blob([val]).size; | ||
} | ||
}; | ||
add(obj); | ||
return size; | ||
} | ||
const cachedDataSet = new WeakSet(); | ||
var FileTypes; | ||
(function (FileTypes) { | ||
FileTypes["js"] = "js"; | ||
FileTypes["css"] = "css"; | ||
FileTypes["module"] = "module"; | ||
FileTypes["template"] = "template"; | ||
})(FileTypes || (FileTypes = {})); | ||
const MAX_SIZE = 10 * 1024 * 1024; | ||
const DEFAULT_POLL = Symbol('__defaultBufferPoll__'); | ||
const FILE_TYPES = [ | ||
FileTypes.js, | ||
FileTypes.css, | ||
FileTypes.module, | ||
FileTypes.template, | ||
DEFAULT_POLL, | ||
]; | ||
class AppCacheContainer { | ||
constructor(maxSize = MAX_SIZE) { | ||
this.totalSize = 0; | ||
this.recorder = {}; | ||
this.maxSize = maxSize; | ||
FILE_TYPES.forEach((key) => { | ||
this.recorder[key] = 0; | ||
this[key] = new Map(); | ||
}); | ||
} | ||
bufferPool(type) { | ||
return this[type]; | ||
} | ||
has(url) { | ||
return FILE_TYPES.some((key) => this[key].has(url)); | ||
} | ||
get(url) { | ||
for (const key of FILE_TYPES) { | ||
if (this[key].has(url)) { | ||
return this[key].get(url); | ||
} | ||
} | ||
} | ||
set(url, data, type) { | ||
const curSize = cachedDataSet.has(data) ? 0 : calculateObjectSize(data); | ||
const totalSize = this.totalSize + curSize; | ||
if (totalSize < this.maxSize) { | ||
let bar = type; | ||
let bufferPool = this.bufferPool(type); | ||
if (!bufferPool) { | ||
bar = DEFAULT_POLL; | ||
bufferPool = this.bufferPool(DEFAULT_POLL); | ||
} | ||
bufferPool.set(url, data); | ||
this.totalSize = totalSize; | ||
this.recorder[bar] += curSize; | ||
return true; | ||
} | ||
return false; | ||
} | ||
clear(type) { | ||
if (typeof type === 'string') { | ||
const cacheBox = this.bufferPool(type); | ||
if (cacheBox && cacheBox instanceof Map) { | ||
const size = this.recorder[type]; | ||
this.totalSize -= size; | ||
this.recorder[type] = 0; | ||
cacheBox.clear(); | ||
} | ||
} | ||
else { | ||
FILE_TYPES.forEach((key) => { | ||
this[key].clear(); | ||
this.recorder[key] = 0; | ||
}); | ||
this.totalSize = 0; | ||
} | ||
} | ||
} | ||
const cachedDataSet = new WeakSet(); | ||
var FileTypes; | ||
(function (FileTypes) { | ||
FileTypes["js"] = "js"; | ||
FileTypes["css"] = "css"; | ||
FileTypes["module"] = "module"; | ||
FileTypes["template"] = "template"; | ||
})(FileTypes || (FileTypes = {})); | ||
const MAX_SIZE = 10 * 1024 * 1024; | ||
const DEFAULT_POLL = Symbol('__defaultBufferPoll__'); | ||
const FILE_TYPES = [ | ||
FileTypes.js, | ||
FileTypes.css, | ||
FileTypes.module, | ||
FileTypes.template, | ||
DEFAULT_POLL, | ||
]; | ||
class AppCacheContainer { | ||
constructor(maxSize = MAX_SIZE) { | ||
this.totalSize = 0; | ||
this.recorder = {}; | ||
this.maxSize = maxSize; | ||
FILE_TYPES.forEach((key) => { | ||
this.recorder[key] = 0; | ||
this[key] = new Map(); | ||
}); | ||
} | ||
bufferPool(type) { | ||
return this[type]; | ||
} | ||
has(url) { | ||
return FILE_TYPES.some((key) => this[key].has(url)); | ||
} | ||
get(url) { | ||
for (const key of FILE_TYPES) { | ||
if (this[key].has(url)) { | ||
return this[key].get(url); | ||
} | ||
} | ||
} | ||
set(url, data, type) { | ||
const curSize = cachedDataSet.has(data) ? 0 : calculateObjectSize(data); | ||
const totalSize = this.totalSize + curSize; | ||
if (totalSize < this.maxSize) { | ||
let bar = type; | ||
let bufferPool = this.bufferPool(type); | ||
if (!bufferPool) { | ||
bar = DEFAULT_POLL; | ||
bufferPool = this.bufferPool(DEFAULT_POLL); | ||
} | ||
bufferPool.set(url, data); | ||
this.totalSize = totalSize; | ||
this.recorder[bar] += curSize; | ||
return true; | ||
} | ||
return false; | ||
} | ||
clear(type) { | ||
if (typeof type === 'string') { | ||
const cacheBox = this.bufferPool(type); | ||
if (cacheBox && cacheBox instanceof Map) { | ||
const size = this.recorder[type]; | ||
this.totalSize -= size; | ||
this.recorder[type] = 0; | ||
cacheBox.clear(); | ||
} | ||
} | ||
else { | ||
FILE_TYPES.forEach((key) => { | ||
this[key].clear(); | ||
this.recorder[key] = 0; | ||
}); | ||
this.totalSize = 0; | ||
} | ||
} | ||
} | ||
// Match url in css | ||
const MATCH_CSS_URL = /url\(['"]?([^\)]+?)['"]?\)/g; | ||
class StyleManager { | ||
constructor(styleCode, url) { | ||
this.depsStack = new Set(); | ||
this.url = url || null; | ||
this.styleCode = styleCode; | ||
} | ||
correctPath(baseUrl) { | ||
const { url, styleCode } = this; | ||
if (!baseUrl) | ||
baseUrl = url; | ||
if (baseUrl && typeof styleCode === 'string') { | ||
// The relative path is converted to an absolute path according to the path of the css file | ||
this.styleCode = styleCode.replace(MATCH_CSS_URL, (k1, k2) => { | ||
if (isAbsolute(k2)) | ||
return k1; | ||
return `url("${transformUrl(url, k2)}")`; | ||
}); | ||
} | ||
} | ||
setScope(_scope) { | ||
// Process css cope | ||
} | ||
setDep(node) { | ||
this.depsStack.add(node); | ||
} | ||
isSameOrigin(node) { | ||
return this.depsStack.has(node); | ||
} | ||
renderAsStyleElement(extraCode = '') { | ||
const node = document.createElement('style'); | ||
node.setAttribute('type', 'text/css'); | ||
// prettier-ignore | ||
node.textContent = extraCode + (this.styleCode | ||
? this.styleCode | ||
: '/**empty style**/'); | ||
return node; | ||
} | ||
clone() { | ||
// @ts-ignore | ||
const cloned = new this.constructor(); | ||
cloned.url = this.url; | ||
cloned.styleCode = this.styleCode; | ||
cloned.depsStack = new Set(this.depsStack); | ||
return cloned; | ||
} | ||
} | ||
// Match url in css | ||
const MATCH_CSS_URL = /url\(['"]?([^\)]+?)['"]?\)/g; | ||
class StyleManager { | ||
constructor(styleCode, url) { | ||
this.depsStack = new Set(); | ||
this.url = url || null; | ||
this.styleCode = styleCode; | ||
} | ||
correctPath(baseUrl) { | ||
const { url, styleCode } = this; | ||
if (!baseUrl) | ||
baseUrl = url; | ||
if (baseUrl && typeof styleCode === 'string') { | ||
// The relative path is converted to an absolute path according to the path of the css file | ||
this.styleCode = styleCode.replace(MATCH_CSS_URL, (k1, k2) => { | ||
if (isAbsolute(k2)) | ||
return k1; | ||
return `url("${transformUrl(url, k2)}")`; | ||
}); | ||
} | ||
} | ||
setScope(_scope) { | ||
// Process css cope | ||
} | ||
setDep(node) { | ||
this.depsStack.add(node); | ||
} | ||
isSameOrigin(node) { | ||
return this.depsStack.has(node); | ||
} | ||
renderAsStyleElement(extraCode = '') { | ||
const node = document.createElement('style'); | ||
node.setAttribute('type', 'text/css'); | ||
// prettier-ignore | ||
node.textContent = extraCode + (this.styleCode | ||
? this.styleCode | ||
: '/**empty style**/'); | ||
return node; | ||
} | ||
clone() { | ||
// @ts-ignore | ||
const cloned = new this.constructor(); | ||
cloned.url = this.url; | ||
cloned.styleCode = this.styleCode; | ||
cloned.depsStack = new Set(this.depsStack); | ||
return cloned; | ||
} | ||
} | ||
class ModuleManager { | ||
constructor(moduleCode, url) { | ||
this.alias = null; | ||
this.url = url || null; | ||
this.moduleCode = moduleCode; | ||
} | ||
setAlias(name) { | ||
if (name && typeof name === 'string') { | ||
this.alias = name; | ||
} | ||
} | ||
clone() { | ||
// @ts-ignore | ||
const cloned = new this.constructor(); | ||
cloned.url = this.url; | ||
cloned.alias = this.alias; | ||
cloned.moduleCode = this.moduleCode; | ||
return cloned; | ||
} | ||
} | ||
class ModuleManager { | ||
constructor(moduleCode, url) { | ||
this.alias = null; | ||
this.url = url || null; | ||
this.moduleCode = moduleCode; | ||
} | ||
setAlias(name) { | ||
if (name && typeof name === 'string') { | ||
this.alias = name; | ||
} | ||
} | ||
clone() { | ||
// @ts-ignore | ||
const cloned = new this.constructor(); | ||
cloned.url = this.url; | ||
cloned.alias = this.alias; | ||
cloned.moduleCode = this.moduleCode; | ||
return cloned; | ||
} | ||
} | ||
class TemplateManager { | ||
constructor(template, url) { | ||
this.DOMApis = new DOMApis(); | ||
this.astTree = []; | ||
this.pretreatmentStore = {}; | ||
// The url is only base url, it may also be a js resource address. | ||
this.url = url || null; | ||
if (template) { | ||
const [astTree, collectionEls] = templateParse(template, [ | ||
'meta', | ||
'link', | ||
'style', | ||
'script', | ||
]); | ||
this.astTree = astTree; | ||
this.pretreatmentStore = collectionEls; | ||
} | ||
} | ||
getNodesByTagName(...tags) { | ||
let counter = 0; | ||
const collection = {}; | ||
for (const tag of tags) { | ||
if (this.pretreatmentStore[tag]) { | ||
counter++; | ||
collection[tag] = this.pretreatmentStore[tag]; | ||
} | ||
else { | ||
this.pretreatmentStore[tag] = collection[tag] = []; | ||
} | ||
} | ||
if (counter !== tags.length) { | ||
const traverse = (node) => { | ||
if (node.type !== 'element') | ||
return; | ||
if (tags.indexOf(node.tagName) > -1) { | ||
collection[node.tagName].push(node); | ||
} | ||
for (const child of node.children) | ||
traverse(child); | ||
}; | ||
for (const node of this.astTree) | ||
traverse(node); | ||
} | ||
return collection; | ||
} | ||
// Render dom tree | ||
createElements(renderer, parent) { | ||
const elements = []; | ||
const traverse = (node, parentEl) => { | ||
let el = null; | ||
if (this.DOMApis.isCommentNode(node)) ; | ||
else if (this.DOMApis.isText(node)) { | ||
el = this.DOMApis.createTextNode(node); | ||
parentEl && parentEl.appendChild(el); | ||
} | ||
else if (this.DOMApis.isNode(node)) { | ||
const { tagName, children } = node; | ||
if (renderer[tagName]) { | ||
el = renderer[tagName](node); | ||
} | ||
else { | ||
el = this.DOMApis.createElement(node); | ||
} | ||
if (parentEl && el) | ||
parentEl.appendChild(el); | ||
if (el) { | ||
const { nodeType, _ignoreChildNodes } = el; | ||
// Filter "comment" and "document" node | ||
if (!_ignoreChildNodes && nodeType !== 8 && nodeType !== 10) { | ||
for (const child of children) { | ||
traverse(child, el); | ||
} | ||
} | ||
} | ||
} | ||
return el; | ||
}; | ||
for (const node of this.astTree) { | ||
if (this.DOMApis.isNode(node) && node.tagName !== '!doctype') { | ||
const el = traverse(node, parent); | ||
el && elements.push(el); | ||
} | ||
} | ||
return elements; | ||
} | ||
toResolveUrl(node, type, baseUrl) { | ||
var _a; | ||
const src = (_a = node.attributes) === null || _a === void 0 ? void 0 : _a.find(({ key }) => key === type); | ||
if (src) { | ||
src.value = transformUrl(baseUrl, src.value); | ||
} | ||
} | ||
ignoreChildNodesCreation(node) { | ||
if (node) { | ||
node._ignoreChildNodes = true; | ||
} | ||
return node; | ||
} | ||
findAllMetaNodes() { | ||
return this.getNodesByTagName('meta').meta; | ||
} | ||
findAllLinkNodes() { | ||
return this.getNodesByTagName('link').link; | ||
} | ||
findAllJsNodes() { | ||
return this.getNodesByTagName('script').script; | ||
} | ||
findAttributeValue(node, type) { | ||
var _a, _b; | ||
return (_b = (_a = node.attributes) === null || _a === void 0 ? void 0 : _a.find(({ key }) => key === type)) === null || _b === void 0 ? void 0 : _b.value; | ||
} | ||
cloneNode(node) { | ||
return deepMerge(node, {}); | ||
} | ||
clone() { | ||
// @ts-ignore | ||
const cloned = new this.constructor(); | ||
cloned.url = this.url; | ||
cloned.astTree = this.astTree; | ||
cloned.pretreatmentStore = this.pretreatmentStore; | ||
cloned.DOMApis = new DOMApis(this.DOMApis.document); | ||
return cloned; | ||
} | ||
} | ||
class TemplateManager { | ||
constructor(template, url) { | ||
this.DOMApis = new DOMApis(); | ||
this.astTree = []; | ||
this.pretreatmentStore = {}; | ||
// The url is only base url, it may also be a js resource address. | ||
this.url = url || null; | ||
if (template) { | ||
const [astTree, collectionEls] = templateParse(template, [ | ||
'meta', | ||
'link', | ||
'style', | ||
'script', | ||
]); | ||
this.astTree = astTree; | ||
this.pretreatmentStore = collectionEls; | ||
} | ||
} | ||
getNodesByTagName(...tags) { | ||
let counter = 0; | ||
const collection = {}; | ||
for (const tag of tags) { | ||
if (this.pretreatmentStore[tag]) { | ||
counter++; | ||
collection[tag] = this.pretreatmentStore[tag]; | ||
} | ||
else { | ||
this.pretreatmentStore[tag] = collection[tag] = []; | ||
} | ||
} | ||
if (counter !== tags.length) { | ||
const traverse = (node) => { | ||
if (node.type !== 'element') | ||
return; | ||
if (tags.indexOf(node.tagName) > -1) { | ||
collection[node.tagName].push(node); | ||
} | ||
for (const child of node.children) | ||
traverse(child); | ||
}; | ||
for (const node of this.astTree) | ||
traverse(node); | ||
} | ||
return collection; | ||
} | ||
// Render dom tree | ||
createElements(renderer, parent) { | ||
const elements = []; | ||
const traverse = (node, parentEl) => { | ||
let el = null; | ||
if (this.DOMApis.isCommentNode(node)) ; | ||
else if (this.DOMApis.isText(node)) { | ||
el = this.DOMApis.createTextNode(node); | ||
parentEl && parentEl.appendChild(el); | ||
} | ||
else if (this.DOMApis.isNode(node)) { | ||
const { tagName, children } = node; | ||
if (renderer[tagName]) { | ||
el = renderer[tagName](node); | ||
} | ||
else { | ||
el = this.DOMApis.createElement(node); | ||
} | ||
if (parentEl && el) | ||
parentEl.appendChild(el); | ||
if (el) { | ||
const { nodeType, _ignoreChildNodes } = el; | ||
// Filter "comment" and "document" node | ||
if (!_ignoreChildNodes && nodeType !== 8 && nodeType !== 10) { | ||
for (const child of children) { | ||
traverse(child, el); | ||
} | ||
} | ||
} | ||
} | ||
return el; | ||
}; | ||
for (const node of this.astTree) { | ||
if (this.DOMApis.isNode(node) && node.tagName !== '!doctype') { | ||
const el = traverse(node, parent); | ||
el && elements.push(el); | ||
} | ||
} | ||
return elements; | ||
} | ||
toResolveUrl(node, type, baseUrl) { | ||
var _a; | ||
const src = (_a = node.attributes) === null || _a === void 0 ? void 0 : _a.find(({ key }) => key === type); | ||
if (src) { | ||
src.value = transformUrl(baseUrl, src.value); | ||
} | ||
} | ||
ignoreChildNodesCreation(node) { | ||
if (node) { | ||
node._ignoreChildNodes = true; | ||
} | ||
return node; | ||
} | ||
findAllMetaNodes() { | ||
return this.getNodesByTagName('meta').meta; | ||
} | ||
findAllLinkNodes() { | ||
return this.getNodesByTagName('link').link; | ||
} | ||
findAllJsNodes() { | ||
return this.getNodesByTagName('script').script; | ||
} | ||
findAttributeValue(node, type) { | ||
var _a, _b; | ||
return (_b = (_a = node.attributes) === null || _a === void 0 ? void 0 : _a.find(({ key }) => key === type)) === null || _b === void 0 ? void 0 : _b.value; | ||
} | ||
cloneNode(node) { | ||
return deepMerge(node, {}); | ||
} | ||
clone() { | ||
// @ts-ignore | ||
const cloned = new this.constructor(); | ||
cloned.url = this.url; | ||
cloned.astTree = this.astTree; | ||
cloned.pretreatmentStore = this.pretreatmentStore; | ||
cloned.DOMApis = new DOMApis(this.DOMApis.document); | ||
return cloned; | ||
} | ||
} | ||
// Maybe we can convert "esModule" to "commonjs" in the future | ||
class JavaScriptManager { | ||
constructor(scriptCode, url) { | ||
// Need to remove duplication, so use "set" | ||
this.depsStack = new Set(); | ||
this.mimeType = ''; | ||
this.async = false; | ||
this.url = url || null; | ||
this.scriptCode = scriptCode; | ||
} | ||
isModule() { | ||
return this.mimeType === 'module'; | ||
} | ||
isInlineScript() { | ||
return Boolean(!this.url); | ||
} | ||
setMimeType(mimeType) { | ||
this.mimeType = mimeType || ''; | ||
} | ||
setAsyncAttribute(val) { | ||
this.async = Boolean(val); | ||
} | ||
setDep(node) { | ||
this.depsStack.add(node); | ||
} | ||
isSameOrigin(node) { | ||
return this.depsStack.has(node); | ||
} | ||
clone() { | ||
// @ts-ignore | ||
const cloned = new this.constructor(); | ||
cloned.url = this.url; | ||
cloned.async = this.async; | ||
cloned.mimeType = this.mimeType; | ||
cloned.scriptCode = this.scriptCode; | ||
cloned.depsStack = new Set(this.depsStack); | ||
return cloned; | ||
} | ||
} | ||
// Maybe we can convert "esModule" to "commonjs" in the future | ||
class JavaScriptManager { | ||
constructor(scriptCode, url) { | ||
// Need to remove duplication, so use "set" | ||
this.depsStack = new Set(); | ||
this.mimeType = ''; | ||
this.async = false; | ||
this.url = url || null; | ||
this.scriptCode = scriptCode; | ||
} | ||
isModule() { | ||
return this.mimeType === 'module'; | ||
} | ||
isInlineScript() { | ||
return Boolean(!this.url); | ||
} | ||
setMimeType(mimeType) { | ||
this.mimeType = mimeType || ''; | ||
} | ||
setAsyncAttribute(val) { | ||
this.async = Boolean(val); | ||
} | ||
setDep(node) { | ||
this.depsStack.add(node); | ||
} | ||
isSameOrigin(node) { | ||
return this.depsStack.has(node); | ||
} | ||
clone() { | ||
// @ts-ignore | ||
const cloned = new this.constructor(); | ||
cloned.url = this.url; | ||
cloned.async = this.async; | ||
cloned.mimeType = this.mimeType; | ||
cloned.scriptCode = this.scriptCode; | ||
cloned.depsStack = new Set(this.depsStack); | ||
return cloned; | ||
} | ||
} | ||
class Loader { | ||
constructor(options) { | ||
this.StyleManager = StyleManager; | ||
this.ModuleManager = ModuleManager; | ||
this.TemplateManager = TemplateManager; | ||
this.JavaScriptManager = JavaScriptManager; | ||
this.personalId = Symbol.for('garfish.loader'); | ||
this.lifecycle = { | ||
clear: new LoaderPlugin('clear'), | ||
loaded: new LoaderPlugin('loaded'), | ||
beforeLoad: new LoaderPlugin('beforeLoad'), | ||
}; | ||
this.options = options || {}; | ||
this.loadingList = Object.create(null); | ||
this.cacheStore = Object.create(null); | ||
} | ||
clear(scope, fileType) { | ||
const appCacheContainer = this.cacheStore[scope]; | ||
if (appCacheContainer) { | ||
appCacheContainer.clear(fileType); | ||
this.lifecycle.clear.emit({ scope, fileType }); | ||
} | ||
} | ||
clearAll(fileType) { | ||
for (const scope in this.cacheStore) { | ||
this.clear(scope, fileType); | ||
} | ||
} | ||
loadModule(url) { | ||
return this.load('modules', url, true); | ||
} | ||
// Unable to know the final data type, so through "generics" | ||
load(scope, url, isModule = false) { | ||
const { options, loadingList, cacheStore } = this; | ||
if (loadingList[url]) { | ||
return loadingList[url]; | ||
} | ||
let appCacheContainer = cacheStore[scope]; | ||
if (!appCacheContainer) { | ||
appCacheContainer = cacheStore[scope] = new AppCacheContainer(options.maxSize); | ||
} | ||
if (appCacheContainer.has(url)) { | ||
return Promise.resolve(copyResult(appCacheContainer.get(url))); | ||
} | ||
else { | ||
// If other containers have cache | ||
for (const key in cacheStore) { | ||
const container = cacheStore[key]; | ||
if (container !== appCacheContainer) { | ||
if (container.has(url)) { | ||
const result = container.get(url); | ||
cachedDataSet.add(result); | ||
appCacheContainer.set(url, result, result.fileType); | ||
return Promise.resolve(copyResult(result)); | ||
} | ||
} | ||
} | ||
} | ||
const requestConfig = mergeConfig(this, url); | ||
const resOpts = this.lifecycle.beforeLoad.emit({ url, requestConfig }); | ||
loadingList[url] = request(resOpts.url, resOpts.requestConfig) | ||
.finally(() => { | ||
loadingList[url] = null; | ||
}) | ||
.then(({ code, mimeType, result }) => { | ||
let managerCtor, fileType; | ||
if (isModule) { | ||
fileType = FileTypes.module; | ||
managerCtor = ModuleManager; | ||
} | ||
else if (isHtml(mimeType) || /\.html/.test(result.url)) { | ||
fileType = FileTypes.template; | ||
managerCtor = TemplateManager; | ||
} | ||
else if (isJs(mimeType) || /\.js/.test(result.url)) { | ||
fileType = FileTypes.js; | ||
managerCtor = JavaScriptManager; | ||
} | ||
else if (isCss(mimeType) || /\.css/.test(result.url)) { | ||
fileType = FileTypes.css; | ||
managerCtor = StyleManager; | ||
} | ||
// Use result.url, resources may be redirected | ||
const resourceManager = managerCtor | ||
? new managerCtor(code, result.url) | ||
: null; | ||
// The results will be cached this time. | ||
// So, you can transform the request result. | ||
const data = this.lifecycle.loaded.emit({ | ||
result, | ||
value: { | ||
url, | ||
resourceManager, | ||
fileType: fileType || '', | ||
code: resourceManager ? '' : code, | ||
}, | ||
}); | ||
appCacheContainer.set(url, data.value, fileType); | ||
return copyResult(data.value); | ||
}); | ||
return loadingList[url]; | ||
} | ||
} | ||
class Loader { | ||
constructor(options) { | ||
this.StyleManager = StyleManager; | ||
this.ModuleManager = ModuleManager; | ||
this.TemplateManager = TemplateManager; | ||
this.JavaScriptManager = JavaScriptManager; | ||
this.personalId = Symbol.for('garfish.loader'); | ||
this.lifecycle = { | ||
clear: new PluginManager('clear'), | ||
loaded: new PluginManager('loaded'), | ||
beforeLoad: new PluginManager('beforeLoad'), | ||
}; | ||
this.options = options || {}; | ||
this.loadingList = Object.create(null); | ||
this.cacheStore = Object.create(null); | ||
} | ||
clear(scope, fileType) { | ||
const appCacheContainer = this.cacheStore[scope]; | ||
if (appCacheContainer) { | ||
appCacheContainer.clear(fileType); | ||
this.lifecycle.clear.run({ scope, fileType }); | ||
} | ||
} | ||
clearAll(fileType) { | ||
for (const scope in this.cacheStore) { | ||
this.clear(scope, fileType); | ||
} | ||
} | ||
loadModule(url) { | ||
return this.load('modules', url, true); | ||
} | ||
// Unable to know the final data type, so through "generics" | ||
load(scope, url, isModule = false) { | ||
const { options, loadingList, cacheStore } = this; | ||
if (loadingList[url]) { | ||
return loadingList[url]; | ||
} | ||
let appCacheContainer = cacheStore[scope]; | ||
if (!appCacheContainer) { | ||
appCacheContainer = cacheStore[scope] = new AppCacheContainer(options.maxSize); | ||
} | ||
if (appCacheContainer.has(url)) { | ||
return Promise.resolve(copyResult(appCacheContainer.get(url))); | ||
} | ||
else { | ||
// If other containers have cache | ||
for (const key in cacheStore) { | ||
const container = cacheStore[key]; | ||
if (container !== appCacheContainer) { | ||
if (container.has(url)) { | ||
const result = container.get(url); | ||
cachedDataSet.add(result); | ||
appCacheContainer.set(url, result, result.fileType); | ||
return Promise.resolve(copyResult(result)); | ||
} | ||
} | ||
} | ||
} | ||
const requestConfig = mergeConfig(this, url); | ||
const resOpts = this.lifecycle.beforeLoad.run({ url, requestConfig }); | ||
loadingList[url] = request(resOpts.url, resOpts.requestConfig) | ||
.finally(() => { | ||
loadingList[url] = null; | ||
}) | ||
.then(({ code, mimeType, result }) => __awaiter(this, void 0, void 0, function* () { | ||
let managerCtor, fileType; | ||
if (isModule) { | ||
fileType = FileTypes.module; | ||
managerCtor = ModuleManager; | ||
} | ||
else if (isHtml(mimeType) || /\.html/.test(result.url)) { | ||
fileType = FileTypes.template; | ||
managerCtor = TemplateManager; | ||
} | ||
else if (isJs(mimeType) || /\.js/.test(result.url)) { | ||
fileType = FileTypes.js; | ||
managerCtor = JavaScriptManager; | ||
} | ||
else if (isCss(mimeType) || /\.css/.test(result.url)) { | ||
fileType = FileTypes.css; | ||
managerCtor = StyleManager; | ||
} | ||
// Use result.url, resources may be redirected | ||
const resourceManager = managerCtor | ||
? new managerCtor(code, result.url) | ||
: null; | ||
// The results will be cached this time. | ||
// So, you can transform the request result. | ||
const data = this.lifecycle.loaded.run({ | ||
result, | ||
value: { | ||
url, | ||
resourceManager, | ||
fileType: fileType || '', | ||
code: resourceManager ? '' : code, | ||
}, | ||
}); | ||
appCacheContainer.set(url, data.value, fileType); | ||
return copyResult(data.value); | ||
})); | ||
return loadingList[url]; | ||
} | ||
} | ||
exports.JavaScriptManager = JavaScriptManager; | ||
exports.Loader = Loader; | ||
exports.ModuleManager = ModuleManager; | ||
exports.StyleManager = StyleManager; | ||
exports.TemplateManager = TemplateManager; | ||
exports.JavaScriptManager = JavaScriptManager; | ||
exports.Loader = Loader; | ||
exports.ModuleManager = ModuleManager; | ||
exports.StyleManager = StyleManager; | ||
exports.TemplateManager = TemplateManager; | ||
Object.defineProperty(exports, '__esModule', { value: true }); | ||
Object.defineProperty(exports, '__esModule', { value: true }); | ||
}))); |
{ | ||
"name": "@garfish/loader", | ||
"version": "0.0.58", | ||
"version": "0.0.59-alpha.0.2", | ||
"description": "loader module.", | ||
@@ -40,3 +40,3 @@ "keywords": [ | ||
}, | ||
"gitHead": "7af85a1c1836eaf3fa50c49f0b20fe0f432ca836" | ||
"gitHead": "fbc01d731786e3e5a4b573aab72f0ac42107aad7" | ||
} |
5384
199262
17