diff --git a/build/embed-packager.js b/build/embed-packager.js new file mode 100644 index 000000000..0c7776bd5 --- /dev/null +++ b/build/embed-packager.js @@ -0,0 +1,276 @@ +/* eslint-disable */ + +var rLinkScript = /(||$))|(?:(\s*]*)>([\s\S]*?)<\/script>)|(?:\s*(]*?)(?:\/)?>)|(]*)>([\s\S]*?)<\/style>)))()?\n?/ig; +var rScriptType = /type=('|")(.*?)\1/i; +var rSrcHref = /\s*(?:src|href)=('|")(.+?)\1/i; +var rRefStyle = /rel=('|")stylesheet\1/i; +var path = require('path'); +var css = require('css'); +var rSourceMap = /(?:\/\/\#\s*sourceMappingURL[^\r\n\'\"]*|\/\*\#\s*sourceMappingURL[^\r\n\'\"]*\*\/)(?:\r?\n|$)/ig; +var caches = {}; +var createResource = fis.require('postpackager-loader/lib/resource.js'); + +function prefixCss(code, prefix) { + var cssAst = css.parse(code); + prefixNode(cssAst); + return css.stringify(cssAst) + '\n' + prefix + '{background-color: #f0f3f4;position: relative;} \n' + prefix + ' .app {padding-bottom:0;}'; + + function prefixSelector(sel){ + if (sel.match(/^@/)) return sel; + var m = sel.match(/(^| )(body|html)($|\W.*)/i); + if (m) + return m[1] + prefix + m[3]; + else if (sel.match(/^(?:\.fr-|\.fa)/)) + return sel; + else + return prefix + ' ' + sel; + } + + function prefixNode(node) { + if (node.selectors) { + node.selectors = node.selectors.map(prefixSelector); + } else if (node.stylesheet) { + node.stylesheet.rules.forEach(prefixNode); + } else if (node.rules) { + node.rules.forEach(prefixNode); + } + } +} + +function replaceSrc(code) { + var cssAst = css.parse(code); + let count = 0; + replaceNode(cssAst); + return css.stringify(cssAst); + + function replaceDeclaration(sel) { + if (sel.property == 'src' && ~sel.value.indexOf('url')) { + sel.value = sel.value.replace(/\/sdk/g, '.'); + } + return sel; + } + + function replaceNode(node) { + // font-awesome的在 1 个 declarations中, bootstrap 的字体路径在 58 declarations中,后续的就不再遍历了,直接返回 + if (count >= 58) return node; + if (node.declarations) { + count++; + node.declarations = node.declarations.map(replaceDeclaration); + } else if (node.stylesheet) { + node.stylesheet.rules.forEach(replaceNode); + } else if (node.rules) { + node.rules.forEach(replaceNode); + } + } +} + +function unicodeJs(str) { + return str.replace(/([\u4E00-\u9FA5]|[\uFE30-\uFFA0]|[\u2019])/g, function(_, value){ + return '\\u' + value.charCodeAt(0).toString(16); + }); +} + +module.exports = function (ret, pack, settings, opt) { + var root = fis.project.getProjectPath(); + + var tpl = ret.pkg['/examples/embed.tpl']; + + if (tpl && tpl._fromCache && caches[tpl.id]) { + tpl.setContent(caches[tpl.id]); + return; + } else if (!tpl) { + return; + } + + var mapping = {}; + var contents = tpl.getContent(); + var cssContents = ''; + var jsContents = ''; + var entryJs = ''; + var resource = tpl._resource; + + var files = ret.pkg; + Object.keys(files).forEach(function (subpath) { + var file = files[subpath]; + + mapping[file.getUrl()] = file; + }); + + contents.replace(rLinkScript, function (all, comment, script, attrs, body, link, lattrs, style, sattrs, sbody, ignored) { + // 忽略注释。 + if (comment || ignored) { + return all; + } + + if (script && !body.trim() && rSrcHref.test(attrs)) { + all = ''; + } else if (script && !rScriptType.test(attrs) || rScriptType.test(attrs) && ~['text/javascript', 'application/javascript'].indexOf(RegExp.$2.toLowerCase())) { + entryJs += ';' + body; + all = ''; + } else if (link && rRefStyle.test(lattrs) && rSrcHref.test(lattrs)) { + var href = RegExp.$2; + let file = resource.getFileByUrl(href); + + if (!file) { + file = resource.getFileByUrl(fis.util(path.join(path.dirname(tpl.release), href))); + } + + if (!file) { + file = mapping[href]; + } + + + if (file && (file.filename === 'font-awesome' || file.filename === 'bootstrap')) { + let content = replaceSrc(file.getContent()) + cssContents += '\n' + content; + } else { + file && (cssContents += '\n' + file.getContent()); + } + all = ''; + } else if (style && sbody.trim()) { + cssContents += sbody; + all = ''; + } + + return all; + }); + + (function (file, ret) { // tpl ret + var resource = createResource(ret, file, {}); + var asyncJsContents = ''; + + file.requires.forEach(function(id) { + resource.add(id); + }); + + file.asyncs.forEach(function(id) { + resource.add(id, true); + }); + + var loaded = resource.loaded; + var res = {}; + var pkg = {}; + + Object.keys(loaded).forEach(function (id) { + if (!/\.(jsx|tsx|js|ts)?$/.test(id)) { + return; + } + var file = resource.getFileById(id); + var isAsync = loaded[id]; + + if (file) { + if (isAsync) { + asyncJsContents += ';/*!' +id+ '*/\n' + file.getContent(); + + var node = resource.getNode(id); + if (node.type !== 'js') { + return; + } + + var item = { + type: node.type + }; + + if (node.deps) { + + // 过滤掉不是 js 的文件依赖。 + var deps = node.deps.filter(function(id) { + if (resource.loaded[id] !== false) { + var dep = resource.getFileById(id); + + if (dep) { + return dep.isJsLike; + } + } + + return false; + }); + + if (deps.length) { + deps.forEach(function(v, k) { + var dep = resource.getFileById(v); + + if (dep && dep.moduleId) { + deps[k] = dep.moduleId; + } else { + deps[k] = v.replace(/\.(es6|jsx|tsx|ts|coffee)$/g, '.js'); + } + + }); + item.deps = deps; + } + } + + var moduleId = node.extras && node.extras.moduleId || file && file.moduleId || id.replace(/\.js$/i, ''); + res[moduleId] = item; + + } else { + jsContents += ';/*!' +id+ '*/\n' + file.getContent(); + } + } + }); + + var modjs = resource.getFileByUrl('/examples/mod.js'); + var resouceMap = ''; + var asyncPkgs = {}; + + if (asyncJsContents) { + asyncJsContents = asyncJsContents.replace(rSourceMap, ''); + asyncJsContents = unicodeJs(asyncJsContents); + + let asyncJsFile = fis.file(root + '/sdk/', tpl.filename + '_aio_async.js'); + asyncJsFile.setContent(asyncJsContents); + ret.pkg[asyncJsFile.subpath] = asyncJsFile; + + Object.keys(res).forEach(function (moduleId) { + res[moduleId].pkg = 'paio'; + }); + + pkg['paio'] = { + uri: asyncJsFile.filename + '.js', + type: 'js' + }; + + asyncPkgs = {res: res, pkg: pkg}; + } + + resouceMap = `(function(){ + var dirname = ''; + try { + throw new Error() + } catch (e) { + const stackPath = e.stack.substring(0, e.stack.indexOf('embed')); + const rgx = /(?<=.)(http|https|file).*/; + dirname = (rgx.exec(stackPath)||[])[0] || ''; + } + var config = ${JSON.stringify(asyncPkgs)}; + var pkg = config.pkg; + Object.keys(pkg).forEach(function(key){ + if (pkg[key].uri) { + pkg[key].uri = dirname + pkg[key].uri; + } + }); + amis.require.resourceMap(config); + })();`; + jsContents = modjs.getContent() + '\n' + resouceMap + '\n' + jsContents; + })(tpl, ret); + + jsContents = jsContents.replace(rSourceMap, ''); + jsContents = unicodeJs(jsContents); + + let jsFile = fis.file(root + '/sdk/', tpl.filename + '_aio.js'); + jsFile.setContent(jsContents); + ret.pkg[jsFile.subpath] = jsFile; + + cssContents = prefixCss(cssContents, '.amis-scope'); + let cssFile = fis.file(root + '/sdk/', tpl.filename + '_aio.css'); + cssFile.setContent(cssContents); + ret.pkg[cssFile.subpath] = cssFile; + entryJs = entryJs.replace(/[\r\n]/g, '').replace(/"/g, '\\"'); + contents = "(function(css, js) {"+entryJs+"})('" + cssFile.getUrl() +"', '" + jsFile.getUrl() + "')"; + contents += '\n// jsurl=' + jsFile.getUrl(); + contents += '\n// cssurl=' + cssFile.getUrl(); + + tpl.setContent(contents); + caches[tpl.id] = contents; +}; diff --git a/docs/renderers/Field.md b/docs/renderers/Field.md index bf211701d..0f8368108 100644 --- a/docs/renderers/Field.md +++ b/docs/renderers/Field.md @@ -99,7 +99,8 @@ ### Json(Field) -todo +- `type` 请设置为 `json`。 +- `levelExpand` 开始展开的层级,默认为 1,如设置不展开,则设置为 0 ### Date(Field) diff --git a/examples/embed.tpl b/examples/embed.tpl new file mode 100644 index 000000000..822ac681f --- /dev/null +++ b/examples/embed.tpl @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + diff --git a/examples/embed.tsx b/examples/embed.tsx new file mode 100644 index 000000000..375ffbbe5 --- /dev/null +++ b/examples/embed.tsx @@ -0,0 +1,202 @@ +import './polyfills/index'; +import React from 'react'; +import { render as renderReact } from 'react-dom'; +import axios from 'axios'; +import copy from 'copy-to-clipboard'; + +import qs from 'qs'; +import { + toast, + alert, + confirm, + ToastComponent, + AlertComponent, + render as renderAmis +} from '../src/index'; + +export function embed(container: string | HTMLElement, schema: any, data: any, env: any) { + if (typeof container === 'string') { + container = document.querySelector(container) as HTMLElement; + } + if (!container) { + console.error('选择器不对,页面上没有此元素'); + return; + } else if (container.tagName === 'BODY') { + let div = document.createElement('div'); + container.appendChild(div); + container = div; + } + container.classList.add('amis-scope'); + let scoped:any; + const normalizeLink = (to:string) => { + to = to || ''; + const location = window.location; + + if (to && to[0] === '#') { + to = location.pathname + location.search + to; + } else if (to && to[0] === '?') { + to = location.pathname + to; + } + + const idx = to.indexOf('?'); + const idx2 = to.indexOf('#'); + let pathname = ~idx ? to.substring(0, idx) : ~idx2 ? to.substring(0, idx2) : to; + let search = ~idx ? to.substring(idx, ~idx2 ? idx2 : undefined) : ''; + let hash = ~idx2 ? to.substring(idx2) : ''; + + if (!pathname) { + pathname = location.pathname; + } else if (pathname[0] != '/' && !/^https?\:\/\//.test(pathname)) { + let relativeBase = location.pathname; + const paths = relativeBase.split('/'); + paths.pop(); + let m; + while ((m = /^\.\.?\//.exec(pathname))) { + if (m[0] === '../') { + paths.pop(); + } + pathname = pathname.substring(m[0].length); + } + pathname = paths.concat(pathname).join('/'); + } + + return pathname + search + hash; + } + + const responseAdpater = (api:any) => (value:any) => { + let response = value.data; + if (env && env.responseAdpater) { + const url = api.url; + const idx = api.url.indexOf('?'); + const query = ~idx ? qs.parse(api.url.substring(idx)) : {}; + const request = { + ...api, + query: query, + body: api.data + }; + response = env.responseAdpater(api, response, query, request); + } else { + if (response.hasOwnProperty('errno')) { + response.status = response.errno; + response.msg = response.errmsg; + } else if (response.hasOwnProperty('no')) { + response.status = response.no; + response.msg = response.error; + } + } + + const result = { + ...value, + data: response + } + return result; + }; + + renderReact(( +
+ + + + {renderAmis(schema, { + ...data, + scopeRef: (ref:any) => scoped = ref + }, { + getModalContainer: () => document.querySelector('.amis-scope'), + notify: (type: string, msg: string) => toast[type] ? toast[type](msg, type === 'error' ? '系统错误' : '系统消息') : console.warn('[Notify]', type, msg), + alert, + confirm, + updateLocation: (to:any, replace:boolean) => { + if (to === 'goBack') { + return window.history.back(); + } + + replace || (location.href = normalizeLink(to)); + }, + isCurrentUrl: (to:string) => { + const link = normalizeLink(to); + const location = window.location; + let pathname = link; + let search = ''; + const idx = link.indexOf('?'); + if (~idx) { + pathname = link.substring(0, idx); + search = link.substring(idx); + } + + if (search) { + if (pathname !== location.pathname || !location.search) { + return false; + } + + const query = qs.parse(search.substring(1)); + const currentQuery = qs.parse(location.search.substring(1)); + + return Object.keys(query).every(key => query[key] === currentQuery[key]); + } else if (pathname === location.pathname) { + return true; + } + + return false; + }, + jumpTo: (to:string, action?:any) => { + if (to === 'goBack') { + return window.history.back(); + } + + to = normalizeLink(to); + + if (action && action.actionType === 'url') { + action.blank === false ? (window.location.href = to) : window.open(to); + return; + } + + if (/^https?:\/\//.test(to)) { + window.location.replace(to); + } else { + location.href = to; + } + }, + fetcher: (api:any) => { + let { + url, + method, + data, + config + } = api; + config = config || {}; + config.withCredentials = true; + + if (method !== 'post' && method !== 'put' && method !== 'patch') { + if (data) { + config.params = data; + } + + return (axios as any)[method](url, config).then(responseAdpater(api)); + } else if (data && data instanceof FormData) { + // config.headers = config.headers || {}; + // config.headers['Content-Type'] = 'multipart/form-data'; + } else if (data + && typeof data !== 'string' + && !(data instanceof Blob) + && !(data instanceof ArrayBuffer) + ) { + data = JSON.stringify(data); + config.headers = config.headers || {}; + config.headers['Content-Type'] = 'application/json'; + } + + return (axios as any)[method](url, data, config).then(responseAdpater(api)); + }, + isCancel: (value: any) => (axios as any).isCancel(value), + copy: (contents: string, options: any = {}) => { + const ret = copy(contents, options); + ret && options.shutup !== true && toast.info('内容已拷贝到剪切板'); + return ret; + }, + richTextToken: '', + ...env + })} +
+ ), container); + return scoped; +} diff --git a/examples/mod.js b/examples/mod.js index 033c6fa81..b60daea29 100644 --- a/examples/mod.js +++ b/examples/mod.js @@ -12,6 +12,7 @@ var define; (function (global) { + var amis = window.amis || {}; // 避免重复加载而导致已定义模块丢失 if (require) { return; @@ -284,4 +285,8 @@ var define; require.timeout = 5000; + amis.require = require; + amis.define = define; + window.amis = amis; + })(this); diff --git a/examples/polyfills/Array.find.ts b/examples/polyfills/Array.find.ts new file mode 100644 index 000000000..1a53d8736 --- /dev/null +++ b/examples/polyfills/Array.find.ts @@ -0,0 +1,46 @@ +// https://tc39.github.io/ecma262/#sec-array.prototype.find +if (!Array.prototype.find) { + Object.defineProperty(Array.prototype, 'find', { + value: function(predicate) { + // 1. Let O be ? ToObject(this value). + if (this == null) { + throw new TypeError('"this" is null or not defined'); + } + + var o = Object(this); + + // 2. Let len be ? ToLength(? Get(O, "length")). + var len = o.length >>> 0; + + // 3. If IsCallable(predicate) is false, throw a TypeError exception. + if (typeof predicate !== 'function') { + throw new TypeError('predicate must be a function'); + } + + // 4. If thisArg was supplied, let T be thisArg; else let T be undefined. + var thisArg = arguments[1]; + + // 5. Let k be 0. + var k = 0; + + // 6. Repeat, while k < len + while (k < len) { + // a. Let Pk be ! ToString(k). + // b. Let kValue be ? Get(O, Pk). + // c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)). + // d. If testResult is true, return kValue. + var kValue = o[k]; + if (predicate.call(thisArg, kValue, k, o)) { + return kValue; + } + // e. Increase k by 1. + k++; + } + + // 7. Return undefined. + return undefined; + }, + configurable: true, + writable: true + }); + } \ No newline at end of file diff --git a/examples/polyfills/Array.from.ts b/examples/polyfills/Array.from.ts new file mode 100644 index 000000000..555d895d6 --- /dev/null +++ b/examples/polyfills/Array.from.ts @@ -0,0 +1,77 @@ +if (!Array.from) { + Array.from = (function () { + var toStr = Object.prototype.toString; + var isCallable = function (fn) { + return typeof fn === 'function' || toStr.call(fn) === '[object Function]'; + }; + var toInteger = function (value) { + var number = Number(value); + if (isNaN(number)) { return 0; } + if (number === 0 || !isFinite(number)) { return number; } + return (number > 0 ? 1 : -1) * Math.floor(Math.abs(number)); + }; + var maxSafeInteger = Math.pow(2, 53) - 1; + var toLength = function (value) { + var len = toInteger(value); + return Math.min(Math.max(len, 0), maxSafeInteger); + }; + + // The length property of the from method is 1. + return function from(arrayLike/*, mapFn, thisArg */) { + // 1. Let C be the this value. + var C = this; + + // 2. Let items be ToObject(arrayLike). + var items = Object(arrayLike); + + // 3. ReturnIfAbrupt(items). + if (arrayLike == null) { + throw new TypeError("Array.from requires an array-like object - not null or undefined"); + } + + // 4. If mapfn is undefined, then let mapping be false. + var mapFn = arguments.length > 1 ? arguments[1] : void undefined; + var T; + if (typeof mapFn !== 'undefined') { + // 5. else + // 5. a If IsCallable(mapfn) is false, throw a TypeError exception. + if (!isCallable(mapFn)) { + throw new TypeError('Array.from: when provided, the second argument must be a function'); + } + + // 5. b. If thisArg was supplied, let T be thisArg; else let T be undefined. + if (arguments.length > 2) { + T = arguments[2]; + } + } + + // 10. Let lenValue be Get(items, "length"). + // 11. Let len be ToLength(lenValue). + var len = toLength(items.length); + + // 13. If IsConstructor(C) is true, then + // 13. a. Let A be the result of calling the [[Construct]] internal method + // of C with an argument list containing the single item len. + // 14. a. Else, Let A be ArrayCreate(len). + var A = isCallable(C) ? Object(new C(len)) : new Array(len); + + // 16. Let k be 0. + var k = 0; + // 17. Repeat, while k < len… (also steps a - h) + var kValue; + while (k < len) { + kValue = items[k]; + if (mapFn) { + A[k] = typeof T === 'undefined' ? mapFn(kValue, k) : mapFn.call(T, kValue, k); + } else { + A[k] = kValue; + } + k += 1; + } + // 18. Let putStatus be Put(A, "length", len, true). + A.length = len; + // 20. Return A. + return A; + }; + }()); + } \ No newline at end of file diff --git a/examples/polyfills/Object.assign.ts b/examples/polyfills/Object.assign.ts new file mode 100644 index 000000000..b6a59c159 --- /dev/null +++ b/examples/polyfills/Object.assign.ts @@ -0,0 +1,29 @@ +if (typeof Object.assign != 'function') { + // Must be writable: true, enumerable: false, configurable: true + Object.defineProperty(Object, "assign", { + value: function assign(target, varArgs) { // .length of function is 2 + 'use strict'; + if (target == null) { // TypeError if undefined or null + throw new TypeError('Cannot convert undefined or null to object'); + } + + var to = Object(target); + + for (var index = 1; index < arguments.length; index++) { + var nextSource = arguments[index]; + + if (nextSource != null) { // Skip over if undefined or null + for (var nextKey in nextSource) { + // Avoid bugs when hasOwnProperty is shadowed + if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { + to[nextKey] = nextSource[nextKey]; + } + } + } + } + return to; + }, + writable: true, + configurable: true + }); +} \ No newline at end of file diff --git a/examples/polyfills/cloest.ts b/examples/polyfills/cloest.ts new file mode 100644 index 000000000..ed3a5b676 --- /dev/null +++ b/examples/polyfills/cloest.ts @@ -0,0 +1,25 @@ +/** + * @file 担心会有些浏览器不支持,所以网上抄了一段 + * @author mdn + */ +if (!Element.prototype.matches) { + Element.prototype.matches = Element.prototype.msMatchesSelector + || Element.prototype.webkitMatchesSelector; +} + +if (!Element.prototype.closest) { + Element.prototype.closest = function (s) { + var el = this; + if (!document.documentElement.contains(el)) { + return null; + } + + do { + if (el.matches(s)) { + return el; + } + el = el.parentElement; + } while (el !== null); + return null; + }; +} diff --git a/examples/polyfills/index.ts b/examples/polyfills/index.ts new file mode 100644 index 000000000..3b0325745 --- /dev/null +++ b/examples/polyfills/index.ts @@ -0,0 +1,7 @@ +import './Object.assign'; +import './Array.from'; +import './Array.find'; +import './cloest'; + +import 'promise/polyfill'; +import 'es6-symbol/implement'; \ No newline at end of file diff --git a/examples/static/favicon.png b/examples/static/favicon.png new file mode 100644 index 000000000..e4a76557c Binary files /dev/null and b/examples/static/favicon.png differ diff --git a/examples/static/loader.js b/examples/static/loader.js new file mode 100644 index 000000000..cba1a629d --- /dev/null +++ b/examples/static/loader.js @@ -0,0 +1,3 @@ +/* eslint-disable */ + +amis.require(__moduleId('../utils/loader')); \ No newline at end of file diff --git a/examples/static/logo.png b/examples/static/logo.png new file mode 100644 index 000000000..fc2bb8529 Binary files /dev/null and b/examples/static/logo.png differ diff --git a/examples/static/mod.js b/examples/static/mod.js new file mode 100644 index 000000000..c04fc75c2 --- /dev/null +++ b/examples/static/mod.js @@ -0,0 +1,260 @@ +/** + * @file mod-sandbox.js. + * @author liyong18@baidu.com + */ +/* eslint-disable */ + +(function (global) { + var require, define; + var amis = window.amis || {}; + // 避免重复加载而导致已定义模块丢失 + + var head = document.getElementsByTagName('head')[0]; + var loadingMap = {}; + var factoryMap = {}; + var modulesMap = {}; + var scriptsMap = {}; + var resMap = {}; + var pkgMap = {}; + + var createScripts = function (queues, onerror) { + + var docFrag = document.createDocumentFragment(); + + for (var i = 0, len = queues.length; i < len; i++) { + var id = queues[i].id; + var url = queues[i].url; + + if (url in scriptsMap) { + continue; + } + + scriptsMap[url] = true; + + var script = document.createElement('script'); + if (onerror) { + (function (script, id) { + var tid = setTimeout(function () { + onerror(id); + }, require.timeout); + + script.onerror = function () { + clearTimeout(tid); + onerror(id); + }; + + var onload = function () { + clearTimeout(tid); + }; + + if ('onload' in script) { + script.onload = onload; + } + else { + script.onreadystatechange = function () { + if (this.readyState === 'loaded' || this.readyState === 'complete') { + onload(); + } + }; + } + })(script, id); + } + script.type = 'text/javascript'; + script.src = url; + + docFrag.appendChild(script); + } + + head.appendChild(docFrag); + }; + + var loadScripts = function (ids, callback, onerror) { + var queues = []; + for (var i = 0, len = ids.length; i < len; i++) { + var id = ids[i]; + var queue = loadingMap[id] || (loadingMap[id] = []); + queue.push(callback); + + // + // resource map query + // + var res = resMap[id] || resMap[id + '.js'] || {}; + var pkg = res.pkg; + var url; + + if (pkg) { + url = pkgMap[pkg].url || pkgMap[pkg].uri; + } + else { + url = res.url || res.uri || id; + } + + queues.push({ + id: id, + url: url + }); + } + + createScripts(queues, onerror); + }; + + define = function (id, factory) { + id = id.replace(/\.js$/i, ''); + factoryMap[id] = factory; + + var queue = loadingMap[id]; + if (queue) { + for (var i = 0, n = queue.length; i < n; i++) { + queue[i](); + } + delete loadingMap[id]; + } + }; + + require = function (id) { + + // compatible with require([dep, dep2...]) syntax. + if (id && id.splice) { + return require.async.apply(this, arguments); + } + + id = require.alias(id); + + var mod = modulesMap[id]; + if (mod) { + return mod.exports; + } + + // + // init module + // + var factory = factoryMap[id]; + if (!factory) { + throw '[ModJS] Cannot find module `' + id + '`'; + } + + mod = modulesMap[id] = { + exports: {} + }; + + // + // factory: function OR value + // + var ret = (typeof factory === 'function') ? factory.apply(mod, [require, mod.exports, mod]) : factory; + + if (ret) { + mod.exports = ret; + } + + return mod.exports; + }; + + require.async = function (names, onload, onerror) { + if (typeof names === 'string') { + names = [names]; + } + + var needMap = {}; + var needNum = 0; + var needLoad = []; + + function findNeed(depArr) { + var child; + + for (var i = 0, n = depArr.length; i < n; i++) { + // + // skip loading or loaded + // + var dep = require.alias(depArr[i]); + + if (dep in needMap) { + continue; + } + + needMap[dep] = true; + + if (dep in factoryMap) { + // check whether loaded resource's deps is loaded or not + child = resMap[dep] || resMap[dep + '.js']; + if (child && 'deps' in child) { + findNeed(child.deps); + } + continue; + } + + needLoad.push(dep); + needNum++; + + child = resMap[dep] || resMap[dep + '.js']; + if (child && 'deps' in child) { + findNeed(child.deps); + } + } + } + + function updateNeed() { + if (0 === needNum--) { + var args = []; + for (var i = 0, n = names.length; i < n; i++) { + args[i] = require(names[i]); + } + + onload && onload.apply(global, args); + } + } + + findNeed(names); + loadScripts(needLoad, updateNeed, onerror); + updateNeed(); + }; + + require.ensure = function (names, callback) { + require.async(names, function () { + callback && callback.call(this, require); + }); + }; + + require.resourceMap = function (obj) { + var k; + var col; + + // merge `res` & `pkg` fields + col = obj.res; + for (k in col) { + if (col.hasOwnProperty(k)) { + resMap[k] = col[k]; + } + } + + col = obj.pkg; + for (k in col) { + if (col.hasOwnProperty(k)) { + pkgMap[k] = col[k]; + } + } + }; + + require.loadJs = function (url) { + if (url in scriptsMap) { + return; + } + + scriptsMap[url] = true; + + var script = document.createElement('script'); + script.type = 'text/javascript'; + script.src = url; + head.appendChild(script); + }; + + require.alias = function (id) { + return id.replace(/\.js$/i, ''); + }; + + require.timeout = 5000; + + amis.require = require; + amis.define = define; + window.amis = amis; + // window.require = window.require || require; +})(this); \ No newline at end of file diff --git a/examples/utils/amis.ts b/examples/utils/amis.ts new file mode 100644 index 000000000..a4985dc2b --- /dev/null +++ b/examples/utils/amis.ts @@ -0,0 +1,3 @@ +import * as amis from '../../src/index'; + +export default amis; \ No newline at end of file diff --git a/examples/utils/loader.ts b/examples/utils/loader.ts new file mode 100644 index 000000000..0b243ce60 --- /dev/null +++ b/examples/utils/loader.ts @@ -0,0 +1,60 @@ + +const __moduleId = (str: string) => ''; +import './openUtils'; + +const mapping: { + [propName: string]: any; +} = { + 'jquery': __moduleId('jquery'), + 'react': __moduleId('react'), + 'react-dom': __moduleId('react-dom'), + 'react-addons-update': __moduleId('react-addons-update'), + 'immutability-helper': __moduleId('react-addons-update'), + 'react-router': __moduleId('react-router'), + 'react-select': __moduleId('react-select'), + 'react-cropper': __moduleId('react-cropper'), + 'react-dropzone': __moduleId('react-dropzone'), + 'aui': __moduleId('amis'), + 'react-bootstrap': __moduleId('react-bootstrap'), + 'classnames': __moduleId('classnames'), + 'axios': __moduleId('axios'), + 'moment': __moduleId('moment'), + 'mobx': __moduleId('mobx'), + 'mobx-state-tree': __moduleId('mobx-state-tree'), + 'react-transition-group': __moduleId('react-transition-group'), + 'echarts': __moduleId('echarts'), + 'zrender': __moduleId('zrender'), + 'sortablejs': __moduleId('sortablejs'), + 'history': __moduleId('history'), + '@fex/aui': __moduleId('amis'), + '@fex/amis-renderer': __moduleId('amis'), + 'amis/utils': __moduleId('./openUtils'), + 'amis/util': __moduleId('./openUtils'), + 'amis/embed': __moduleId('../embed.tsx'), + 'prop-types': __moduleId('prop-types'), + 'async': __moduleId('async'), + 'qs': __moduleId('qs'), + 'lodash/find': __moduleId('lodash/find'), + 'lodash/findLast': __moduleId('lodash/findLast'), + 'lodash/chunk': __moduleId('lodash/chunk'), + 'lodash/flatMap': __moduleId('lodash/flatMap'), + 'lodash/isEqual': __moduleId('lodash/isEqual'), + 'lodash/transform': __moduleId('lodash/transform'), + 'lodash/debounce': __moduleId('lodash/debounce'), + 'lodash/difference': __moduleId('lodash/difference'), + 'lodash/partition': __moduleId('lodash/partition'), + 'lodash/forEach': __moduleId('lodash/forEach'), + 'lodash/omit': __moduleId('lodash/omit'), + 'lodash/pick': __moduleId('lodash/pick'), + 'lodash/isPlainObject': __moduleId('lodash/isPlainObject'), + 'lodash/isObject': __moduleId('lodash/isObject'), +}; + +function amisRequire(...args: Array) { + let id = args.shift(); + id = Array.isArray(id) ? id.map(id => mapping[id] || id) : mapping[id] || id; + args.unshift(id); + return require.apply(this, args); +}; + +(window as any).amisRequire = amisRequire; \ No newline at end of file diff --git a/examples/utils/openUtils.ts b/examples/utils/openUtils.ts new file mode 100644 index 000000000..9b8872d98 --- /dev/null +++ b/examples/utils/openUtils.ts @@ -0,0 +1,40 @@ +import { + wrapFetcher, + filter +} from '../../src/index'; +import axios from 'axios'; + + +export const fetch = wrapFetcher(({ + url, + method, + data, + config +}:any) => { + config = config || {}; + + if (method !== 'post' && method !== 'put' && method !== 'patch') { + if (data) { + config.params = data; + } + + return (axios as any)[method](url, config); + } else if (data && data instanceof FormData) { + // config.headers = config.headers || {}; + // config.headers['Content-Type'] = 'multipart/form-data'; + } else if (data + && typeof data !== 'string' + && !(data instanceof Blob) + && !(data instanceof ArrayBuffer) + ) { + data = JSON.stringify(data); + config.headers = config.headers || {}; + config.headers['Content-Type'] = 'application/json'; + } + + return (axios as any)[method](url, data, config); +}); + +export { + filter +}; \ No newline at end of file diff --git a/fis-conf.js b/fis-conf.js index 8306f4944..eff3e3d87 100644 --- a/fis-conf.js +++ b/fis-conf.js @@ -8,12 +8,12 @@ fis.get('project.ignore').push( 'gh-pages/**', '.*/**' ); - // 配置只编译哪些文件。 fis.set('project.files', [ 'scss/**.scss', '/examples/*.html', + '/examples/*.tpl', '/src/**.html', 'mock/**' ]); @@ -22,6 +22,10 @@ fis.match('/mock/**', { useCompile: false }); +fis.match('mod.js', { + useCompile: false +}); + fis.match('*.scss', { parser: fis.plugin('node-sass', { sourceMap: true @@ -58,7 +62,7 @@ fis.match('/docs/**.md', { isMod: true }); -fis.match('{*.jsx,*.tsx,/src/**.js,/src/**.ts}', { +fis.match('{*.ts,*.jsx,*.tsx,/src/**.js,/src/**.ts}', { parser: [fis.plugin('typescript', { importHelpers: true, esModuleInterop: true, @@ -107,11 +111,25 @@ fis.match('/node_modules/monaco-editor/min/**/loader.js', { } }); +fis.match('{/node_modules/font-awesome/fonts/(*), /node_modules/bootstrap/dist/fonts/(*)}', { + release: '/sdk/$1' +}); + +fis.match('**.{js,jsx,ts,tsx}', { + moduleId: function (m, path) { + return fis.util.md5('amis' + path); + } +}); + fis.match('::package', { - postpackager: fis.plugin('loader', { - useInlineMap: false, - resourceType: 'mod' - }) + postpackager: [ + fis.plugin('loader', { + useInlineMap: false, + resourceType: 'mod' + }), + + require('./build/embed-packager') + ] }); fis.hook('node_modules', { @@ -123,6 +141,26 @@ fis.hook('commonjs', { extList: ['.js', '.jsx', '.tsx', '.ts'] }); +fis.match('monaco-editor/**.js', { + isMod: false, + standard: null, + packTo: null +}); + +fis.on('compile:optimizer', function (file) { + if (file.isJsLike && file.isMod) { + var contents = file.getContent(); + + if (typeof contents === 'string' && contents.substring(0, 7) === 'define(') { + contents = 'amis.' + contents; + + contents = contents.replace('function(require, exports, module)', 'function(require, exports, module, define)'); + + file.setContent(contents); + } + } +}); + fis .media('dev') .match('/node_modules/**.js', { diff --git a/package.json b/package.json index 4a3440946..b78acb108 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,10 @@ "tslib": "^1.9.3", "uncontrollable": "4.1.0", "video-react": "0.9.4", - "redux": "^3.7.2" + "redux": "^3.7.2", + "css": "2.2.1", + "es6-symbol": "3.1.1", + "copy-to-clipboard": "3.0.8" }, "devDependencies": { "@types/async": "^2.0.45",