diff --git a/Makefile b/Makefile index f4df2b2..ea1ea9c 100644 --- a/Makefile +++ b/Makefile @@ -5,6 +5,7 @@ install: release: @$(min) ./runtime/cube.js -o ./runtime/cube.min.js + @$(min) ./runtime/cube-reconstruction.js -o ./runtime/cube-reconstruction.min.js @node ./bin/version.js publish: release tag diff --git a/bin/version.js b/bin/version.js index 3a8b3ba..2ffe416 100644 --- a/bin/version.js +++ b/bin/version.js @@ -1,16 +1,23 @@ 'use strict'; -var fs = require('xfs'); -var path = require('path'); -var pkg = require('../package.json'); -var fpath = path.join(__dirname, '../runtime/cube.min.js'); -var code = fs.readFileSync(fpath).toString(); +const fs = require('xfs'); +const path = require('path'); +const pkg = require('../package.json'); +const fpath1 = path.join(__dirname, '../runtime/cube.min.js'); +const fpath2 = path.join(__dirname, '../runtime/cube-reconstruction.min.js'); -code = code.replace(/\$\$version\$\$/, pkg.version); -var cmt = -`/*! - * Cube v${pkg.version} - */ -`; +function combine(fpath) { + let code = fs.readFileSync(fpath).toString(); -fs.writeFileSync(fpath, cmt + code); \ No newline at end of file + code = code.replace(/\$\$version\$\$/, pkg.version); + const cmt = + `/*! + * Cube v${pkg.version} + */ + `; + + fs.writeFileSync(fpath, cmt + code); +} + +combine(fpath1); +combine(fpath2); \ No newline at end of file diff --git a/index.js b/index.js index 9e090c5..880fe7d 100644 --- a/index.js +++ b/index.js @@ -37,12 +37,13 @@ Cube.middleware = function (cube, config) { * - base {Path} http prefix * - processors {Array} extenal processors * - cached {Path} the cached path + * @param {Object} servOpt config for service * @return {cube} */ -Cube.service = function (config) { +Cube.service = function (config, servOpt) { let cube = new Cube(config); let service = require('./service'); - service.init(cube); + service.init(cube, servOpt); return cube; }; /** diff --git a/lib/cube/ext_api_process.js b/lib/cube/ext_api_process.js index 976d2a7..60db0a1 100644 --- a/lib/cube/ext_api_process.js +++ b/lib/cube/ext_api_process.js @@ -6,9 +6,34 @@ const babylon = require('babylon'); const babelType = require('babel-types'); const babelTraverse = require('babel-traverse').default; const babelGenerator = require('babel-generator').default; -const esminify = require('esminify'); +const uglifyJS = require("uglify-js"); +const uuid = require('uuid'); const debug = require('debug')('cube:wraper'); const utils = require('../utils'); +const resolveExports = require('resolve.exports').resolve; + +function findNearestPackageJson(dir, root) { + let current = dir; + while (true) { + if (fs.existsSync(path.join(current, 'package.json'))) { + return current; + } + if (current === root || path.dirname(current) === current) break; + current = path.dirname(current); + } + return root; +} + +function getSubpath(modPath, pkgRoot) { + let rel = path.relative(pkgRoot, modPath).replace(/\\/g, '/'); // 兼容 Windows + if (!rel || rel === '' || rel === 'index.js') { + return '.'; + } + if (!rel.startsWith('.')) { + rel = './' + rel; + } + return rel; +} /** * transfer filename: the ext part @@ -33,12 +58,10 @@ function unique(arr) { /** * tryFiles 扫描文件 - * 根据给定的modPath, 识别出文件类型,默认是 script - * 如果要加载style和template, require的时候必须带后缀 - * - * 确定完类型之后,开始寻找存在的文件: - * 比如请求的文件是 index.js, 实际文件可以是 fname.$possibleExts - * $possibleExts 就是类型下所有注册的可能的后缀名称 + * 先用 tryFilesBaisc 扫描,假如没找到,再试下默认后缀的识别方法 + * + * eg. xxx.glsl => 找不到 + * xxx.glsl.js => 找到 * * @exceptions * @@ -46,7 +69,25 @@ function unique(arr) { * */ function tryFiles(cube, root, modPath) { - var origExt = path.extname(modPath); + try { + // 按原始后缀找一次 + return tryFilesBaisc(cube, root, modPath, path.extname(modPath)); + } catch (e) { + // 兜底用默认后缀找一次 + return tryFilesBaisc(cube, root, modPath, ''); + } +} + +/** + * tryFilesBaisc 扫描文件 + * 根据给定的modPath, 识别出文件类型,默认是 script + * 如果要加载style和template, require的时候必须带后缀 + * + * 确定完类型之后,开始寻找存在的文件: + * 比如请求的文件是 index.js, 实际文件可以是 fname.$possibleExts + * $possibleExts 就是类型下所有注册的可能的后缀名称 + */ +function tryFilesBaisc(cube, root, modPath, origExt) { var fName = path.basename(modPath, origExt); var dir = path.dirname(modPath); var arr; @@ -93,15 +134,18 @@ function tryFiles(cube, root, modPath) { } /** * testModPath and return the relative real path + * + * 要注意 browser module main 的关系:https://github.com/SunshowerC/blog/issues/8 + * * @param {Path} modPath abs root, may be file, may be module dir * @param {String} modName 在require中书写的模块名 + * @param {String} modRootPath 在require中书写的模块名的根节点,即 package.json 的所在位置 * @return {Path} relative path based on root */ -function testModPath(cube, modPath, modName) { +function testModPath(cube, modPath, modName, modRootPath) { let tmpModPath; let stat; let root = cube.config.root; - let moduleMap = cube.config.moduleMap; try { // test if dir try { @@ -109,89 +153,203 @@ function testModPath(cube, modPath, modName) { } catch(e) { // DO NOTHING } - try { - debug('testModPath: try single-file first.', modPath); - tmpModPath = tryFiles(cube, root, modPath); - } catch (e) { - if (stat && stat.isDirectory()) { - // get module default enter - try { - let pkg = require(path.join(modPath, './package.json')); - let alreadyFound = false; - /** - * first of all: moduleMap - */ - // tmpModPath = cube.config.moduleMap && cube.config.moduleMap[modName]; - // if (cube.config.moduleMap && tmpModPath) { - // tmpModPath = path.join(modPath, cube.config.moduleMap[modName]); - // debug('testModPath: try moduleMap[', modName, ',', cube.config.moduleMap[modName],']'); - // } - /** - * then search for browserify config in package.json - * "browserify": "d3.js" - */ - if (moduleMap && moduleMap[modName]) { - let tmp; - try { - tmp = fs.readlinkSync(modPath); - modPath = path.join(path.dirname(modPath), tmp); - } catch (e) { - // do nothing - } - tmpModPath = path.join(modPath, moduleMap[modName]); - } - else if (pkg.browserify && typeof pkg.browserify === 'string') { - tmpModPath = path.join(modPath, pkg.browserify); - debug('testModPath: try package.json.browserify', tmpModPath); - } - /** - * then search for browser config in package.json - * "browser": "browser.js" - */ - else if (pkg.browser && typeof pkg.browser === 'string') { - tmpModPath = path.join(modPath, pkg.browser); - debug('testModPath: try package.json.browser', tmpModPath); - } - /** - * then search for pkg.main - */ - else if (pkg.main && typeof pkg.main === 'string') { - tmpModPath = path.join(modPath, pkg.main); - debug('testModPath: try package.json.main ', tmpModPath); + // 文件夹和文件的处理趋于一致 + tmpModPath = testModPathBaisc(cube, modPath, modName, modRootPath, stat && stat.isDirectory()); + } catch (e) { + debug('testModPath: not found at last', modPath); + throw e; + } + return tmpModPath.substr(root.length); +} + +/** + * 文件处理: + * 找到包目录下 package.json,按 moduleMap => browserify => broswer => modPath => modPath/index.js + * + * 文件夹处理: + * 1. 找当前目录有没有 package.json,没有的话找包目录下 package.json + * 2. 按 moduleMap => browserify => broswer => main => modPath => modPath/index.js + */ +function testModPathBaisc(cube, modPath, modName, modRootPath, isDirectory) { + let root = cube.config.root; + let moduleMap = cube.config.moduleMap; + let tmpModPath; + // @ns/module module 都需要在路径上拼 node_modules + const rootPath = modRootPath; + try { + let pkg; + let isRoot = false; + // 获取 package.json + if (isDirectory) { + // 看看当前目录有没有 package.json, 优先级会更高 + isRoot = true; + try { + pkg = require(path.join(modPath, './package.json')); + } catch(e) {} // DO NOTHING + } + // 假如没有,不论文件夹还是文件都再次找一下包根目录有没有 package.json + if (!pkg) { + try { + isRoot = modPath === modRootPath; + pkg = require(path.join(modRootPath, './package.json')); + } catch (e) {} // DO NOTHING + } + + let alreadyFound = false; + if (moduleMap && moduleMap[modName]) { + let tmp; + try { + tmp = fs.readlinkSync(modPath); + modPath = path.join(path.dirname(modPath), tmp); + } catch (e) { + // do nothing + } + tmpModPath = path.join(modPath, moduleMap[modName]); + debug('testModPath: try moduleMap', tmpModPath); + try { + tmpModPath = tryFiles(cube, root, tmpModPath); + alreadyFound = true; + } catch (e) { + } + } + + // 新增:优先支持 package.json 的 exports 字段 + if (!alreadyFound && pkg && pkg.exports) { + const subpath = getSubpath(modPath, modRootPath); + // 使用 resolve.exports 解析 exports 字段 + try { + let exportTarget = resolveExports(pkg, subpath, { require: true }); + if (Array.isArray(exportTarget)) { + exportTarget = exportTarget[0]; + } + if (exportTarget) { + tmpModPath = path.join(modRootPath, exportTarget); + debug('testModPath: try package.json.exports (via resolve.exports)', tmpModPath); + tmpModPath = tryFiles(cube, root, tmpModPath); + alreadyFound = true; + } + } catch (e) { + } + } + + /** + * "browserify": "browserify.js" + */ + if (!alreadyFound && pkg && pkg.browserify && typeof pkg.browserify === 'string') { + tmpModPath = path.join(modPath, pkg.browserify); + debug('testModPath: try package.json.browserify', tmpModPath); + try { + tmpModPath = tryFiles(cube, root, tmpModPath); + alreadyFound = true; + } catch (e) { + } + } + + /** + * then search for browser config in package.json + * "browser": "browser.js" + */ + if (!alreadyFound && pkg && pkg.browser && typeof pkg.browser === 'string') { + tmpModPath = path.join(modPath, pkg.browser); + debug('testModPath: try package.json.browser', tmpModPath); + try { + tmpModPath = tryFiles(cube, root, tmpModPath); + alreadyFound = true; + } catch (e) { + } + } + + /** + * browser 为 obj 的 2 种情况, 先判断是否存在,然后获取 broswer path + * "browser": { + * "./aaa/aaa.js": "./aaa/dist/aaa.js", + * "fs": false + * } + */ + if (!alreadyFound && pkg && pkg.browser && typeof pkg.browser === 'object' && !!exist(pkg.browser, modPath, rootPath)) { + const browser = pkg.browser; + const browserArr = Object.keys(browser); + for (let i =0; i < browserArr.length; i++) { + if (modPath === path.resolve(rootPath, browserArr[i])) { + if (browser[browserArr[i]]) { + // browser[key] 找到了,既拼接返回 + tmpModPath = path.resolve(rootPath, browser[browserArr[i]]) + } else { + // browser[key] 被置为了 false, 需要忽略,这边造一个伪文件 + const id = `./${uuid.v4()}.js`; + browser[browserArr[i]] = id; + // rootPath 文件夹可能不存在, 因为可能人为的拼了 node_modules try { - tmpModPath = tryFiles(cube, root, tmpModPath); - alreadyFound = true; - } catch (e) { - /** if package.main like : ./lib, it means ./lib/index.js **/ - tmpModPath = path.join(tmpModPath, './index.js'); - debug('testModPath: try package.json.main / index.js ', tmpModPath); + fs.statSync(rootPath); + } catch(e) { + fs.mkdirSync(rootPath); } - } else { - tmpModPath = path.join(modPath, './index.js'); - debug('testModPath: try module_dir/index.js', tmpModPath); - } - if (!alreadyFound) { - tmpModPath = tryFiles(cube, root, tmpModPath); + fs.writeFileSync(path.join(rootPath, id), 'module.exports = {};'); + tmpModPath = path.join(rootPath, id); } - } catch (e) { - debug('testModPath: not found', e); - // can not find module main enter, use index.js as default - tmpModPath = path.join(modPath, 'index'); - debug('testModPath: try default module/index', tmpModPath); try { + debug('testModPath: try package.json.browser object', tmpModPath); tmpModPath = tryFiles(cube, root, tmpModPath); + alreadyFound = true; + break; } catch (e) { - debug('testModPath: not found'); - throw e; } } } } - } catch (e) { - debug('testModPath: not found at last', modPath); - throw e; + /** + * then search for pkg.main + */ + if (!alreadyFound && pkg && isRoot && pkg.main && typeof pkg.main === 'string') { + tmpModPath = path.join(modPath, pkg.main); + debug('testModPath: try package.json.main ', tmpModPath); + try { + tmpModPath = tryFiles(cube, root, tmpModPath); + alreadyFound = true; + } catch (e) { + /** if package.main like : ./lib, it means ./lib/index.js **/ + tmpModPath = path.join(tmpModPath, './index.js'); + debug('testModPath: try package.json.main / index.js ', tmpModPath); + } + } else { + try { + debug('testModPath: try modPath', modPath); + tmpModPath = tryFiles(cube, root, modPath); + alreadyFound = true; + } catch(e) { + } + } + + if (!alreadyFound) { + tmpModPath = tryFiles(cube, root, tmpModPath); + } + } catch(e) { + debug('testModPath: not found', e); + // can not find module main enter, use index.js as default + tmpModPath = path.join(modPath, 'index'); + debug('testModPath: try default module/index', tmpModPath); + try { + tmpModPath = tryFiles(cube, root, tmpModPath); + } catch (e) { + debug('testModPath: not found'); + throw e; + } } - return tmpModPath.substr(root.length); + return tmpModPath; +} + +// 判断 searchObj 中是否有 isTure +function exist(searchObj, modPath, rootPath) { + const searchArr = Object.keys(searchObj); + let isExist = false; + + for (let i =0; i < searchArr.length; i++) { + if (modPath === path.resolve(rootPath, searchArr[i])) { + isExist = true; + break; + } + } + return isExist; } function ifRootPath(path) { @@ -368,7 +526,8 @@ function fixAst(ast) { } function genCode(cube, qpath, ast, compress) { if (compress) { - let minifyOpt = cube.config.minify; + // 原有 minify 配置项直传 uglifyJS 会报错 + let minifyOpt = cube.config.uglifyMinify || {}; let opt = {}; if (typeof ast === 'string') { opt.code = ast; @@ -379,8 +538,17 @@ function genCode(cube, qpath, ast, compress) { */ opt.ast = fixAst(ast); } - opt.config = minifyOpt; - return esminify.minify(opt); + let out = babelGenerator(opt.ast, { + quotes: 'single', + comments: false, + compact: true, + minified: true, + concise: true, + filename: qpath, + }); + + out = uglifyJS.minify(out.code, minifyOpt); + return out.code; } else { if (typeof ast === 'string') { return ast; @@ -409,6 +577,7 @@ module.exports = { let dir = path.dirname(curModule); let count = 0; let nodeModulePath; + let moduleRootPath; let p; debug(`${curModule} resolve path: "${module}" in root: "${root}"`); /** @@ -428,9 +597,11 @@ module.exports = { }); } else if (/^@?\w+/.test(module)) { debug('module type: node_modules'); + var modName = module[0] === '@' ? module.split('/').slice(0, 2).join('/') : module.split('/')[0]; while (dir) { count++; nodeModulePath = path.join(root, dir, '/node_modules/', module); + moduleRootPath = findNearestPackageJson(nodeModulePath, root); let ignore = this.checkIgnore(nodeModulePath.substr(root.length)); if (ignore.ignore) { this.log.warn(`"${module}" which required by "${curModule}" is ignored by .cubeignore config`); @@ -445,7 +616,7 @@ module.exports = { } } try { - p = testModPath(this, nodeModulePath, module); + p = testModPath(this, nodeModulePath, module, moduleRootPath); debug('resolvePath: ok > ', p); break; } catch (e) { @@ -469,12 +640,12 @@ module.exports = { if (/^\//.test(module)) { // if abs path: like "/jquery" debug('module type: abs_module', module); p = path.join(root, module); - } else { + } else { // if relative path : ./xxx debug('module type: relative_module', module); p = path.join(root, dir, module); } try { - p = testModPath(this, p, module); + p = testModPath(this, p, module, findNearestPackageJson(p, root)); } catch (e) { debug('resolvePath error, module not found', module); this.log.warn(`required path:'${module}' not found in file: ${curModule}`); @@ -526,6 +697,8 @@ module.exports = { /** require 列表 */ data.requires = []; data.requiresOrigin = []; + /** queryPath 要重新计算 */ + data.queryPath = utils.moduleName(data.queryPath, data.type, config.release, config.remote); return callback(null, data); } diff --git a/lib/cube/index.js b/lib/cube/index.js index b4b3324..47c3aef 100644 --- a/lib/cube/index.js +++ b/lib/cube/index.js @@ -11,11 +11,11 @@ const EXT_API_PROCESS = require('./ext_api_process'); const EXT_API_TRANSFER = require('./ext_api_transfer'); const buildinProcessors = { - BUILDIN_JS: new require(path.join(__dirname, '../processor/js')), - BUILDIN_CSS: new require(path.join(__dirname, '../processor/css')), - BUILDIN_JSON: new require(path.join(__dirname, '../processor/json')), - BUILDIN_HTML: new require(path.join(__dirname, '../processor/html')), - BUILDIN_RAW: new require(path.join(__dirname, '../processor/raw')) + BUILDIN_JS: require(path.join(__dirname, '../processor/js')), + BUILDIN_CSS: require(path.join(__dirname, '../processor/css')), + BUILDIN_JSON: require(path.join(__dirname, '../processor/json')), + BUILDIN_HTML: require(path.join(__dirname, '../processor/html')), + BUILDIN_RAW: require(path.join(__dirname, '../processor/raw')) }; /** diff --git a/package.json b/package.json index 9416b6e..9015a7d 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "fish (https://github.com/fishbar)", "name": "node-cube", "description": "a new way to write js in browser", - "version": "3.1.17-beta.2", + "version": "5.0.0-beta.28", "homepage": "https://github.com/fishbar/cube", "repository": { "type": "git", @@ -29,28 +29,32 @@ "clean-css": "^4.1.4", "commander": "4.0.1", "connect": "^3.6.3", + "cors": "^2.8.5", "debug": "4.1.0", - "esminify": "3.0.5", "lodash": "4.17.15", + "resolve.exports": "^2.0.3", "serve-static": "1.12.4", + "tnpm-sync": "^1.0.1", + "uglify-js": "^3.14.1", + "uuid": "^8.3.0", "xfs": "~0.2.2" }, "devDependencies": { + "babel-preset-env": "*", + "cube-babel": "*", "cube-coffee": "*", "cube-ejs": "*", - "cube-babel": "*", "cube-jade": "*", + "cube-lazy-parse": "*", "cube-less": "*", "cube-react": "*", "cube-stylus": "*", - "cube-lazy-parse": "*", + "esminify": "3.0.5", "expect.js": "0.3.1", "jscoverage": "0.6.0", "jsdom": "6.2.0", "minami": "^1.1.1", "mocha": "3.2.0", - "supertest": "3.0.0", - "babel-preset-env": "*" - }, - "optionalDependencies": {} + "supertest": "3.0.0" + } } diff --git a/runtime/cube-reconstruction.js b/runtime/cube-reconstruction.js new file mode 100644 index 0000000..8ff61aa --- /dev/null +++ b/runtime/cube-reconstruction.js @@ -0,0 +1,1583 @@ +// WATCH! 该文件由 cube-reconstruct.ts 导出 请勿直接改动 +(function (globalThis) { + // 支持 cube 的一些工具方法 + function noop() {} + function baseCodeProxy(c) { + return c; + } + function combineExecute(c) { + return 'Cube.cStart();' + c + ';Cube.cStop();'; + } + function fetchCubeCode(url, inputCodeProxy, responseAdapter) { + const codeProxy = inputCodeProxy || baseCodeProxy; + const options = typeof url === 'string' ? { url } : url; + return (typeof options.fetch === 'function' ? options.fetch : fetch)( + options.url, + { + headers: { + 'Content-Type': 'text/plain', + }, + } + ) + .then((response) => { + if (responseAdapter) responseAdapter(response); + return response; + }) + .then((response) => response.text()) + .then((code) => { + var _a; + try { + return new Function(codeProxy(code))(); + } catch (error) { + (_a = options.onCodeError) === null || _a === void 0 + ? void 0 + : _a.call(options, error, { + url: options.url, + }); + /** 保持抛错 */ + console.error(error); + } + }); + } + const head = + typeof document === 'undefined' + ? { + appendChild: () => {}, + } + : document.querySelector('head'); + /** 原有 cube 请求方法 */ + function scriptCubeCode(url) { + var script = + document === null || document === void 0 + ? void 0 + : document.createElement('script'); + script.type = 'text/javascript'; + script.async = true; + script.onerror = () => { + console.error(`load module failed.`); + }; + script.src = url; + head === null || head === void 0 ? void 0 : head.appendChild(script); + } + function fixMododulePath(paths, remoteSeparator) { + var len = paths.length; + var mod; + for (var i = 0; i < len; i++) { + mod = paths[i]; + if (mod.indexOf(remoteSeparator) === -1) { + /** fix #12 **/ + if (mod.indexOf('./') === 0) { + // be compatible with ./test.js + paths[i] = mod.substr(1); + } else if (mod[0] !== '/') { + // be campatible with test.js + paths[i] = '/' + mod; + } + } + } + return paths; + } + const parseCssRe = /([^};]+)(\{[^}]+\})/g; + /** 原有 css 请求方法 */ + function scriptCubeCss(originCss, namespace, file) { + let css = originCss; + if (namespace) { + css = originCss.replace(parseCssRe, function (_m0, m1, m2) { + var selectors = m1.split(',').map(function (selector) { + return namespace + ' ' + selector.trim(); + }); + return selectors.join(',') + m2; + }); + } + var style = document.createElement('style'); + style.setAttribute('type', 'text/css'); + if (file) { + style.setAttribute('mod', file); + } + if (namespace) { + style.setAttribute('ns', namespace); + } + head === null || head === void 0 ? void 0 : head.appendChild(style); + style.innerHTML = css; + return css; + } + function parseQueryString(param) { + let kvs = param.split('&'); + let obj = {}; + kvs.forEach((kv) => { + let tmp = kv.split('='); + obj[tmp[0]] = tmp[1]; + }); + return obj; + } + /** + * If name is like 'remoteXXX:/com/user/index.js', replace remoteXXX with path defined in init() + */ + function rebase(name, config) { + const { base, remoteSeparator, remoteBase } = config; + let defaultPath = base + name; + var offset = name.indexOf ? name.indexOf(remoteSeparator) : 0; + if (offset <= 0) return defaultPath; + var rbase = name.substr(0, offset); + if (!remoteBase[rbase]) return defaultPath; + return remoteBase[rbase] + name.substr(offset + 1); + } + // 定制业务逻辑 ?env=publish === 不加 env + // 此逻辑加在 cube 似乎处不合理 + function removePublishName(name) { + const [main, params] = String(name).split('?'); + if (params) { + let kvs = params.split('&'); + if (kvs.includes('env=publish')) { + kvs = kvs.filter((v) => v !== 'env=publish'); + const newParams = kvs.join('&'); + if (newParams) { + return main + '?' + newParams; + } else { + return main; + } + } + } + return name; + } + // 从模块路径中提取模块名称和模块版本 + function extractModuleInfo(path) { + const regex = /^((@[^/]+\/[^/]+|[^/]+))(?:\/(.*))?$/; + const match = path.match(regex); + if (!match) return null; + const moduleName = match[1]; + const modulePath = match[3] || ''; // 模块路径(默认为空字符串) + return { moduleName, modulePath }; + } + // require => datav:/npm/react/16.4.6?env=xxx + function extractModuleInfoFromRequire(require) { + const regex = + /^datav:\/npm\/((?:@[^/]+\/[^/]+)|[^/]+)(?:\/([^/?]+))?(?:\/([^?]*))?/; + const match = require.match(regex); + if (!match) return null; + const moduleName = match[1]; + const modulePath = match[3] || ''; // 模块路径(默认为空字符串) + return { moduleName, modulePath }; + } + + function _defineProperty(e, r, t) { + return ( + (r = _toPropertyKey(r)) in e + ? Object.defineProperty(e, r, { + value: t, + enumerable: !0, + configurable: !0, + writable: !0, + }) + : (e[r] = t), + e + ); + } + function ownKeys(e, r) { + var t = Object.keys(e); + if (Object.getOwnPropertySymbols) { + var o = Object.getOwnPropertySymbols(e); + r && + (o = o.filter(function (r) { + return Object.getOwnPropertyDescriptor(e, r).enumerable; + })), + t.push.apply(t, o); + } + return t; + } + function _objectSpread2(e) { + for (var r = 1; r < arguments.length; r++) { + var t = null != arguments[r] ? arguments[r] : {}; + r % 2 + ? ownKeys(Object(t), !0).forEach(function (r) { + _defineProperty(e, r, t[r]); + }) + : Object.getOwnPropertyDescriptors + ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) + : ownKeys(Object(t)).forEach(function (r) { + Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); + }); + } + return e; + } + function _toPrimitive(t, r) { + if ('object' != typeof t || !t) return t; + var e = t[Symbol.toPrimitive]; + if (void 0 !== e) { + var i = e.call(t, r || 'default'); + if ('object' != typeof i) return i; + throw new TypeError('@@toPrimitive must return a primitive value.'); + } + return ('string' === r ? String : Number)(t); + } + function _toPropertyKey(t) { + var i = _toPrimitive(t, 'string'); + return 'symbol' == typeof i ? i : i + ''; + } + + function mockClassicalCube(global) { + var _global$process, _global$process$env, _global$process2; + /* short global val */ + var doc = typeof document === 'undefined' ? {} : document; + var log = console; + + /* settings */ + var base = ''; + var remoteBase = {}; + var remoteSeparator = ':'; + var charset = 'utf-8'; + var version; + var strict = true; + var debug = true; + var combine = true; + var mockedProcess = _objectSpread2( + _objectSpread2( + {}, + (_global$process = global.process) !== null && + _global$process !== void 0 + ? _global$process + : {} + ), + {}, + { + env: _objectSpread2( + _objectSpread2( + {}, + (_global$process$env = + (_global$process2 = global.process) === null || + _global$process2 === void 0 + ? void 0 + : _global$process2.env) !== null && + _global$process$env !== void 0 + ? _global$process$env + : {} + ), + {}, + { + NODE_ENV: 'production', + } + ), + } + ); + var mockedGlobal = undefined; + var esModule = false; + var entrances = new Map(); // Cube.use's cb + // 兼容请求 key 带入参,返回 key 不带入参的情况。eg. 请求 /xxx?env=xx 返回 Cube('/xxx',), requireMap 缓存了 { '/xxx': '/xxx?env=xx' } + // 此兼容是在业务方已知的情况,后期会改造返回的代码头。 + var requireMap = {}; + // 注册表。 eg. { [moduleName]: { [modulePath]: { require, matchType, match, module } } } + var registerMap = {}; + var installedModules = { + /*exports, fn, loaded, fired*/ + }; // The module cache + var loading = {}; + var combineMap = {}; + // type BlackList = Array + var combineBlackList = []; + + /* store requires before init */ + var inited = false; + /** 未初始化时添加的等待请求的 module */ + var loadQueue = []; + var combineFailTime = 10000; + + // watch! 旧版使用 fetch 容易产生问题 + let requestMethod = 'script'; // 'fetch' | 'script' + // let isIntercepted = false; + let fetchMethod = undefined; + let onCodeError = undefined; + + /** + * The require function + * @param module + * @param namespace + * @returns {*} + * @private + */ + function __cube_require__(module, namespace) { + if (typeof namespace === 'undefined') { + return fireModule(module); + } else { + var css = fireModule(module); + Cube.css(css, namespace, module); + return module; + } + } + + /** + * This function creates the load function + */ + function __cube_load_creator__(referer) { + /** + * The load function + * @param module + * @param namespace + * @param cb + * @private + */ + return function __cube_load__(module, namespace, cb) { + if (typeof cb === 'undefined' && typeof namespace === 'function') { + cb = namespace; + namespace = null; + Cube.use(module, referer, cb); + } else { + Cube.use(module, referer, function (css) { + css = Cube.css(css, namespace, module); + cb && cb(css); + }); + } + }; + } + Cube.setRemoteBase = function (_remoteBase) { + Object.assign(remoteBase, _remoteBase); + }; + function checkAllDownloaded() { + if (loadQueue.length) { + return false; + } + for (var i in loading) { + if (loading.hasOwnProperty(i)) { + return false; + } + } + startAppAndCallback(); + } + + /** + * 下载模块 + * @param requires + * @param referer + * @param root 是否为顶层请求组件 + */ + function load(requires, referer, root) { + if (typeof requires === 'string') { + requires = [requires]; + } + if (!inited) { + loadQueue.push([requires, referer]); + return; + } + requires.forEach(function (require) { + if (installedModules[require] || getGlobalRegister(require)) { + if ( + combineMap[require] && + combineMap[require].failed && + installedModules[require] && + installedModules[require].loaded === false + ); + else { + return; + } + } + installedModules[require] = { + exports: {}, + loaded: false, + fired: false, + }; + + // 只有拼 src 时要带上 m & ref 时才需要分离 require 里的入参 query, 平时 /xxx?query=xx 才作为 installedModules 的 key + const [mod, custom] = String(require).split('?'); + var rebaseName = rebase(mod, { + base, + remoteSeparator, + remoteBase, + }); + var srcPath = rebaseName || base + mod; + var query = []; + if (version) { + query.push(version); + } + // 目前仅根节点(组件级别)发起 combine + if (checkCombineState(srcPath) && root) { + query.push('combine=true'); + installedModules[require].combine = true; + if (!combineMap[require]) { + combineMap[require] = { + start: Date.now(), + timeout: setTimeout(() => { + if (loading[require]) { + combineMap[require].failed = true; + load(require, referer); + // 标记超时了 + } + }, combineFailTime), + failed: false, + }; + } + } + if (custom) { + const customArgs = parseQueryString(custom); + Array.prototype.push.apply( + query, + Object.keys(customArgs).map((c) => { + return ''.concat(c, '=').concat(customArgs[c]); + }) + ); + } + if (query.length) { + srcPath = srcPath + '?' + query.join('&'); + } + if (requestMethod === 'fetch') { + // combine 接口失败后的 traceId 记录排查 + if (combine && combineMap[require] && !combineMap[require].traceId) { + fetchCubeCode( + { + url: srcPath, + fetch: fetchMethod, + onCodeError: onCodeError, + }, + undefined, + (res) => { + if (res.headers.has('request-id')) { + combineMap[require].traceId = res.headers.get('request-id'); + } + } + ); + } else { + fetchCubeCode(srcPath); + } + } else { + scriptCubeCode(srcPath); + } + requireMap[mod] = require; + loading[require] = true; + }); + checkAllDownloaded(); + } + function checkCombineState(path) { + if (!combine) return false; + if (combineBlackList.length) { + return !combineBlackList.some((black) => { + return !!path.match(black); + }); + } + // combineBlackList 没有或错误的情况下兜底 combine + return true; + } + + // require => datav:/npm/react/16.4.6?env=xxx + function getGlobalRegister(require) { + if (!require.startsWith('datav:/npm/')) { + return false; + } + let { moduleName, modulePath } = extractModuleInfoFromRequire(require); + if (!registerMap[moduleName]) return false; + modulePath = modulePath || 'default'; + if (registerMap[moduleName][modulePath]) + return registerMap[moduleName][modulePath].module; + Object.entries(registerMap[moduleName]).forEach((_ref) => { + let [path, register] = _ref; + if (register.match.test(require)) { + return register.module; + } + }); + return false; + } + + /** + * 运行模块 + * @param module + * @returns {*} + */ + function fireModule(module) { + var m = getGlobalRegister(module) || installedModules[module]; + if (!m) { + const err = new Error( + 'Cube Error: Cannot find module ' + "'" + module + "'" + ); + if (strict) { + throw err; + } else { + log.error(err); + return {}; + } + } + if (!m.fired) { + m.fired = true; + if (strict) { + m.exports = m.fn.apply(global, [ + m, + m.exports, + __cube_require__, + __cube_load_creator__(module), + mockedProcess, + mockedGlobal, + ]); + } else { + try { + m.exports = m.fn.apply(global, [ + m, + m.exports, + __cube_require__, + __cube_load_creator__(module), + mockedProcess, + mockedGlobal, + ]); + } catch (e) { + log.error(e); + m.exports = {}; + } + } + } + return isEsModule(m.exports) ? m.exports.default : m.exports; + } + + /** + * 从Cube.use的文件开始自上而下运行,并调用回调函数 + */ + function startAppAndCallback() { + for (let [key, value] of entrances) { + key.length && + key.forEach(function (entrance) { + // 出现多次 startAppAndCallback, 在某次 startAppAndCallback 未结束时,entrances 增加了,但其实 loading 并未结束 + // 严格检查 + if (loading[entrance]) return; + var count = 0; + const exportModule = fireModule(entrance); + value.length && + value.forEach(function (fn) { + var called = fn(exportModule); + if (called) { + count++; + } + }); + if (value.length === count) { + // 回调函数都执行完后删除 + entrances.delete(key); + } + }); + } + } + + /** + * 非构造函数,只供模块的wrapper调用 + * installedModules[name] name 是带入参的,不同入参的,不同key + * @param name + * @param requires + * @param sourceCode + */ + function Cube(name, requires, callback) { + // 暂时兼容返回的 name 不带入参的情况 + const oldName = String(name); + name = requireMap[name] || name; + var mod = installedModules[name]; + // 定制业务逻辑 ?env=publish === 不加 env + mod = removePublishName(mod); + if (!mod) { + mod = installedModules[name] = { + exports: {}, + fired: false, + }; + } + // 记录或清理合并接口信息 + if (combineMap[name] && !mod.loaded) { + if (!combineMap[name].failed) { + clearTimeout(combineMap[name].timeout); + delete combineMap[name]; + } else { + combineMap[name].end = Date.now(); + } + } + mod.loaded = true; + mod.fn = callback; + requireMap[oldName] && delete requireMap[oldName]; + if (loading[name]) { + delete loading[name]; + load(requires, name); + } else if (requires.length) { + load(requires, name); + } + } + + /** version, will replace in `make release` **/ + Cube.toString = function () { + return 'Cube:v$$version$$'; + }; + + /** + * init global setting for Cube + * @static + * @param {Object} config {base, remoteBase, charset, version} + */ + Cube.init = function (config) { + if (config.base && config.base !== '/') { + base = config.base.replace(/\/$/, ''); + } + if (config.remoteBase) { + for (var key in config.remoteBase) { + if (config.remoteBase.hasOwnProperty(key)) { + remoteBase[key] = config.remoteBase[key].replace(/\/$/, ''); + } + } + } + if (config.charset) { + charset = config.charset; + } + if (config.version) { + version = config.version; + } + if (config.strict !== undefined) { + strict = config.strict; + } + if (config.env) { + mockedProcess.env.NODE_ENV = config.env; + } + if (config.global) { + mockedGlobal = config.global; + } + if (config.combine !== undefined) { + combine = config.combine; + } + if (config.combineBlackList) { + combineBlackList = config.combineBlackList; + } + if (config.requestMethod) { + requestMethod = config.requestMethod; + } + // support ES6 module, default is true + if (config.esModule !== undefined) { + esModule = config.esModule; + } + if (config.fetchMethod) { + fetchMethod = config.fetchMethod; + } + if (config.onCodeError) { + onCodeError = config.onCodeError; + } + if (config.combineFailTime) { + combineFailTime = config.combineFailTime; + } + inited = true; + while (loadQueue.length) { + var deps = loadQueue.shift(); + load(deps[0], deps[1]); + } + return this; + }; + /** + * loading module async, this function only support abs path + * @public + * @param {Path} moduleNames module abs path + * @param {Function} cb callback function, usually with module.exports as it's first param + * @param {Boolean} noFix used only in single mode + */ + Cube.use = function (mods, referer, cb, noFix) { + if (!mods) { + throw new Error('Cube.use(moduleName) moduleName is undefined!'); + } + if (typeof referer === 'function') { + noFix = cb; + cb = referer; + referer = undefined; + } + if (!referer) { + referer = 'Cube.use'; + } + cb = cb || noop; + if (typeof mods === 'string') { + mods = [removePublishName(mods)]; + } else { + mods = mods.map(removePublishName); + } + if (!noFix) { + mods = fixMododulePath(mods, remoteSeparator); + } + + // WARN: mods 是数组,会被自然的用 , 拼接,但 query 入参也可能带 , 所以这边 entrances 用 Map + if (!entrances.has(mods)) { + entrances.set(mods, []); + } + entrances.get(mods).push( + (function () { + var apps = []; + var length = mods.length; + var firing = false; + return function (exports) { + if (firing) { + return; + } + apps.push(exports); + if (apps.length === length) { + firing = true; + cb.apply(global, apps); + return true; + } + }; + })() + ); + load(mods, referer, true); + return this; + }; + /** + * register module in to cache + * @param {string} module [description] + * @param {} exports [description] + * @param {object} options 配置项 + * @param {string} options.matchType 匹配模式,version 默认为按版本全匹配; module 按库级别,只要库一致就替换 + */ + Cube.register = function (module, exports) { + let { matchType = 'version' } = + arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; + if (installedModules[module]) { + return log.warn( + 'Cube Warning: Module ' + "'" + module + "'" + ' already registered' + ); + } + installedModules[module] = { + exports: exports, + fn: noop, + loaded: true, + fired: true, + }; + if (matchType === 'module') { + registerMap[module] = registerMap[module] || {}; + registerMap[module].default = { + require: module, + matchType, + match: new RegExp('^datav:/npm/'.concat(module, '/([^/]+)?$')), + module: installedModules[module], + moduleName: module, + }; + } + if (matchType === 'function') { + const { moduleName, modulePath } = extractModuleInfo(module); + if (!modulePath) { + return log.warn( + 'Cube Warning: Module ' + + "'" + + module + + "'" + + ' matchType is function, but no path' + ); + } + registerMap[moduleName] = registerMap[moduleName] || {}; + registerMap[moduleName][modulePath] = { + require: module, + matchType, + match: new RegExp( + '^datav:/npm/' + .concat( + moduleName, + '/(?\\d+\\.\\d+\\.\\d+(-[a-zA-Z0-9.-]+)?(\\+[a-zA-Z0-9.-]+)?)/' + ) + .concat(modulePath, '$') + ), + module: installedModules[module], + moduleName, + }; + } + return this; + }; + /** + * @interface inject css into page + * css inject is comp + * ie8 and lower only support 32 stylesheets, so this function + * @param {String} name module name + * @param {CssCode} css css code + */ + var cssMod = {}; + Cube.css = function (css, namespace, file) { + if (!css) { + return; + } + var modId = file + '@' + namespace; + if (cssMod[modId]) { + return; + } + cssMod[modId] = true; + return scriptCubeCss(css, namespace, file); + }; + Cube.debug = function () { + log.error('Cube Error: Cube.debug nolonger supported'); + }; + Cube.cache = function () { + var unloaded = {}, + unfired = {}, + i, + m; + for (i in installedModules) { + if (installedModules.hasOwnProperty(i)) { + m = installedModules[i]; + if (!m.loaded) { + unloaded[i] = m; + } + if (!m.fired) { + unfired[i] = m; + } + } + } + log.info('modules:', installedModules); + log.info('unloaded:', unloaded); + log.info('unfired:', unfired); + }; + if (global['Cube']) { + log.error( + 'Cube Error: window.' + + 'Cube' + + ' already in using, replace the last "null" param in cube.js' + ); + } else { + global['Cube'] = Cube; + } + + /** + * intergration with + */ + var cse = doc.currentScript; + if (cse) { + var cfg = cse.dataset; + if (cfg.base) { + Cube.init(cfg); + Cube.use(cfg.main || 'index.js', function (app) { + app.run && app.run(); + }); + } + } + // 支持 Cube 获取配置信息与新版一致 + Object.defineProperty(Cube, 'config', { + get() { + return { + base, + remoteBase, + remoteSeparator, + version, + strict, + debug, + esModule, + mockedGlobal, + mockedProcess, + charset, + combine, + combineMap, + }; + }, + }); + function isEsModule(module) { + return ( + esModule && module && typeof module === 'object' && module.__esModule + ); + } + } + + /** + * 默认配置项变量 + */ + const DEFAULT_CUBE_CONFIG = { + base: '', + remoteBase: {}, + remoteSeparator: ':', + mockedProcess: { + env: { NODE_ENV: 'production' }, + }, + mockedGlobal: undefined, + /** 这个字段应该已经废弃了 */ + charset: 'utf-8', + /** 仅严格模式 */ + strict: true, + /** 声明组件源码是否使用 esModule 模式*/ + esModule: false, + version: undefined, + /** 是否开启 debug 模式*/ + debug: true, + /** 是否开启请求合并 */ + combine: false, + /** 声明使用 fetch 请求还是创建 script 请求 */ + requestMethod: 'fetch', + // 旧版 fetchUndeclaredModule = false && aggregateFetch = true + // 新版 fetchUndeclaredModule = true && aggregateFetch = false + /** 是否重新请求未声明的文件 */ + fetchUndeclaredModule: false, + /** 是否聚合请求 */ + aggregateFetch: true, + /** 自定义 fetch 方法 */ + fetchMethod: undefined, + /** 下载脚本报错回调 */ + onCodeError: undefined, + }; + /** + * cube 重构 + * https://yuque.antfin.com/lcv0by/ph89oq/chzehxz50ldg5krg + */ + class Cube { + constructor() { + this.config = Object.assign({}, DEFAULT_CUBE_CONFIG); + this.state = { + /** 是否完成初始化 */ + inited: false, + /** 是否被拦截 */ + isIntercepted: false, + /** 未初始化时添加的等待请求的 module */ + pendingQueue: [], + lostDepModule: {}, + /** 记录资源加载完成后的回调信息 */ + entrances: new Map(), + /** 记录 css 模块加载情况 */ + cssModule: {}, + /** 已下载模块 */ + installedModules: getStringOnlyObj(), + /** 注册模块 */ + registerModules: [], + // 兼容请求 key 带入参,返回 key 不带入参的情况。 + // eg. 请求 /xxx?env=xx 返回 Cube('/xxx',), requireMap 缓存了 { '/xxx': '/xxx?env=xx' } + requireMap: {}, + // 是否处于文件合并执行状态 + fileExecuting: false, + // 聚合请求 + aggregateLoading: {}, + delayTrigger: undefined, + // TODO 新版支持 combine 兜底(combineMap) + }; + /** + * 跳过请求注册模块 + * @param moduleName 模块名 + * @param exports 模块实例 + * @param matchType 匹配模式,version 默认为按版本全匹配; module 按库级别,只要库一致就替换 + */ + this.register = ( + moduleName, + exports, + option = { matchType: 'version' } + ) => { + var _a; + const { matchType } = option; + if ( + (_a = this._getModule(moduleName)) === null || _a === void 0 + ? void 0 + : _a.fired + ) { + return console.warn( + 'Cube Warning: Module ' + + "'" + + moduleName + + "'" + + ' already registered' + ); + } + this.state.installedModules[moduleName] = { + exports: exports, + sourceCode: noop, + dep: [], + refer: { entryDep: [] }, + loaded: true, + firing: false, + fired: true, + }; + if (matchType === 'module') { + this.state.registerModules.push({ + moduleName, + matchType, + match: new RegExp(`^datav:\/npm\/${moduleName}\/([^\/]+)?$`), + module: this.state.installedModules[moduleName], + }); + } + }; + /** 初始化 */ + this.init = (config) => { + var _a, _b, _c, _d, _e, _f, _g; + if (this.state.inited) { + console.warn('Cube 重复初始化,可能产生资源请求错误'); + } + if (config.base && config.base !== '/') { + this.config.base = config.base.replace(/\/$/, ''); + } + if (config.remoteBase) { + for (let key in config.remoteBase) { + if (config.remoteBase.hasOwnProperty(key)) { + this.config.remoteBase[key] = config.remoteBase[key].replace( + /\/$/, + '' + ); + } + } + } + this.config.version = + (_a = config.version) !== null && _a !== void 0 + ? _a + : this.config.version; + this.config.esModule = + (_b = config.esModule) !== null && _b !== void 0 + ? _b + : this.config.esModule; + this.config.debug = + (_c = config.debug) !== null && _c !== void 0 + ? _c + : this.config.debug; + this.config.combine = + (_d = config.combine) !== null && _d !== void 0 + ? _d + : this.config.combine; + this.config.requestMethod = + (_e = config.requestMethod) !== null && _e !== void 0 + ? _e + : this.config.requestMethod; + this.config.fetchUndeclaredModule = + (_f = config.fetchUndeclaredModule) !== null && _f !== void 0 + ? _f + : this.config.fetchUndeclaredModule; + this.config.aggregateFetch = + (_g = config.aggregateFetch) !== null && _g !== void 0 + ? _g + : this.config.aggregateFetch; + this.config.fetchMethod = config.fetchMethod || fetch; + this.config.onCodeError = config.onCodeError; + this.state.inited = true; + for (let i = 0; i < this.state.pendingQueue.length; i++) { + let pendingInfo = this.state.pendingQueue[i]; + this._load(pendingInfo[0], pendingInfo[1]); + } + this.state.pendingQueue = []; + }; + /** + * 异步加载模块 + */ + this.use = ( + moduleName, + refererOrCallback, + callbackOrOmitFix, + omitFixOrUndefined + ) => { + if (!moduleName) { + throw new Error('Cube.use(moduleName) moduleName is undefined!'); + } + // 整理入参 + // 确保 moduleNames 唯一 + let moduleNames = + typeof moduleName === 'string' + ? [moduleName] + : typeof moduleName === 'number' + ? [moduleName.toString()] + : [...moduleName]; + let omitFix = omitFixOrUndefined; + // let _referer: string | undefined; + let callback; + if (typeof refererOrCallback === 'string') { + // referer = refererOrCallback; + callback = callbackOrOmitFix; + } else { + // referer = undefined; + callback = refererOrCallback; + omitFix = callbackOrOmitFix; + } + callback = callback || noop; + moduleNames = !omitFix + ? fixMododulePath(moduleNames, this.config.remoteSeparator) + : moduleNames; + const entry = { + callback, + loadSource: {}, + targets: [...moduleNames], + }; + if (this.config.aggregateFetch); + else { + moduleNames.forEach((i) => { + entry.loadSource[i] = false; + }); + } + this.state.entrances.set(moduleNames, entry); + moduleNames.forEach((mName) => this._load(mName, moduleNames)); + }; + /** 执行 cube 源码 即原 Cube(...) */ + this.execute = (responseName, requires, sourceCode) => { + var _a; + if (typeof responseName === 'number') { + responseName = responseName.toString(); + } + const moduleName = this._calibrateName(responseName); + // load 处已做判断 但仍有可能某个模块源码带有其他冗余模块的情况 + if ( + (_a = this.state.installedModules[moduleName]) === null || + _a === void 0 + ? void 0 + : _a.loaded + ) { + return; + } + this._store(moduleName, requires, sourceCode); + this._initiate(moduleName); + }; + /** + * 加载 css + */ + this.css = (css, namespace, file) => { + if (!css) { + return; + } + var modId = file + '@' + namespace; + if (this.state.cssModule[modId]) { + return; + } + this.state.cssModule[modId] = true; + return scriptCubeCss(css, namespace, file); + }; + /** + * 模块存储 + */ + this._store = (moduleName, dep, sourceCode) => { + const module = this.state.installedModules[moduleName]; + if (module) { + module.dep.push(...dep); + module.sourceCode = sourceCode; + module.loaded = true; + } else { + this.state.installedModules[moduleName] = { + exports: {}, + sourceCode, + dep, + refer: { entryDep: [] }, + loaded: true, + firing: false, + fired: false, + }; + } + }; + /** 请求资源 */ + this._load = (moduleName, entryKey) => { + if (typeof moduleName === 'number') { + moduleName = moduleName.toString(); + } + if (!this.config.aggregateFetch) { + const entry = this.state.entrances.get(entryKey); + if (entry && !entry.loadSource.hasOwnProperty(moduleName)) { + entry.loadSource[moduleName] = false; + } + } + if (!this.state.inited || this.state.fileExecuting) { + this.state.pendingQueue.push([moduleName, entryKey]); + return; + } + const module = this._getModule(moduleName); + if (module) { + this._addReferToDependency(moduleName, module, entryKey); + if (module.loaded) { + this._triggerCallback(moduleName, module); + } + return; + } + const [name] = moduleName.split('?'); + this.state.requireMap[name] = moduleName; + this.state.installedModules[moduleName] = { + exports: {}, + sourceCode: undefined, + dep: [], + refer: { + entryDep: [], + }, + loaded: false, + firing: false, + fired: false, + }; + this._addReferToDependency( + moduleName, + this.state.installedModules[moduleName], + entryKey + ); + const srcPath = this._generatePath(moduleName); + this.config.requestMethod === 'fetch' + ? fetchCubeCode( + { + url: srcPath, + fetch: this.config.fetchMethod, + onCodeError: this.config.onCodeError, + }, + combineExecute + ) + : scriptCubeCode(srcPath); + }; + /** 实例化并执行回调 */ + this._initiate = (moduleName) => { + const module = this.state.installedModules[moduleName]; + this._triggerCallback(moduleName, module); + }; + /** 向上检索树依赖及回调 */ + this._triggerCallback = (moduleName, module) => { + if (!module.loaded) return; + if (this.config.aggregateFetch) { + module.dep.forEach((m) => { + var _a; + if ( + (_a = this._getModule(m)) === null || _a === void 0 + ? void 0 + : _a.loaded + ) { + return; + } + this._load(m, []); + }); + if (this.state.aggregateLoading[moduleName]) { + delete this.state.aggregateLoading[moduleName]; + this._triggerAllCallback(); + } + return; + } + const refDep = module.refer; + let finishedEntry = []; + refDep.entryDep.forEach((entryKey) => { + const entry = this.state.entrances.get(entryKey); + if (entry) { + entry.loadSource[moduleName] = true; + let next = true; + if (!module.fired) { + module.dep.forEach((m) => { + if (entry.loadSource[m]) return; + const subModule = this._getModule(m); + if ( + subModule === null || subModule === void 0 + ? void 0 + : subModule.fired + ) + return; + next = false; + this._load(m, entryKey); + }); + } + if (next) { + // 考虑标记 unload 提速 + if (Object.values(entry.loadSource).every((i) => i)) { + this._triggerEntryCallback(entryKey, entry); + } + } + } else { + finishedEntry.push(entryKey); + } + }); + if (finishedEntry.length) { + module.refer.entryDep = refDep.entryDep.filter( + (i) => !finishedEntry.includes(i) + ); + } + }; + /** 执行回调函数 */ + this._triggerEntryCallback = (entryKey, entry) => { + let readyCallback = true; + entry.targets.forEach((moduleName) => { + const module = this._getModule(moduleName); + if (module.fired) return; + // 理论上不会不存在 + if (module.firing) { + readyCallback = false; + return; + } + this._fireModule(moduleName); + if (module.fired) return; + readyCallback = false; + }); + if (readyCallback) { + entry.callback( + ...entry.targets.map((e) => this.state.installedModules[e].exports) + ); + this.state.entrances.delete(entryKey); + } + }; + this._triggerAllCallback = () => { + if (this.state.delayTrigger) return; + // this.state.delayTrigger = setTimeout(() => { + // this.state.delayTrigger = undefined; + if (this.state.pendingQueue.length) return; + if (Object.keys(this.state.aggregateLoading).length) return; + this.state.entrances.forEach((entry, entryKey) => { + this._triggerEntryCallback(entryKey, entry); + }); + // }); + }; + /** 实例化某一模块 */ + this._fireModule = (moduleName) => { + const module = this.state.installedModules[moduleName]; + if (!module || !module.loaded) return false; + if (module.fired) return true; + // 处理循环依赖问题 + if (module.firing) { + return true; + } + let fireResult = true; + try { + module.firing = true; + const exports = module.sourceCode.apply(window, [ + module, + // 此处需要组件不改变实例 + module.exports, + this._cubeRequire(moduleName), + this._cubeLoad(moduleName), + this.config.mockedProcess, + this.config.mockedGlobal, + ]); + module.exports = this._isEsModule(exports) + ? exports.default + : exports; + module.error = false; + } catch (e) { + if ( + this.config.fetchUndeclaredModule && + e.message === `Cube inner denpendency lost; refetch inited` + ) { + console.warn('Cube 检测到文件依赖缺失'); + fireResult = false; + } else { + console.error('Cube 生成实例失败', e); + console.error(moduleName, module); + module.error = true; + } + } finally { + module.firing = false; + if (this.config.fetchUndeclaredModule) { + // 避免组件内部有 catch 导致 抓不到错误的情况 + if (this.state.lostDepModule[moduleName]) { + this.state.lostDepModule[moduleName].forEach((name) => { + if (!module.dep.includes(name)) { + module.dep.push(name); + module.refer.entryDep.forEach((eKey) => { + this._load(name, eKey); + }); + console.warn( + `Cube module ${moduleName} 缺失声明依赖 ${name}` + ); + } + }); + Reflect.deleteProperty(this.state.lostDepModule, moduleName); + fireResult = false; + } + } else { + module.fired = true; + } + } + return fireResult; + }; + /** 支持组件内模块请求 */ + this._cubeRequire = (selfName) => (moduleName, namespace) => { + if (namespace === undefined) { + const module = this._getModule(moduleName); + if (module === null || module === void 0 ? void 0 : module.fired) { + return module.exports; + } + const fireFinished = this._fireModule(moduleName); + if (!module || !fireFinished) { + if (this.config.fetchUndeclaredModule) { + if (this.state.lostDepModule[selfName]) { + this.state.lostDepModule[selfName].push(moduleName); + } else { + this.state.lostDepModule[selfName] = [moduleName]; + } + // WATCH! 由于组件内相对路径依赖没有声明 导致必须强行中断流程, + // 后续应该将相对依赖加入依赖中 + throw new Error(`Cube inner denpendency lost; refetch inited`); + } else { + throw new Error(`Cube 获取未声明资源 ${moduleName} 失败`); + } + } else { + return module.exports; + } + } else { + // 默认 css 模块不再依赖其它模块 + let css; + const module = this._getModule(moduleName); + if (!module || !module.loaded) return; + if (module.fired) { + css = module.exports; + } + const fireSucceed = this._fireModule(moduleName); + if (fireSucceed) { + css = module.exports; + } + return this.css(css, namespace, moduleName); + } + }; + /** 支持组件内模块加载 */ + this._cubeLoad = (referer) => { + /** The load function */ + const __cube_load__ = (moduleName, namespace, cb) => { + if (cb === undefined && typeof namespace === 'function') { + cb = namespace; + namespace = ''; + this.use(moduleName, referer, cb); + } else { + this.use(moduleName, referer, (css) => { + css = this.css(css, namespace, moduleName); + cb && cb(css); + }); + } + }; + return __cube_load__; + }; + /** 请求路径生成 */ + this._generatePath = (moduleName) => { + // 只有拼 src 时要带上 m & ref 时才需要分离 require 里的入参 query, 平时 /xxx?query=xx 才作为 installedModules 的 key + const [name, custom] = moduleName.split('?'); + let srcPath = rebase(name, this.config); + const query = []; + if (this.config.version) { + query.push(this.config.version); + } + if (this.config.combine) { + query.push('combine=true'); + } + if (custom) { + const customArgs = parseQueryString(custom); + query.push( + Object.keys(customArgs).map((c) => { + return `${c}=${customArgs[c]}`; + }) + ); + } + // 历史逻辑 疑似命中缓存 + if (!query.includes('m=1')) { + query.push('m=1'); + } + if (query.length) { + srcPath = srcPath + '?' + query.join('&'); + } + return srcPath; + }; + /** 存储引用关系 */ + this._addReferToDependency = (moduleName, module, referer) => { + if (this.config.aggregateFetch) { + if (!module.loaded) { + this.state.aggregateLoading[moduleName] = true; + } + return; + } + const refDep = module.refer; + if (!referer) { + return; + } + if (!refDep.entryDep.includes(referer)) { + refDep.entryDep.push(referer); + } + }; + /** 修正返回值 */ + this._calibrateName = (responseName) => { + // 兼容返回的 name 不带入参的情况 + const moduleName = this.state.requireMap[responseName] || responseName; + if (this.state.requireMap[responseName]) { + Reflect.deleteProperty(this.state.requireMap, responseName); + } + return moduleName; + }; + /** + * 获取全局默认模块 + * requirePath => datav:/npm/react/16.4.6?env=xxx + */ + this._getGlobalRegister = (requirePath) => { + for (const register of this.state.registerModules) { + if (requirePath && register.match.test(requirePath)) { + return register.module; + } + } + }; + this._getModule = (name) => { + // TODO 此处有问题 理论上优先选取 globalRegister + // 但考虑到正则匹配的耗时 每次匹配耗时过长 + // 所以应该是此处顺序不变 注册的时候做一次是否满足正则的校验 + const module = this.state.installedModules[name]; + if (!module) { + return this._getGlobalRegister(name); + } + return module; + }; + this._isEsModule = (module) => { + return ( + this.config.esModule && + module && + typeof module === 'object' && + module.__esModule + ); + }; + this.cStart = () => { + this.state.fileExecuting = true; + }; + this.cStop = () => { + this.state.fileExecuting = false; + for (let i = 0; i < this.state.pendingQueue.length; i++) { + let pendingInfo = this.state.pendingQueue[i]; + this._load(pendingInfo[0], pendingInfo[1]); + } + this.state.pendingQueue = []; + }; + /****************************** 以下为原有方法兼容 **************************/ + /** 原有方法 直接打印内部状态 */ + this.cache = () => { + console.info( + 'modules:', + Object.fromEntries(Object.entries(this.state.installedModules)) + ); + console.info( + 'unloaded:', + Object.fromEntries( + Object.entries(this.state.installedModules).filter( + (m) => !m[1].loaded + ) + ) + ); + console.info( + 'unfired:', + Object.fromEntries( + Object.entries(this.state.installedModules).filter( + (m) => !m[1].fired + ) + ) + ); + }; + /** @deprecated */ + this.debug = () => { + console.error('debug 方法不再支持'); + }; + /** @deprecated */ + this.setRemoteBase = () => { + console.error('不支持动态修改 remoteBase'); + }; + } + } + function getStringOnlyObj() { + return new Proxy( + {}, + { + get: function (target, propKey) { + const key = + typeof propKey === 'number' ? propKey.toString() : propKey; + return Reflect.get(target, key); + }, + set: function (target, propKey, value, receiver) { + const key = + typeof propKey === 'number' ? propKey.toString() : propKey; + return Reflect.set(target, key, value, receiver); + }, + } + ); + } + /** 全局初始化单例 */ + function setGlobalCube(oldVersion) { + const alias = 'Cube'; + const global = typeof window !== 'undefined' ? window : globalThis; + if (global[alias]) { + console.error('Cube Error: window.' + alias + ' already in using'); + return global[alias]; + } + if (oldVersion) { + mockClassicalCube(global); + } else { + const cube = new Cube(); + // 支持 Cube(...args) 的写法 + const cubeHandler = (moduleName, requires, instance) => { + return cube.execute(moduleName, requires, instance); + }; + const mockCube = new Proxy(cubeHandler, { + get: function (handler, key) { + if (Reflect.ownKeys(cube).includes(key)) { + return cube[key]; + } + return Reflect.get(handler, key); + }, + }); + global[alias] = mockCube; + } + const cubeVersion = '5.0.0-beta.27'; + global[alias].cubeVersion = cubeVersion; + global[alias].oldVersion = oldVersion; + return global[alias]; + } + + if (typeof window === 'undefined') { + module.exports = setGlobalCube(true); + } else { + setGlobalCube(true); + } +})(typeof window !== 'undefined' ? window : globalThis); diff --git a/runtime/cube-reconstruction.min.js b/runtime/cube-reconstruction.min.js new file mode 100644 index 0000000..3b9dd44 --- /dev/null +++ b/runtime/cube-reconstruction.min.js @@ -0,0 +1,4 @@ +/*! + * Cube v5.0.0-beta.28 + */ + (function(a){function b(){}function c(a){return a}function d(a){return'Cube.cStart();'+a+';Cube.cStop();'}function e(a,b,d){const e='string'===typeof a?{url:a}:a;return('function'===typeof e.fetch?e.fetch:fetch)(e.url,{headers:{"Content-Type":'text/plain'}}).then((a)=>(d&&d(a),a)).then((a)=>a.text()).then((a)=>{var d;try{return new Function((b||c)(a))()}catch(a){null===(d=e.onCodeError)||void 0===d?void 0:d.call(e,a,{url:e.url}),console.error(a)}})}function f(a){var b=null===document||void 0===document?void 0:document.createElement('script');b.type='text/javascript',b.async=!0,b.onerror=()=>{console.error(`load module failed.`)},b.src=a,null===v||void 0===v?void 0:v.appendChild(b)}function g(a,b){for(var c,d=a.length,e=0;e{let b=a.split('=');c[b[0]]=b[1]}),c}function j(a,b){const{base:c,remoteSeparator:d,remoteBase:e}=b;let f=c+a;var g=a.indexOf?a.indexOf(d):0;if(0>=g)return f;var h=a.substr(0,g);return e[h]?e[h]+a.substr(g+1):f}function k(a){const[b,c]=(a+'').split('?');if(c){let a=c.split('&');if(a.includes('env=publish')){a=a.filter((a)=>'env=publish'!==a);const c=a.join('&');return c?b+'?'+c:b}}return a}function l(a){const b=/^((@[^/]+\/[^/]+|[^/]+))(?:\/(.*))?$/,c=a.match(b);if(!c)return null;const d=c[1],e=c[3]||'';return{moduleName:d,modulePath:e}}function m(a){const b=/^datav:\/npm\/((?:@[^/]+\/[^/]+)|[^/]+)(?:\/([^/?]+))?(?:\/([^?]*))?/,c=a.match(b);if(!c)return null;const d=c[1],e=c[3]||'';return{moduleName:d,modulePath:e}}function n(a,b,c){return(b=s(b))in a?Object.defineProperty(a,b,{value:c,enumerable:!0,configurable:!0,writable:!0}):a[b]=c,a}function o(a,b){var c=Object.keys(a);if(Object.getOwnPropertySymbols){var d=Object.getOwnPropertySymbols(a);b&&(d=d.filter(function(b){return Object.getOwnPropertyDescriptor(a,b).enumerable})),c.push.apply(c,d)}return c}function p(a){for(var b,c=1;c{P[a]&&(Q[a].failed=!0,o(a,b))},U),failed:!1})),g){const a=i(g);Array.prototype.push.apply(l,Object.keys(a).map((b)=>''.concat(b,'=').concat(a[b])))}l.length&&(k=k+'?'+l.join('&')),'fetch'===X?H&&Q[a]&&!Q[a].traceId?e({url:k,fetch:V,onCodeError:W},void 0,(b)=>{b.headers.has('request-id')&&(Q[a].traceId=b.headers.get('request-id'))}):e(k):f(k),M[d]=a,P[a]=!0}),n()):void T.push([a,b])}function q(a){return!!H&&(!R.length||!R.some((b)=>!!a.match(b)))}function r(a){if(!a.startsWith('datav:/npm/'))return!1;let{moduleName:b,modulePath:c}=m(a);return!!N[b]&&((c=c||'default',N[b][c])?N[b][c].module:(Object.entries(N[b]).forEach((b)=>{let[c,d]=b;if(d.match.test(a))return d.module}),!1))}function s(b){var f=r(b)||O[b];if(!f){const a=new Error('Cube Error: Cannot find module \''+b+'\'');if(G)throw a;else return B.error(a),{}}if(!f.fired)if(f.fired=!0,G)f.exports=f.fn.apply(a,[f,f.exports,c,d(b),I,J]);else try{f.exports=f.fn.apply(a,[f,f.exports,c,d(b),I,J])}catch(a){B.error(a),f.exports={}}return v(f.exports)?f.exports.default:f.exports}function t(){for(let[a,b]of L)a.length&&a.forEach(function(c){if(P[c])return;var d=0;const e=s(c);b.length&&b.forEach(function(a){var b=a(e);b&&d++}),b.length===d&&L.delete(a)})}function u(a,b,c){const d=a+'';a=M[a]||a;var e=O[a];e=k(e),e||(e=O[a]={exports:{},fired:!1}),Q[a]&&!e.loaded&&(Q[a].failed?Q[a].end=Date.now():(clearTimeout(Q[a].timeout),delete Q[a])),e.loaded=!0,e.fn=c,M[d]&&delete M[d],P[a]?(delete P[a],o(b,a)):b.length&&o(b,a)}function v(a){return K&&a&&'object'===typeof a&&a.__esModule}var w,x,y,z,A='undefined'===typeof document?{}:document,B=console,C='',D={},E=':',F='utf-8',G=!0,H=!0,I=p(p({},null!==(w=a.process)&&void 0!==w?w:{}),{},{env:p(p({},null!==(x=null===(y=a.process)||void 0===y?void 0:y.env)&&void 0!==x?x:{}),{},{NODE_ENV:'production'})}),J=void 0,K=!1,L=new Map,M={},N={},O={},P={},Q={},R=[],S=!1,T=[],U=1e4;let V,W,X='script';u.setRemoteBase=function(a){Object.assign(D,a)},u.toString=function(){return'Cube:v5.0.0-beta.28'},u.init=function(a){if(a.base&&'/'!==a.base&&(C=a.base.replace(/\/$/,'')),a.remoteBase)for(var b in a.remoteBase)a.remoteBase.hasOwnProperty(b)&&(D[b]=a.remoteBase[b].replace(/\/$/,''));for(a.charset&&(F=a.charset),a.version&&(z=a.version),void 0!==a.strict&&(G=a.strict),a.env&&(I.env.NODE_ENV=a.env),a.global&&(J=a.global),void 0!==a.combine&&(H=a.combine),a.combineBlackList&&(R=a.combineBlackList),a.requestMethod&&(X=a.requestMethod),void 0!==a.esModule&&(K=a.esModule),a.fetchMethod&&(V=a.fetchMethod),a.onCodeError&&(W=a.onCodeError),a.combineFailTime&&(U=a.combineFailTime),S=!0;T.length;){var c=T.shift();o(c[0],c[1])}return this},u.use=function(c,d,e,f){if(!c)throw new Error('Cube.use(moduleName) moduleName is undefined!');return'function'===typeof d&&(f=e,e=d,d=void 0),d||(d='Cube.use'),e=e||b,c='string'===typeof c?[k(c)]:c.map(k),f||(c=g(c,E)),L.has(c)||L.set(c,[]),L.get(c).push(function(){var b=[],d=c.length,f=!1;return function(c){if(!f)return(b.push(c),b.length===d)?(f=!0,e.apply(a,b),!0):void 0}}()),o(c,d,!0),this},u.register=function(a,c){let{matchType:d='version'}=2\\d+\\.\\d+\\.\\d+(-[a-zA-Z0-9.-]+)?(\\+[a-zA-Z0-9.-]+)?)/').concat(c,'$')),module:O[a],moduleName:b}}return this};var Y={};u.css=function(a,b,c){if(a){var d=c+'@'+b;if(!Y[d])return Y[d]=!0,h(a,b,c)}},u.debug=function(){B.error('Cube Error: Cube.debug nolonger supported')},u.cache=function(){var a,b,c={},d={};for(a in O)O.hasOwnProperty(a)&&(b=O[a],b.loaded||(c[a]=b),b.fired||(d[a]=b));B.info('modules:',O),B.info('unloaded:',c),B.info('unfired:',d)},a.Cube?B.error('Cube Error: window.Cube already in using, replace the last "null" param in cube.js'):a.Cube=u;var Z=A.currentScript;if(Z){var $=Z.dataset;$.base&&(u.init($),u.use($.main||'index.js',function(a){a.run&&a.run()}))}Object.defineProperty(u,'config',{get(){return{base:C,remoteBase:D,remoteSeparator:E,version:z,strict:G,debug:!0,esModule:K,mockedGlobal:J,mockedProcess:I,charset:F,combine:H,combineMap:Q}}})}function t(){return new Proxy({},{get:function(a,b){const c='number'===typeof b?b.toString():b;return Reflect.get(a,c)},set:function(a,b,c,d){const e='number'===typeof b?b.toString():b;return Reflect.set(a,e,c,d)}})}function u(b){const c='Cube',d='undefined'===typeof window?a:window;if(d[c])return console.error('Cube Error: window.'+c+' already in using'),d[c];if(b)r(d);else{const a=new y,b=new Proxy((b,c,d)=>a.execute(b,c,d),{get:function(b,c){return Reflect.ownKeys(a).includes(c)?a[c]:Reflect.get(b,c)}});d[c]=b}return d[c].cubeVersion='5.0.0-beta.27',d[c].oldVersion=b,d[c]}const v='undefined'===typeof document?{appendChild:()=>{}}:document.querySelector('head'),w=/([^};]+)(\{[^}]+\})/g,x={base:'',remoteBase:{},remoteSeparator:':',mockedProcess:{env:{NODE_ENV:'production'}},mockedGlobal:void 0,charset:'utf-8',strict:!0,esModule:!1,version:void 0,debug:!0,combine:!1,requestMethod:'fetch',fetchUndeclaredModule:!1,aggregateFetch:!0,fetchMethod:void 0,onCodeError:void 0};class y{constructor(){this.config=Object.assign({},x),this.state={inited:!1,isIntercepted:!1,pendingQueue:[],lostDepModule:{},entrances:new Map,cssModule:{},installedModules:t(),registerModules:[],requireMap:{},fileExecuting:!1,aggregateLoading:{},delayTrigger:void 0},this.register=(a,c,d={matchType:'version'})=>{var e;const{matchType:f}=d;return(null===(e=this._getModule(a))||void 0===e?void 0:e.fired)?console.warn('Cube Warning: Module \''+a+'\' already registered'):void(this.state.installedModules[a]={exports:c,sourceCode:b,dep:[],refer:{entryDep:[]},loaded:!0,firing:!1,fired:!0},'module'===f&&this.state.registerModules.push({moduleName:a,matchType:f,match:new RegExp(`^datav:\/npm\/${a}\/([^\/]+)?$`),module:this.state.installedModules[a]}))},this.init=(a)=>{var b,c,d,e,f,g,h;if(this.state.inited&&console.warn('Cube \u91CD\u590D\u521D\u59CB\u5316\uFF0C\u53EF\u80FD\u4EA7\u751F\u8D44\u6E90\u8BF7\u6C42\u9519\u8BEF'),a.base&&'/'!==a.base&&(this.config.base=a.base.replace(/\/$/,'')),a.remoteBase)for(let b in a.remoteBase)a.remoteBase.hasOwnProperty(b)&&(this.config.remoteBase[b]=a.remoteBase[b].replace(/\/$/,''));this.config.version=null!==(b=a.version)&&void 0!==b?b:this.config.version,this.config.esModule=null!==(c=a.esModule)&&void 0!==c?c:this.config.esModule,this.config.debug=null!==(d=a.debug)&&void 0!==d?d:this.config.debug,this.config.combine=null!==(e=a.combine)&&void 0!==e?e:this.config.combine,this.config.requestMethod=null!==(f=a.requestMethod)&&void 0!==f?f:this.config.requestMethod,this.config.fetchUndeclaredModule=null!==(g=a.fetchUndeclaredModule)&&void 0!==g?g:this.config.fetchUndeclaredModule,this.config.aggregateFetch=null!==(h=a.aggregateFetch)&&void 0!==h?h:this.config.aggregateFetch,this.config.fetchMethod=a.fetchMethod||fetch,this.config.onCodeError=a.onCodeError,this.state.inited=!0;for(let b,c=0;c{if(!a)throw new Error('Cube.use(moduleName) moduleName is undefined!');let f,h='string'===typeof a?[a]:'number'===typeof a?[a.toString()]:[...a],i=e;'string'===typeof c?f=d:(f=c,i=d),f=f||b,h=i?h:g(h,this.config.remoteSeparator);const j={callback:f,loadSource:{},targets:[...h]};if(this.config.aggregateFetch);else h.forEach((a)=>{j.loadSource[a]=!1});this.state.entrances.set(h,j),h.forEach((a)=>this._load(a,h))},this.execute=(a,b,c)=>{var d;'number'===typeof a&&(a=a.toString());const e=this._calibrateName(a);(null===(d=this.state.installedModules[e])||void 0===d?void 0:d.loaded)||(this._store(e,b,c),this._initiate(e))},this.css=(a,b,c)=>{if(a){var d=c+'@'+b;if(!this.state.cssModule[d])return this.state.cssModule[d]=!0,h(a,b,c)}},this._store=(a,b,c)=>{const d=this.state.installedModules[a];d?(d.dep.push(...b),d.sourceCode=c,d.loaded=!0):this.state.installedModules[a]={exports:{},sourceCode:c,dep:b,refer:{entryDep:[]},loaded:!0,firing:!1,fired:!1}},this._load=(a,b)=>{if('number'===typeof a&&(a=a.toString()),!this.config.aggregateFetch){const c=this.state.entrances.get(b);c&&!c.loadSource.hasOwnProperty(a)&&(c.loadSource[a]=!1)}if(!this.state.inited||this.state.fileExecuting)return void this.state.pendingQueue.push([a,b]);const c=this._getModule(a);if(c)return this._addReferToDependency(a,c,b),void(c.loaded&&this._triggerCallback(a,c));const[g]=a.split('?');this.state.requireMap[g]=a,this.state.installedModules[a]={exports:{},sourceCode:void 0,dep:[],refer:{entryDep:[]},loaded:!1,firing:!1,fired:!1},this._addReferToDependency(a,this.state.installedModules[a],b);const h=this._generatePath(a);'fetch'===this.config.requestMethod?e({url:h,fetch:this.config.fetchMethod,onCodeError:this.config.onCodeError},d):f(h)},this._initiate=(a)=>{const b=this.state.installedModules[a];this._triggerCallback(a,b)},this._triggerCallback=(a,b)=>{if(!b.loaded)return;if(this.config.aggregateFetch)return b.dep.forEach((a)=>{var b;(null===(b=this._getModule(a))||void 0===b?void 0:b.loaded)||this._load(a,[])}),void(this.state.aggregateLoading[a]&&(delete this.state.aggregateLoading[a],this._triggerAllCallback()));const c=b.refer;let d=[];c.entryDep.forEach((c)=>{const e=this.state.entrances.get(c);if(e){e.loadSource[a]=!0;let d=!0;b.fired||b.dep.forEach((a)=>{if(!e.loadSource[a]){const b=this._getModule(a);(null===b||void 0===b?void 0:b.fired)||(d=!1,this._load(a,c))}}),d&&Object.values(e.loadSource).every((a)=>a)&&this._triggerEntryCallback(c,e)}else d.push(c)}),d.length&&(b.refer.entryDep=c.entryDep.filter((a)=>!d.includes(a)))},this._triggerEntryCallback=(a,b)=>{let c=!0;b.targets.forEach((a)=>{const b=this._getModule(a);if(!b.fired){if(b.firing)return void(c=!1);this._fireModule(a),b.fired||(c=!1)}}),c&&(b.callback(...b.targets.map((a)=>this.state.installedModules[a].exports)),this.state.entrances.delete(a))},this._triggerAllCallback=()=>{this.state.delayTrigger||this.state.pendingQueue.length||Object.keys(this.state.aggregateLoading).length||this.state.entrances.forEach((a,b)=>{this._triggerEntryCallback(b,a)})},this._fireModule=(a)=>{const b=this.state.installedModules[a];if(!b||!b.loaded)return!1;if(b.fired)return!0;if(b.firing)return!0;let c=!0;try{b.firing=!0;const c=b.sourceCode.apply(window,[b,b.exports,this._cubeRequire(a),this._cubeLoad(a),this.config.mockedProcess,this.config.mockedGlobal]);b.exports=this._isEsModule(c)?c.default:c,b.error=!1}catch(d){this.config.fetchUndeclaredModule&&`Cube inner denpendency lost; refetch inited`===d.message?(console.warn('Cube \u68C0\u6D4B\u5230\u6587\u4EF6\u4F9D\u8D56\u7F3A\u5931'),c=!1):(console.error('Cube \u751F\u6210\u5B9E\u4F8B\u5931\u8D25',d),console.error(a,b),b.error=!0)}finally{b.firing=!1,this.config.fetchUndeclaredModule?this.state.lostDepModule[a]&&(this.state.lostDepModule[a].forEach((c)=>{b.dep.includes(c)||(b.dep.push(c),b.refer.entryDep.forEach((a)=>{this._load(c,a)}),console.warn(`Cube module ${a} 缺失声明依赖 ${c}`))}),Reflect.deleteProperty(this.state.lostDepModule,a),c=!1):b.fired=!0}return c},this._cubeRequire=(a)=>(b,c)=>{if(void 0===c){const c=this._getModule(b);if(null===c||void 0===c?void 0:c.fired)return c.exports;const d=this._fireModule(b);if(c&&d)return c.exports;if(this.config.fetchUndeclaredModule)throw this.state.lostDepModule[a]?this.state.lostDepModule[a].push(b):this.state.lostDepModule[a]=[b],new Error(`Cube inner denpendency lost; refetch inited`);else throw new Error(`Cube 获取未声明资源 ${b} 失败`)}else{let a;const d=this._getModule(b);if(!d||!d.loaded)return;d.fired&&(a=d.exports);const e=this._fireModule(b);return e&&(a=d.exports),this.css(a,c,b)}},this._cubeLoad=(a)=>{return(b,c,d)=>{d===void 0&&'function'===typeof c?(d=c,c='',this.use(b,a,d)):this.use(b,a,(a)=>{a=this.css(a,c,b),d&&d(a)})}},this._generatePath=(a)=>{const[b,c]=a.split('?');let d=j(b,this.config);const e=[];if(this.config.version&&e.push(this.config.version),this.config.combine&&e.push('combine=true'),c){const a=i(c);e.push(Object.keys(a).map((b)=>`${b}=${a[b]}`))}return e.includes('m=1')||e.push('m=1'),e.length&&(d=d+'?'+e.join('&')),d},this._addReferToDependency=(a,b,c)=>{if(this.config.aggregateFetch)return void(b.loaded||(this.state.aggregateLoading[a]=!0));const d=b.refer;c&&(d.entryDep.includes(c)||d.entryDep.push(c))},this._calibrateName=(a)=>{const b=this.state.requireMap[a]||a;return this.state.requireMap[a]&&Reflect.deleteProperty(this.state.requireMap,a),b},this._getGlobalRegister=(a)=>{for(const b of this.state.registerModules)if(a&&b.match.test(a))return b.module},this._getModule=(a)=>{const b=this.state.installedModules[a];return b?b:this._getGlobalRegister(a)},this._isEsModule=(a)=>this.config.esModule&&a&&'object'===typeof a&&a.__esModule,this.cStart=()=>{this.state.fileExecuting=!0},this.cStop=()=>{this.state.fileExecuting=!1;for(let a,b=0;b{console.info('modules:',Object.fromEntries(Object.entries(this.state.installedModules))),console.info('unloaded:',Object.fromEntries(Object.entries(this.state.installedModules).filter((a)=>!a[1].loaded))),console.info('unfired:',Object.fromEntries(Object.entries(this.state.installedModules).filter((a)=>!a[1].fired)))},this.debug=()=>{console.error('debug \u65B9\u6CD5\u4E0D\u518D\u652F\u6301')},this.setRemoteBase=()=>{console.error('\u4E0D\u652F\u6301\u52A8\u6001\u4FEE\u6539 remoteBase')}}}'undefined'===typeof window?module.exports=u(!0):u(!0)})('undefined'===typeof window?globalThis:window); \ No newline at end of file diff --git a/runtime/cube.js b/runtime/cube.js index b68c04c..d094896 100644 --- a/runtime/cube.js +++ b/runtime/cube.js @@ -22,12 +22,17 @@ var version; var strict = true; var debug = true; - var entrances = {}; // Cube.use's cb + var entrances = new Map(); // Cube.use's cb + // 兼容请求 key 带入参,返回 key 不带入参的情况。eg. 请求 /xxx?env=xx 返回 Cube('/xxx',), requireMap 缓存了 { '/xxx': '/xxx?env=xx' } + // 此兼容是在业务方已知的情况,后期会改造返回的代码头。 + var requireMap = {}; + var registerArr = []; - var mockedProcess = { + var mockedProcess = Object.assign(global.process || {}, { env: {NODE_ENV: 'production'} - }; + }); var mockedGlobal = undefined; + var esModule = false; var installedModules = {/*exports, fn, loaded, fired*/}; // The module cache var loading = {}; @@ -37,7 +42,7 @@ /* store requires before init */ var inited = false; var loadQueue = []; - console.time('cube load'); + debug && console.time('cube load'); /** * The require function * @param module @@ -89,11 +94,12 @@ */ function reBase(mod) { var offset = mod.indexOf ? mod.indexOf(remoteSeparator) : 0; - if (offset > 0) { - return remoteBase[mod.substr(0, offset)] + mod.substr(offset + 1); - } else { - return ''; - } + if (offset <= 0) return ''; + + var rbase = mod.substr(0, offset); + if (!remoteBase[rbase]) return ''; + + return remoteBase[rbase] + mod.substr(offset + 1); } function fixUseModPath(mods) { @@ -122,7 +128,7 @@ return false; } } - console.timeEnd('cube load'); + debug && console.timeEnd('cube load'); startAppAndCallback(); } @@ -141,9 +147,12 @@ } requires.forEach(function (require) { - if (installedModules[require]) { + if (installedModules[require] || getGlobalRegister(require)) { return; } + + // 只有拼 src 时要带上 m & ref 时才需要分离 require 里的入参 query, 平时 /xxx?query=xx 才作为 installedModules 的 key + const [mod, custom] = String(require).split('?'); // download form server var script = doc.createElement('script'); script.type = 'text/javascript'; @@ -155,8 +164,9 @@ }); }; - var rebaseName = reBase(require); - var srcPath = rebaseName || (base + require); + var rebaseName = reBase(mod); + var srcPath = rebaseName || (base + mod); + var q = []; if (version) { q.push(version); @@ -166,6 +176,13 @@ q.push('ref=' + referer); } + if (custom) { + const customArgs = parseQueryString(custom); + Array.prototype.push.apply(q, Object.keys(customArgs).map(c => { + return `${c}=${customArgs[c]}` + })); + } + if (q.length) { script.src = srcPath + '?' + q.join('&'); } else { @@ -177,23 +194,35 @@ loaded: false, fired: false }; + requireMap[mod] = require; loading[require] = true; }); checkAllDownloaded(); } + // require => datav:/npm/react/16.4.6?env=xxx + function getGlobalRegister(require) { + for (const register of registerArr) { + if (require && register.match.test(require)) { + return register.module; + } + } + return false; + } + /** * 运行模块 * @param module * @returns {*} */ function fireModule(module) { - var m = installedModules[module]; + var m = getGlobalRegister(module) || installedModules[module]; if (!m) { + const err = new Error('Cube Error: Cannot find module ' + '\'' + module + '\''); if (strict) { - throw new Error('Cube Error: Cannot find module ' + '\'' + module + '\''); + throw err; } else { - log.error(e); + log.error(err); return {}; } } @@ -210,44 +239,46 @@ } } } - return m.exports; + return isEsModule(m.exports) ? m.exports.default : m.exports; } /** * 从Cube.use的文件开始自上而下运行,并调用回调函数 */ function startAppAndCallback() { - var key, arr; - console.time('cube exec'); - for (key in entrances) { - if (entrances.hasOwnProperty(key)) { - arr = key.split(','); - arr.forEach(function (entrance) { - var count = 0; - fireModule(entrance); - entrances[key].forEach(function (fn) { - var called = fn(installedModules[entrance].exports); - if (called) { - count++; - } - }); - if (entrances[key].length === count) { // 回调函数都执行完后删除 - delete entrances[key]; + debug && console.time('cube exec'); + for (let [key, value] of entrances) { + key.length && key.forEach(function (entrance) { + // 出现多次 startAppAndCallback, 在某次 startAppAndCallback 未结束时,entrances 增加了,但其实 loading 并未结束 + // 严格检查 + if (loading[entrance]) return; + var count = 0; + const exportModule = fireModule(entrance); + value.length && value.forEach(function (fn) { + var called = fn(exportModule); + if (called) { + count++; } }); - } + if (value.length === count) { // 回调函数都执行完后删除 + entrances.delete(key); + } + }); } - console.timeEnd('cube exec'); + debug && console.timeEnd('cube exec'); } - /** * 非构造函数,只供模块的wrapper调用 + * installedModules[name] name 是带入参的,不同入参的,不同key * @param name * @param requires * @param callback */ function Cube(name, requires, callback) { + // 暂时兼容返回的 name 不带入参的情况 + const oldName = String(name); + name = requireMap[name] || name; var mod = installedModules[name]; if (!mod) { mod = installedModules[name] = { @@ -257,6 +288,7 @@ } mod.loaded = true; mod.fn = callback; + requireMap[oldName] && delete requireMap[oldName]; if (loading[name]) { delete loading[name]; load(requires, name); @@ -304,6 +336,10 @@ if (config.global) { mockedGlobal = config.global; } + // support ES6 module, default is true + if (config.esModule !== undefined) { + esModule = config.esModule; + } inited = true; @@ -338,14 +374,16 @@ if (typeof mods === 'string') { mods = [mods]; } + if (!noFix) { mods = fixUseModPath(mods); } - if (!entrances[mods]) { - entrances[mods] = []; + // WARN: mods 是数组,会被自然的用 , 拼接,但 query 入参也可能带 , 所以这边 entrances 用 Map + if (!entrances.has(mods)) { + entrances.set(mods, []); } - entrances[mods].push(function () { + entrances.get(mods).push(function () { var apps = []; var length = mods.length; var firing = false; @@ -368,19 +406,31 @@ }; /** * register module in to cache - * @param {string} module [description] - * @param {} exports [description] + * @param {string} module [description] + * @param {} exports [description] + * @param {object} options 配置项 + * @param {string} options.matchType 匹配模式,version 默认为按版本全匹配; module 按库级别,只要库一致就替换 */ - Cube.register = function (module, exports) { + Cube.register = function (module, exports, { matchType = 'version' } = {}) { if (installedModules[module]) { - return log.error('Cube Error: Module ' + '\'' + module + '\'' + ' already registered'); + return log.warn('Cube Warning: Module ' + '\'' + module + '\'' + ' already registered'); } installedModules[module] = { exports: exports, fn: noop, loaded: true, - fired: true + fired: true, }; + + if (matchType === 'module') { + registerArr.push({ + require: module, + matchType, + match: new RegExp(`^datav:\/npm\/${module}\/([^\/]+)?$`), + module: installedModules[module], + }); + } + return this; }; /** @@ -477,4 +527,18 @@ Cube.use(cfg.main || 'index.js', function(app){app.run&& app.run();}); } } -})(window, null); + + function parseQueryString(param) { + let kvs = param.split('&'); + let obj = {}; + kvs.forEach((kv) => { + let tmp = kv.split('='); + obj[tmp[0]] = tmp[1]; + }); + return obj; + } + + function isEsModule(module) { + return esModule && module && typeof module === 'object' && module.__esModule; + } +})(window, null); \ No newline at end of file diff --git a/runtime/cube.min.js b/runtime/cube.min.js index 86771e8..1102fe9 100644 --- a/runtime/cube.min.js +++ b/runtime/cube.min.js @@ -1,4 +1,4 @@ /*! - * Cube v3.1.16 - */ -(function(a,b){function c(){}function d(a,b){if(1===arguments.length)return k(a);var c=k(a);return m.css(c,b,a),a}function f(a){return function(b,c,d){2===arguments.length&&'function'===typeof c?(d=c,c=null,m.use(b,a,d)):m.use(b,a,function(a){a=m.css(a,c,b),d&&d(a)})}}function g(a){var b=a.indexOf?a.indexOf(t):0;return 0{m(a,[],()=>{console.error(`load module: ${a} failed.`)})};var d=g(a),f=d||r+a,h=[];n&&h.push(n),w&&(h.push('m'),h.push('ref='+b)),c.src=h.length?f+'?'+h.join('&'):f,C.appendChild(c),A[a]={exports:{},loaded:!1,fired:!1},B[a]=!0}}),i()):void E.push([a,b])}function k(b){var c=A[b];if(!c)if(v)throw new Error('Cube Error: Cannot find module \''+b+'\'');else return q.error(e),{};if(!c.fired)if(c.fired=!0,v)c.exports=c.fn.apply(a,[c,c.exports,d,f(b),y,z]);else try{c.exports=c.fn.apply(a,[c,c.exports,d,f(b),y,z])}catch(a){q.error(a),c.exports={}}return c.exports}function l(){var a,b;for(a in console.time('cube exec'),x)x.hasOwnProperty(a)&&(b=a.split(','),b.forEach(function(b){var c=0;k(b),x[a].forEach(function(a){var d=a(A[b].exports);d&&c++}),x[a].length===c&&delete x[a]}));console.timeEnd('cube exec')}function m(a,b,c){var d=A[a];d||(d=A[a]={exports:{},fired:!1}),d.loaded=!0,d.fn=c,B[a]?(delete B[a],j(b,a)):b.length&&j(b,a)}var n,o=window,p=document,q=console,r='',s={},t=':',u='utf-8',v=!0,w=!0,x={},y={env:{NODE_ENV:'production'}},z=void 0,A={},B={},C=p.querySelector('head'),D=!1,E=[];console.time('cube load'),m.setRemoteBase=function(a){Object.assign(s,a)},m.toString=function(){return'Cube:v3.1.16'},m.init=function(a){if(a.base&&'/'!==a.base&&(r=a.base.replace(/\/$/,'')),a.remoteBase)for(var b in a.remoteBase)a.remoteBase.hasOwnProperty(b)&&(s[b]=a.remoteBase[b].replace(/\/$/,''));for(a.charset&&(u=a.charset),a.version&&(n=a.version),void 0!==a.debug&&(w=a.debug),void 0!==a.strict&&(v=a.strict),a.env&&(y.env.NODE_ENV=a.env),a.global&&(z=a.global),D=!0;E.length;){var c=E.shift();j(c[0],c[1])}return this},m.use=function(b,d,f,g){if(!b)throw new Error('Cube.use(moduleName) moduleName is undefined!');return'function'===typeof d&&(g=f,f=d,d=void 0),d||(d='Cube.use'),f=f||c,'string'===typeof b&&(b=[b]),g||(b=h(b)),x[b]||(x[b]=[]),x[b].push(function(){var c=[],d=b.length,g=!1;return function(b){if(!g)return(c.push(b),c.length===d)?(g=!0,f.apply(a,c),!0):void 0}}()),j(b,d),this},m.register=function(a,b){return A[a]?q.error('Cube Error: Module \''+a+'\' already registered'):(A[a]={exports:b,fn:c,loaded:!0,fired:!0},this)};var F=/([^};]+)(\{[^}]+\})/g,G={};m.css=function(a,b,c){if(a){var d=c+'@'+b;if(!G[d]){G[d]=!0,b&&(a=a.replace(F,function(a,c,d){var f=c.split(',').map(function(a){return b+' '+a.trim()});return f.join(',')+d}));var f=p.createElement('style');return f.setAttribute('type','text/css'),f.setAttribute('mod',c),b&&f.setAttribute('ns',b),C.appendChild(f),f.innerHTML=a,a}}},m.debug=function(){o.localStorage&&o.addEventListener?(localStorage.cube='debug',location.reload()):q.error('Cube Error: Cannot debug, your browser does not support localStorage or addEventListener')},m.cache=function(){var a,b,c={},d={};for(a in A)A.hasOwnProperty(a)&&(b=A[a],b.loaded||(c[a]=b),b.fired||(d[a]=b));q.info('modules:',A),q.info('unloaded:',c),q.info('unfired:',d)},o.localStorage&&'debug'===localStorage.cube&&(w=!0,o.addEventListener('load',m.cache)),b=b||'Cube',a[b]?q.error('Cube Error: window.'+b+' already in using, replace the last "null" param in cube.js'):a[b]=m;var H=p.currentScript;if(H){var I=H.dataset;I.base&&(m.init(I),m.use(I.main||'index.js',function(a){a.run&&a.run()}))}})(window,null); \ No newline at end of file + * Cube v5.0.0-beta.28 + */ + (function(a,b){function c(){}function d(a,b){if(1===arguments.length)return k(a);var c=k(a);return m.css(c,b,a),a}function e(a){return function(b,c,d){2===arguments.length&&'function'===typeof c?(d=c,c=null,m.use(b,a,d)):m.use(b,a,function(a){a=m.css(a,c,b),d&&d(a)})}}function f(a){var b=a.indexOf?a.indexOf(v):0;if(0>=b)return'';var c=a.substr(0,b);return u[c]?u[c]+a.substr(b+1):''}function g(a){for(var b,c=a.length,d=0;d{m(a,[],()=>{console.error(`load module: ${a} failed.`)})};var d=f(h),e=d||t+h,g=[];if(p&&g.push(p),y&&(g.push('m'),g.push('ref='+b)),i){const a=n(i);Array.prototype.push.apply(g,Object.keys(a).map((b)=>`${b}=${a[b]}`))}c.src=g.length?e+'?'+g.join('&'):e,H.appendChild(c),F[a]={exports:{},loaded:!1,fired:!1},A[h]=a,G[a]=!0}}),h()):void J.push([a,b])}function j(a){for(const b of B)if(a&&b.match.test(a))return b.module;return!1}function k(b){var c=j(b)||F[b];if(!c){const a=new Error('Cube Error: Cannot find module \''+b+'\'');if(x)throw a;else return s.error(a),{}}if(!c.fired)if(c.fired=!0,x)c.exports=c.fn.apply(a,[c,c.exports,d,e(b),C,D]);else try{c.exports=c.fn.apply(a,[c,c.exports,d,e(b),C,D])}catch(a){s.error(a),c.exports={}}return o(c.exports)?c.exports.default:c.exports}function l(){y&&console.time('cube exec');for(let[a,b]of z)a.length&&a.forEach(function(c){if(G[c])return;var d=0;const e=k(c);b.length&&b.forEach(function(a){var b=a(e);b&&d++}),b.length===d&&z.delete(a)});y&&console.timeEnd('cube exec')}function m(a,b,c){const d=a+'';a=A[a]||a;var e=F[a];e||(e=F[a]={exports:{},fired:!1}),e.loaded=!0,e.fn=c,A[d]&&delete A[d],G[a]?(delete G[a],i(b,a)):b.length&&i(b,a)}function n(a){let b=a.split('&'),c={};return b.forEach((a)=>{let b=a.split('=');c[b[0]]=b[1]}),c}function o(a){return E&&a&&'object'===typeof a&&a.__esModule}var p,q=window,r=document,s=console,t='',u={},v=':',w='utf-8',x=!0,y=!0,z=new Map,A={},B=[],C=Object.assign(a.process||{},{env:{NODE_ENV:'production'}}),D=void 0,E=!1,F={},G={},H=r.querySelector('head'),I=!1,J=[];y&&console.time('cube load'),m.setRemoteBase=function(a){Object.assign(u,a)},m.toString=function(){return'Cube:v5.0.0-beta.28'},m.init=function(a){if(a.base&&'/'!==a.base&&(t=a.base.replace(/\/$/,'')),a.remoteBase)for(var b in a.remoteBase)a.remoteBase.hasOwnProperty(b)&&(u[b]=a.remoteBase[b].replace(/\/$/,''));for(a.charset&&(w=a.charset),a.version&&(p=a.version),void 0!==a.debug&&(y=a.debug),void 0!==a.strict&&(x=a.strict),a.env&&(C.env.NODE_ENV=a.env),a.global&&(D=a.global),void 0!==a.esModule&&(E=a.esModule),I=!0;J.length;){var c=J.shift();i(c[0],c[1])}return this},m.use=function(b,d,e,f){if(!b)throw new Error('Cube.use(moduleName) moduleName is undefined!');return'function'===typeof d&&(f=e,e=d,d=void 0),d||(d='Cube.use'),e=e||c,'string'===typeof b&&(b=[b]),f||(b=g(b)),z.has(b)||z.set(b,[]),z.get(b).push(function(){var c=[],d=b.length,f=!1;return function(b){if(!f)return(c.push(b),c.length===d)?(f=!0,e.apply(a,c),!0):void 0}}()),i(b,d),this},m.register=function(a,b,{matchType:d='version'}={}){return F[a]?s.warn('Cube Warning: Module \''+a+'\' already registered'):(F[a]={exports:b,fn:c,loaded:!0,fired:!0},'module'===d&&B.push({require:a,matchType:d,match:new RegExp(`^datav:\/npm\/${a}\/([^\/]+)?$`),module:F[a]}),this)};var K=/([^};]+)(\{[^}]+\})/g,L={};m.css=function(a,b,c){if(a){var d=c+'@'+b;if(!L[d]){L[d]=!0,b&&(a=a.replace(K,function(a,c,d){var e=c.split(',').map(function(a){return b+' '+a.trim()});return e.join(',')+d}));var e=r.createElement('style');return e.setAttribute('type','text/css'),e.setAttribute('mod',c),b&&e.setAttribute('ns',b),H.appendChild(e),e.innerHTML=a,a}}},m.debug=function(){q.localStorage&&q.addEventListener?(localStorage.cube='debug',location.reload()):s.error('Cube Error: Cannot debug, your browser does not support localStorage or addEventListener')},m.cache=function(){var a,b,c={},d={};for(a in F)F.hasOwnProperty(a)&&(b=F[a],b.loaded||(c[a]=b),b.fired||(d[a]=b));s.info('modules:',F),s.info('unloaded:',c),s.info('unfired:',d)},q.localStorage&&'debug'===localStorage.cube&&(y=!0,q.addEventListener('load',m.cache)),b=b||'Cube',a[b]?s.error('Cube Error: window.'+b+' already in using, replace the last "null" param in cube.js'):a[b]=m;var M=r.currentScript;if(M){var N=M.dataset;N.base&&(m.init(N),m.use(N.main||'index.js',function(a){a.run&&a.run()}))}})(window,null); \ No newline at end of file diff --git a/service.js b/service.js index ac505e4..9e847ea 100644 --- a/service.js +++ b/service.js @@ -9,6 +9,7 @@ const path = require('path'); const connect = require('connect'); const connectStatic = require('serve-static'); const async = require('async'); +const cors = require('cors'); function createMiddleware(cube, serveStatic, checkSkip) { let config = cube.config; @@ -199,7 +200,7 @@ function createMiddleware(cube, serveStatic, checkSkip) { * * @param {Cube} cube instance */ -exports.init = function (cube) { +exports.init = function (cube, servOpt = {}) { let config = cube.config; let root = config.root; let serveStatic; @@ -232,7 +233,8 @@ exports.init = function (cube) { * fallback the 404 request */ serveStatic = connectStatic(config.cached ? config.cached : config.root, { - maxAge: config.maxAge + maxAge: config.maxAge, + ...(servOpt.connect || {}), }); /** @@ -247,6 +249,7 @@ exports.init = function (cube) { }; } else { app = connect(); + app.use(cors(servOpt.cors || {})); app.use(config.router, config.static || config.cached ? serveStatic : cubeMiddleware); app.use(function(err, req, res, next) { if (/favicon\.ico$/.test(req.url)) { diff --git a/test/index.js b/test/index.js index 88d7180..e76bf3e 100644 --- a/test/index.js +++ b/test/index.js @@ -250,7 +250,7 @@ describe('index.js', function () { request.get('/test/test_error.js?m') .expect(200) .expect(function (res) { - expect(res.text).match(/Unexpected token/ig); + expect(res.text).match(/Missing semicolon/ig); }) .end(done); }); diff --git a/tools.js b/tools.js index fdca595..93c2fcc 100644 --- a/tools.js +++ b/tools.js @@ -274,9 +274,13 @@ function processDirSmart(cube, data, cb) { }); async.waterfall(actions, function (err) { + // xfs.writeFileSync( + // path.join(dest, 'cube.js'), + // xfs.readFileSync(path.join(__dirname, './runtime/cube.min.js')) + // ); xfs.writeFileSync( path.join(dest, 'cube.js'), - xfs.readFileSync(path.join(__dirname, './runtime/cube.min.js')) + xfs.readFileSync(path.join(__dirname, './runtime/cube-reconstruction.min.js')) ); xfs.writeFileSync( path.join(dest, 'cube_file_map.json'),