tserkov / vue-plugin-load-script Goto Github PK
View Code? Open in Web Editor NEWA Vue plugin for injecting remote scripts.
License: MIT License
A Vue plugin for injecting remote scripts.
License: MIT License
How to import something like loadScript
using the Composition API?
I was wondering if there is a way to compile and build a vue app which is finally a js file and load it into our current vue app and use it as a remote component? can u make an example. tnx. can I export a vue component or a full vue app as js file and then load it using your library'? can u please explain in detail how to export properly and how to load it in another vue app.. tnc
TS2769: No overload matches this call. Overload 1 of 2, '(plugin: PluginObject<unknown> | PluginFunction<unknown>, options?: unknown): VueConstructor<Vue>', gave the following error. Argument of type 'typeof import("/Users/test/node_modules/vue-plugin-load-script/index")' is not assignable to parameter of type 'PluginObject<unknown> | PluginFunction<unknown>'. Overload 2 of 2, '(plugin: PluginObject<any> | PluginFunction<any>, ...options: any[]): VueConstructor<Vue>', gave the following error. Argument of type 'typeof import("/Users/test/node_modules/vue-plugin-load-script/index")' is not assignable to parameter of type 'PluginObject<any> | PluginFunction<any>'.
I have Vue 2.x and nuxt application.
When i import from npm package i'm getting "Unexpected token export".
When i copied code from github and put in ordinary .js file and import that - everything is ok
Any ideas ?
The docs are rather confusing, as there is no explicit explanation or example of how to call a script after loading it.
I am trying to use this module in an SFC application that I'm developing with NPM inside Visual Studio Code to access the Jitsi videoconference API.
This is in my mounted() code block:
Vue.loadScript("https://meet.jit.si/external_api.js")
.then(() => {
let api = new JitsiMeetExternalAPI("meet.jit.si",{ roomName: 'dummy', parentNode: document.querySelector('#jitsi') })
Vue.set(outerthis,"api",api)
})
.catch(error => {
// Failed to fetch script
console.log(error)
});
...but it complains that JitsiMeetExternalAPI was not defined. If I put in a dummy declaration for JitsiMeetExternalAPI at the start of the script, that doesn't help, as it tries to use that instead, and complains that it isn't a constructor at runtime. The library has definitely loaded -- I can access the JitsiMeetExternalAPI from the top level; I just don't see it in my Vue code.
If the problem is just that it won't work with a linter as the identifier isn't defined until runtime, I think that's definitely worth mentioning in the docs, because most of us will have linting on by default. However, I'm left wondering whether this will work at all on a compiled SFC application (i.e. not being served dynamically on node), as compilation mungs identifiers, and what's it going to do with an undefined one?
Some clarification of this in the instructions would be very much appreciated.
The script was loading when you navigate to the route first time but on second route visit it wasn't loading or remaining there, means the function which I was calling was working on first vue router call but not on subsequent second or more calls.
now even if I call the same load script on each route visit it will keep calling the server for the script which I don't think is a proper solution.
I have just set up a new Vue CLI3 project and installed the load-script plugin. I have also copied the code into my main.js and linked it to jquery. Right away I get an error:
Could not find a declaration file for module 'vue-plugin-load-script'. '/Users/me/Documents/_Private/_Projects/_TECH/vue-test03/node_modules/vue-plugin-load-script/index.js' implicitly has an 'any' type.
Try npm install @types/vue-plugin-load-script
if it exists or add a new declaration (.d.ts) file containing declare module 'vue-plugin-load-script';
I have then tried to run: npm install @types/vue-plugin-load-script
Error:
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: [email protected] (node_modules/idb-connector):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for [email protected]: wanted {"os":"aix","arch":"any"} (current: {"os":"darwin","arch":"x64"})
Any help is appreciated.
use in ios 9.0
Neither method works now, waste of time
The type definition added in v1.3.4 in index.d.ts
prevents any local declaration of of "vue/types/vue" from being loaded.
When I upgraded from vue-plugin-load-script v1.3.3 to v1.3.4, my build started breaking with complaints that my injected services on the Vue instance were not valid properties. My type definition file to define custom properties on the Vue instance has stopped having any effect. If I manually delete the code
declare module "vue/types/vue" {
interface Vue {
$loadScript: (src: string) => Promise<HTMLScriptElement>;
$unloadScript: (src: string) => Promise<void>;
}
}
from this package's index.d.ts
file, my own file will load again, allowing my build to succeed again.
It's my understanding that this type definition on the Vue object should be left to the consumer of the package, but if there's something I'm missing, I'd be happy to be wrong.
I'm getting Uncaught SyntaxError: Unexpected token <
for the following inclusion in my App.vue
file.
export default {
name: 'app',
components: {
Home
},
created () {
this.$loadScript("./assets/js/home.js");
}
}
How do I fix it?
When trying to use this package with Vue 3 I get the TypeError mentioned above. I followed the installation guide for Vue 3 which states to use npm install --save vue-plugin-load-script@">=2.0.0"
but when I do this my package.json shows a different version:
"vue-plugin-load-script": "^1.3.2"
I also use typescript but have properly declared the type by doing declare module 'vue-plugin-load-script';
in my index.d.ts file. My main.ts looks like this:
import { createApp } from 'vue';
import App from './App.vue';
import LoadScript from 'vue-plugin-load-script';
const app = createApp(App).use(LoadScript);
app.mount('#app');
What is wrong here?
It'd be useful to be able to pass a flag to enable these options on the generated script tag.
When this code tries to detect whether the script has been loaded yet, it checks if the script is on the DOM, not whether it's actually loaded.
Since the script is being loaded async
, there's a decent chance a second call would resolve before the script has actually loaded.
I have the site in a staging server and it's working fine. It has been working fine in my dev environment for months but when I fired up the project today I get errors. In the browser (Chrome) I get this: "Uncaught ReferenceError: undefinedLoadScript is not defined at Module../node_modules/vue-plugin-load-script/index.js"
I'm building with webpack and get this in VS Code:
module "...node_modules/vue-plugin-load-script/index"
Could not find a declaration file for module 'vue-plugin-load-script'. '.../node_modules/vue-plugin-load-script/index.js' implicitly has an 'any' type.
Trynpm install @types/vue-plugin-load-script
if it exists or add a new declaration (.d.ts) file containingdeclare module 'vue-plugin-load-script';
ts(7016)
I've been trying to figure this out for a while now. I can't find anyone else reporting a solution. I'm totally baffled, especially since it's been fine before today.
My webpack file, package.json, and the js file are below. Thanks for taking a look at this!
webpack.config.js:
const path = require('path');
const publicPath = 'http://localhost:3000/';
const config = {
'domain': 'urphysiciangroup.cwdev',
};
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const BrowserSyncPlugin = require('browser-sync-webpack-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const ManifestPlugin = require('webpack-manifest-plugin');
const HardSourceWebpackPlugin = require('hard-source-webpack-plugin');
const webpack = require('webpack');
const autoprefixer = require('autoprefixer');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
// Configuration for the ExtractTextPlugin โ DRY rule.
const extractConfig = {
use: [
// "postcss" loader applies autoprefixer to our CSS.
{
loader: 'raw-loader'
},
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: [
autoprefixer({
flexbox: 'no-2009',
}),
],
},
},
// "sass" loader converts SCSS to CSS.
{
loader: 'sass-loader',
options: {
outputStyle: 'production' === process.env.MODE ? 'compressed' : 'nested',
},
},
],
};
module.exports = {
mode: process.env.MODE,
entry: {
main: './src/index',
admin: './src/admin/index',
},
resolve: {
alias: {
'node_modules': path.join(__dirname, 'node_modules'),
'SRC': path.join(__dirname, 'src'),
vue: 'vue/dist/vue.min.js'
}
},
output: {
pathinfo: true,
path: path.resolve(__dirname, 'dist'),
filename: '[name].js'
},
devtool: 'source-map',
module: {
rules: [{
loader: 'babel-loader',
test: /\.js$/,
exclude: /node_modules/,
query: {
plugins: ['lodash'],
presets: [
['@babel/preset-env', {
'targets': {
'node': 6
}
}]
]
}
},
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/env', '@babel/react']
}
}
},
{
test: /\.(s)css$/,
exclude: /(node_modules|bower_components)/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [{
loader: 'css-loader',
options: {
minimize: true,
sourcemap: true,
publicPath: '/'
}
},
{
loader: 'postcss-loader'
},
{
loader: 'sass-loader'
}
]
})
},
{
test: /\.css$/,
include: /node_modules/,
loaders: ['style-loader', 'css-loader'],
},
{
test: /\.(png|svg|jpg|gif)$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'file-loader',
options: {
name: '[name].[ext]',
publicPath: '../'
}
}
},
{
test: /\.(woff(2)?|ttf|eot|svg)(\?v=\d+\.\d+\.\d+)?$/,
exclude: /(node_modules|bower_components)/,
use: [{
loader: 'file-loader',
options: {
name: '[name].[ext]',
publicPath: '../'
}
}]
}
]
},
plugins: [
new BundleAnalyzerPlugin(),
new HardSourceWebpackPlugin(),
new ExtractTextPlugin({
filename: (getPath) => {
return getPath('css/[name].css').replace('css/js', 'css');
},
allChunks: true
}),
new BrowserSyncPlugin({
proxy: config.domain,
files: [
'**/*.php'
],
reloadDelay: 0
}),
new UglifyJsPlugin({
sourceMap: true,
uglifyOptions: {
ie8: false,
ecma: 8,
mangle: true,
output: {
comments: false,
beautify: false
},
warnings: false
}
}),
new ManifestPlugin({
fileName: 'asset-manifest.json',
writeToFileEmit: true
})
]
};
package.json:
{
"name": "cw",
"version": "1.0.0",
"description": "",
"main": "index.js",
"directories": {
"lib": "lib"
},
"browserslist": [
"> 1%",
"ie > 9",
"last 4 versions",
"Firefox ESR",
"not ie < 9"
],
"babel": {
"plugins": [
"lodash"
],
"presets": [
[
"@babel/env",
{
"targets": {
"node": 6
}
}
]
]
},
"scripts": {
"test": "test",
"start": "cross-env BABEL_ENV=default MODE=development webpack --watch",
"serve": "webpack-dev-server --open",
"build": "cross-env BABEL_ENV=default MODE=production webpack"
},
"author": "",
"license": "ISC",
"dependencies": {
"@wordpress/components": "^7.2.2",
"axios": "^0.18.1",
"babel-preset-react": "^6.24.1",
"browser-sync": "^2.26.7",
"extract-loader": "^2.0.1",
"gsap": "^2.1.2",
"hard-source-webpack-plugin": "^0.11.0",
"lodash": "^4.17.15",
"moment": "^2.24.0",
"normalize.css": "^8.0.1",
"orgchart": "^2.1.5",
"raw-loader": "^0.5.1",
"react": "^16.9.0",
"react-render-html": "^0.6.0",
"script-loader": "^0.7.2",
"smoothscroll-polyfill": "^0.4.4",
"url-loader": "^1.1.2",
"vue": "^2.6.10",
"vue-axios": "^2.1.4",
"vue-carousel": "^0.18.0",
"vue-organization-chart": "^1.1.6"
},
"devDependencies": {
"@babel/cli": "^7.5.5",
"@babel/core": "^7.5.5",
"@babel/preset-env": "^7.5.5",
"@babel/preset-react": "^7.0.0",
"@wordpress/babel-preset-default": "^1.3.0",
"autoprefixer": "^9.6.1",
"babel-core": "^7.0.0-bridge.0",
"babel-eslint": "^8.2.2",
"babel-loader": "^7.1.5",
"babel-plugin-lodash": "^3.3.4",
"babel-plugin-transform-react-jsx": "^6.24.1",
"babel-preset-env": "^1.7.0",
"browser-sync-webpack-plugin": "^2.2.2",
"classnames": "^2.2.5",
"cross-env": "^5.1.4",
"css-loader": "^0.28.11",
"eslint": "^5.16.0",
"extract-text-webpack-plugin": "^4.0.0-beta.0",
"file-loader": "^1.1.11",
"mini-css-extract-plugin": "^0.4.5",
"node-sass": "^4.12.0",
"optimize-css-assets-webpack-plugin": "^4.0.3",
"path": "^0.12.7",
"postcss-loader": "^2.1.6",
"raw-loader": "^0.5.1",
"sass-loader": "^7.3.1",
"style-loader": "^0.21.0",
"uglifyjs-webpack-plugin": "^1.3.0",
"vue-plugin-load-script": "^1.2.0",
"webpack": "^4.39.2",
"webpack-bundle-analyzer": "^3.4.1",
"webpack-cli": "^3.3.7",
"webpack-manifest-plugin": "^2.0.4"
}
}
maps.js:
/* global map_options: false, google: false */
import Vue from 'vue';
import LoadScript from 'vue-plugin-load-script';
const mapCanvas = document.querySelectorAll('.cw-map-canvas');
if (mapCanvas.length) {
Vue.use(LoadScript);
Vue.loadScript('https://maps.googleapis.com/maps/api/js?key='+map_options.maps_api_key).then(() => {
for (let m = 0; m < mapCanvas.length; m++) {
const mapCan = mapCanvas[m];
let id = mapCan.getAttribute('id');
let address = mapCan.getAttribute('data-address');
new Vue({
el: mapCan,
data: {
results: {},
},
mounted: function() {
this.draw_map();
},
methods: {
draw_map: function() {
let map;
// let bounds = new google.maps.LatLngBounds();
let mapOptions = {
mapTypeId: 'roadmap',
zoom: parseInt(map_options.zoom),
scrollwheel: map_options.scroll_zoom,
disableDefaultUI: map_options.controls
};
let customMapType = new google.maps.StyledMapType([
{
stylers: [
{hue: map_options.hue},
{visibility: map_options.visibility},
{saturation: map_options.satch},
]
}
], {
name: 'Custom Style'
});
let customMapTypeId = 'custom_style';
// Display a map on the page
map = new google.maps.Map(document.getElementById(id), mapOptions);
let geocoder = new google.maps.Geocoder();
geocoder.geocode({ 'address': address }, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
let position = new google.maps.LatLng(results[0].geometry.location.lat(), results[0].geometry.location.lng());
new google.maps.Marker({
position: position,
map: map,
animation: google.maps.Animation.DROP,
});
map.setOptions({center:position});
}
});
map.mapTypes.set(customMapTypeId, customMapType);
map.setMapTypeId(customMapTypeId);
}
}
});
}
}).catch((e) => {
console.log(e);
});
}
If I want to include the same script from two different components that exist on the same page it would be great if the scripts were not loaded twice.
function loadStyle (href) {
return new Promise(function (resolve, reject) {
let shouldAppend = false;
let el = document.querySelector('link[href="' + href + '"]');
if (!el) {
el = document.createElement('link');
el.rel = 'stylesheet';
el.async = true;
el.href = href;
shouldAppend = true;
}
else if (el.hasAttribute('data-loaded')) {
resolve(el);
return;
}
el.addEventListener('error', reject);
el.addEventListener('abort', reject);
el.addEventListener('load', function loadScriptHandler() {
el.setAttribute('data-loaded', true);
resolve(el);
});
if (shouldAppend) document.head.appendChild(el);
});
};
I updated a project's packages and one of those was moving to a newer typescript (4.5.4). Somewhere in there calling Vue.use(LoadScript) caued the following typescript check errors.
Overload 1 of 2, '(plugin: PluginObject<unknown> | PluginFunction<unknown>, options?: unknown): VueConstructor<Vue>', gave the following error.
Argument of type 'typeof import("C:/Users/bwilson/sc/ts/node_modules/vue-plugin-load-script/index")' is not assignable to parameter of type 'PluginObject<unknown> | PluginFunction<unknown>'.
Type 'typeof import("XXX/node_modules/vue-plugin-load-script/index")' is not assignable to type 'PluginFunction<unknown>'.
Type 'typeof import(XXX/node_modules/vue-plugin-load-script/index")' provides no match for the signature '(Vue: VueConstructor<Vue>, options?: unknown): void'.
Overload 2 of 2, '(plugin: PluginObject<any> | PluginFunction<any>, ...options: any[]): VueConstructor<Vue>', gave the following error.
Argument of type 'typeof import("XXX/node_modules/vue-plugin-load-script/index")' is not assignable to parameter of type 'PluginObject<any> | PluginFunction<any>'.
Type 'typeof import("XXX/node_modules/vue-plugin-load-script/index")' is not assignable to type 'PluginFunction<any>'.
Type 'typeof import("XXX/node_modules/vue-plugin-load-script/index")' provides no match for the signature '(Vue: VueConstructor<Vue>, options?: any): void'.
I was able to clear the error by adding the following to your index.d.ts:
export default class LoadScript {
static install: PluginFunction<undefined>;
}
I'm not a typescript expert so I don't know if that is the correct thing to do.
There is no ts support currently.
I have this error message
Could not find a declaration file for module 'vue-plugin-load-script'. 'D:/path/to/project/node_modules/vue-plugin-load-script/index.js' implicitly has an 'any' type.
For other people, This is my solution
// file @types/vue-plugin-load-script.module.d.ts
declare module "vue-plugin-load-script";
// file @types/vue.global.d.ts
declare module "vue/types/vue" {
interface Vue {
$loadScript: (src: string) => Promise<void>;
$unloadScript: (src: string) => Promise<void>;
}
}
The usage of export default LoadScript
seem to break vue ssr server entry, I could fix this locally
while changing the export to common js module.exports = LoadScript;
Occasionally I'll get the following error. If I do an npm update it'll fix it, but I'd rather not have to update sites constantly. Any ideas of what might be causing it?
Uncaught ReferenceError: undefinedLoadScript is not defined
at Module../node_modules/vue-plugin-load-script/index.js (index.js:43)
at n (bootstrap:19)
at Object../src/js/maps.js (maps.js:4)
at n (bootstrap:19)
at bootstrap:83
at bootstrap:83
./node_modules/vue-plugin-load-script/index.js @ maps.js?ver=1592255756:1
n @ maps.js?ver=1592255756:1
./src/js/maps.js @ maps.js?ver=1592255756:1
n @ maps.js?ver=1592255756:1
(anonymous) @ maps.js?ver=1592255756:1
(anonymous) @ maps.js?ver=1592255756:1
Here is my implementation:
/* global map_options: false, google: false */
import Vue from 'vue';
import LoadScript from 'vue-plugin-load-script';
const mapCanvas = document.querySelectorAll('.cw-map-canvas');
if (mapCanvas.length) {
Vue.use(LoadScript);
Vue.loadScript('https://maps.googleapis.com/maps/api/js?key='+map_options.maps_api_key).then(() => {
for (let m = 0; m < mapCanvas.length; m++) {
const mapCan = mapCanvas[m];
let id = mapCan.getAttribute('id');
let address = mapCan.getAttribute('data-address');
new Vue({
el: mapCan,
data: {
results: {},
},
mounted: function() {
this.draw_map();
},
methods: {
draw_map: function() {
let map;
// let bounds = new google.maps.LatLngBounds();
let mapOptions = {
mapTypeId: 'roadmap',
zoom: parseInt(map_options.zoom),
scrollwheel: map_options.scroll_zoom,
disableDefaultUI: map_options.controls
};
let customMapType = new google.maps.StyledMapType([
{
stylers: [
{hue: map_options.hue},
{visibility: map_options.visibility},
{saturation: map_options.satch},
]
}
], {
name: 'Custom Style'
});
let customMapTypeId = 'custom_style';
// Display a map on the page
map = new google.maps.Map(document.getElementById(id), mapOptions);
let geocoder = new google.maps.Geocoder();
geocoder.geocode({ 'address': address }, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
let position = new google.maps.LatLng(results[0].geometry.location.lat(), results[0].geometry.location.lng());
new google.maps.Marker({
position: position,
map: map,
animation: google.maps.Animation.DROP,
});
map.setOptions({center:position});
}
});
map.mapTypes.set(customMapTypeId, customMapType);
map.setMapTypeId(customMapTypeId);
}
}
});
}
}).catch((e) => {
console.log(e);
});
}
Hi! First of all, this is a great library! I'm trying to use it to inject MathJax with my Vuejs project. It works fine with a newly loaded page or a reloaded page. However, if I do a $router.push to another page, even though there is a $loadScript called in the created() method, it returns undefined without loading the library or throwing an error. Can you help me with this matter? Thanks so much!
Why do you suggest to install the package as "npm install --save-dev" or "yarn add --dev".
If the code is executed in production, we should add it without the dev parameters, correct?
Is there any way to load several scripts simultaneously?
At the moment I can only do this:
this.$loadScript("/js/script1.js").then(() => {
this.$loadScript("/js/script2.js").then(() => {
this.$loadScript("/js/script3.js").then(() => {
// Do something after all 3 scripts loaded.
}).catch(() => {
// Failed to fetch script3
});
}).catch(() => {
// Failed to fetch script2
});
}).catch(() => {
// Failed to fetch script1
});
But this solution has one big downside. Befole loading script2 it will wait for the script1. It increases the loading time dramatically.
It would be nice to have something like this where all scripts are loaded asynchniously and only when all of them are loaded the then
is called:
this.$loadScript(["/js/script1.js", "/js/script2.js", "/js/script3.js"]).then(() => {
// Do something after all 3 scripts loaded.
}).catch(() => {
// Failed to fetch at least one of those scripts
});
When my network is not allowed to access the scrips, I can't deal with the timeout error.
Hi, I can import scripts properly, but it creates a script element between head tags as below. This causes an error on the console. I wonder what's the solution?
<script type="text/javascript" async="" src="[object Object]" data-loaded="true"></script>
running a node ssr server throws the following error :
vue-plugin-load-script/index.js:44
export { unloadScript, loadScript };
^^^^^^
SyntaxError: Unexpected token 'export'
at Object.compileFunction (node:vm:352:18)
at wrapSafe (node:internal/modules/cjs/loader:1033:15)
at Module._compile (node:internal/modules/cjs/loader:1069:27)
at Object.Module._extensions..js (node:internal/modules/cjs/loader:1159:10)
at Module.load (node:internal/modules/cjs/loader:981:32)
at Function.Module._load (node:internal/modules/cjs/loader:822:12)
at Module.require (node:internal/modules/cjs/loader:1005:19)
at require (node:internal/modules/cjs/helpers:102:18)
at Object.vue-plugin-load-script (public/js/ssr.js:7126:18)
at webpack_require (js/ssr.js:7161:41)
ReferenceError: Cannot access 'WEBPACK_DEFAULT_EXPORT' before initialization
at Module.default (public/js/ssr.js:5093:42)
at /node_modules/@inertiajs/inertia-vue3/dist/index.js:1:9383
at processTicksAndRejections (node:internal/process/task_queues:96:5)
i have ckeditor and google's recaptcha external js files and i only want to add them to the pages with forms. when i load them on mounted () { they are still loaded when i go to a new page. is there a way to unload them when i go to a new page? i am use vue, vuetify and vue-router.
thanks! i was surprised that vue didn't include your feature officially. it would be nice to have a way to load external js files and not cause "error: 'CKEDITOR' is not defined" when i "npm run serve"
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.