UNPKG

kef-builder-buffet

Version:

buffet-builder构建工具

380 lines (340 loc) 12.8 kB
'use strict'; const path = require('path'); const semver = require('semver'); const WrapperPlugin = require('./wraper-webpack-plugin'); const env = process.env; const CONST = require('../../utils/const'); function normalizeEntry(entry, preparedChunks) { const preparedName = preparedChunks.filter((module) => { return typeof module.name !== 'undefined'; }).map((module) => module.name); return Object.keys(entry).concat(preparedName); }; const branch = (/([^/]+)\/(\d+\.\d+\.\d+)/i.exec(env.BUILD_GIT_BRANCH) || [, , ''])[2]; const publicPath = env.BUILD_ENV === 'cloud' ? `/${env.BUILD_GIT_GROUP}/${env.BUILD_GIT_PROJECT}/${branch}/` : '/build/'; const getIceReadyCode = () => { let defaultDebugPath = 'null'; if (CONST.isSinglePageApp) { // spa /build/index.js defaultDebugPath = '/index.js'; } else { // 多页面 remove 没有 /build/pages 目录 defaultDebugPath = '/pages/home/index.js'; } // __asyncLoaded__ = false // ready 的方法在 __asyncLoaded__ = true 的情况下立即执行 return ` window.ICE = window.ICE || {}; // https: or http: var protocol = window.location.protocol; // 如果是 file:, about:, 等等其它的协议 if (!/^http/.test(protocol)) { protocol = 'http:'; } window.ICE.protocol = protocol || ''; // smart-loader var searchStr = window.location.search.replace(/^\\?/, ''); var searchSplited = searchStr.split('&'); var search = searchSplited.reduce(function(prev, current) { if (current) { var tuple = current.split('='); prev[tuple[0]] = tuple[1]; } return prev; }, {}); if (search.debug) { var originValue = '127.0.0.1'; // can not be override var originPort = search.debugPort || '3333'; var originPath = search.debugPath || ${JSON.stringify(defaultDebugPath)}; if (window.ICE.debug) { window.ICE.debug.origin = originValue; window.ICE.debug.port = originPort; window.ICE.debug.path = originPath; // 基础路径, 默认是 '/build/', 用于修改 __webpack_public_path__ window.ICE.debug.base = search.debugBase || null; } else { window.ICE.debug = { origin: originValue, port: originPort, path: originPath }; } } `; }; /** * 获取加载Scripts的代码 * @param {String} reactVersion reactVersion * @param {Boolean} notProduction 是否DEV模式 * @return {String} 加载REACT的function定义代码 */ const getLoadScriptsCode = (loadReact, reactVersion, options) => { const notProduction = process.env.NODE_ENV !== 'production'; const scripts = [ 'https://g.alicdn.com/code/icon/babel-polyfill/6.16.0/polyfill.min.js', ]; if (options.injectBabelPolyfill === false) { scripts.pop(); } // 大于16.0.0 用umd地址 const useUMDPath = semver.satisfies(reactVersion, '>=16.0.0'); let reactFileMin = `https://g.alicdn.com/code/npm/??react/${reactVersion}/dist/react-with-addons.min.js,react-dom/${reactVersion}/dist/react-dom.min.js`; let reactFileFull = `https://g.alicdn.com/code/npm/??react/${reactVersion}/dist/react-with-addons.js,react-dom/${reactVersion}/dist/react-dom.js`; if (useUMDPath) { reactFileMin = `https://g.alicdn.com/code/npm/??react/${reactVersion}/umd/react.production.min.js,react-dom/${reactVersion}/umd/react-dom.production.min.js`; reactFileFull = `https://g.alicdn.com/code/npm/??react/${reactVersion}/umd/react.development.js,react-dom/${reactVersion}/umd/react-dom.development.js`; } if (options && options.reactFile) { reactFileMin = reactFileFull = options.reactFile; if (options.reactFileMin) { reactFileMin = options.reactFileMin; } if (options.reactFileFull) { reactFileFull = options.reactFileFull; } } // dev 的情况下不使用 min 文件 // url 中含有 ice-debug 的情况不使用 min 文件 (runtime judge) const loadReactScript = ` var isUrlDev = window.location.href.match(/ice[_|-]debug/i); if ((${notProduction} || isUrlDev)) { scriptList.push( '${reactFileFull}' ); } else { scriptList.push( '${reactFileMin}' ); } `; // 自动加载 css 的 code let loadCSSCode = ''; if (options.smartLoader) { loadCSSCode = ` var didStyleLoaded = document.querySelector('link[data-ice-style-loaded]'); if (typeof iceScript !== 'undefined' && iceScript !== null) { var scriptSrc = iceScript.src; var scriptRawSrc = iceScript.getAttribute('src') || ''; var linkHref = scriptSrc.replace(/\\.js$/, '.css').replace(/(\\.js)(?=[?#])/i, '.css'); // 通过 src|href 路径匹配, 防止老项目重复加载 var linkWithSameHref = document.querySelector('link[href$="' + scriptRawSrc.replace(/\\.js$/, '.css').replace(/(\\.js)(?=[?#])/i, '.css') + '"]'); if (!didStyleLoaded && !linkWithSameHref) { cssList.push(linkHref); } } `; } return ` var scriptList = ${JSON.stringify(scripts)}; var cssList = []; var iceScript = document.getElementById('ice-script') || document.currentScript; ${loadCSSCode} ${loadReact ? loadReactScript : ''} var loadScripts = (function() { var timer = null; var headNode = document.head || document.querySelector('head'); function clearTimer() { if (timer) { clearTimeout(timer); timer = null; } } function loadCSS(url, done) { var link = document.createElement('link'); link.rel = 'stylesheet'; link.type = 'text/css'; link.href = url; link.onload = onload; link.setAttribute('data-ice-style-loaded', 'true'); var timer = setTimeout(function() { console.warn('加载 css 超时!'); done && done(new Error('timeout for 5s')); }, 5 * 1000); // 超时时间 5s function onload() { clearTimeout(timer); done && done(null, link); } headNode.appendChild(link); } function load(url, done) { var script = document.createElement('script'); script.type = 'text/javascript'; script.async = true; clearTimer(); timer = setTimeout(function() { clearTimer(); done('请求超时'); }, 60 * 1000); function onerror(evt) { script.onerror = null; script = null; clearTimer(); done('JS Can not load: ' + url); } function onload() { var readyState = script.readyState; if (!readyState || readyState === 'loaded' || readyState === 'complete') { script.onreadystatechange = script.onload = null; script = undefined; clearTimer(); done(null); } } if ('onload' in script) { script.onload = onload; script.onerror = onerror; } else { script.onreadystatechange = onload; } script.onreadystatechange = script.onload = onload; script.src = url; headNode.insertBefore(script, headNode.firstChild); } return function(scriptList, cssList, callback) { if (!headNode) { throw new Error('页面中必须存在 head 元素'); } // 兼容两个参数, cssList 为可选 if (typeof cssList === 'function') { callback = cssList; cssList = []; } var loadList = cssList.map(function(url) { return { type: 'css', url: url }; }).concat(scriptList.map(function(url) { return { type: 'js', url: url }; })); var counter = loadList.length; (function run(counter){ if (counter === 0) { return callback(); } var current = loadList.shift(); var loadFn = current.type === 'css' ? loadCSS : load; if (current.type === 'js' ){ // 已经存在 react 不加载 if ( window.React && window.React.version ) { if (window.React.version !== '${reactVersion}') { console.warn('当前页面已加载 React 版本 %s,可能会导致功能失效!推荐使用 ${reactVersion}', window.React.version); } return run(loadList.length); } else if ( // 已经存在 babel-polyfill 不加载 window._babelPolyfill && current.url.indexOf('babel-polyfill') !== -1 ) { return run(loadList.length); } } loadFn(current.url, function(err) { if (err) { console.error(err); return; } run(loadList.length); }); })(counter); }; })(); `; }; /** * [wrapBefore description] * @param {Boolean} loadReact 是否使用 wrap-react boolean * @param {String} reactVersion reactVersion * @param {Object} abcConfig abc.json * @return {String} wrap before code */ const wrapBefore = (loadReact, reactVersion, options) => { const iceReadyCode = getIceReadyCode(); const loadScriptsCode = getLoadScriptsCode(loadReact, reactVersion, options) || ''; return ` (function(){ /* ICE READY CODE START */ ${iceReadyCode} /* ICE READY CODE END */ /* ICE LOADSCRIPTS START */ ${loadScriptsCode} /* ICE LOADSCRIPTS END */ ${ options.smartLoader ? ` if (iceScript === null) { console.error('!您正在使用 ICE 智能调试服务, 但是页面上的 script 节点找不到 ice-script 的 id, 请检查!'); } else if (window.ICE.debug && window.ICE.debug.origin) { var scriptPath = (iceScript.src.replace(/^https?:\\/\\//, '').match(/(\\/.*)/) || ['', '/index.js'])[1]; window.ICE.debug.customJS = window.ICE.protocol + '//' + window.ICE.debug.origin + ':' + window.ICE.debug.port + (window.ICE.debug.path || scriptPath); cssList = [ window.ICE.debug.customJS .replace(/\\.js$/i, '.css') .replace(/(\\.js)(?=[?#])/i, '.css') ]; var linkHref = (iceScript.getAttribute('src') || '').replace(/\\.js$/i, '.css') .replace(/(\\.js)(?=[?#])/i, '.css'); var currentLink = document.querySelector('link[href$="' + linkHref + '"]'); if (currentLink) { // debug 下, 移除原先加载的 css currentLink.parentElement.removeChild(currentLink); } } if (iceScript !== null && window.ICE.debug && window.ICE.debug.customJS && !window.ICE.debug.skip) { window.ICE.debug.skip = true; loadScripts([window.ICE.debug.customJS], function() { console.warn('您正在使用 ICE 智能调试服务, 目前加载的 JS 和 CSS 路径为:' ); console.warn(window.ICE.debug.customJS + "\\n" + cssList[0]); }); return; } ` : '' } loadScripts(scriptList, cssList, function() { `; }; /** * [wrapAfter description] * @return {String} wrap after code */ const wrapAfter = () => { return ` /* ICE READY TRIGGER START */ /* ICE READY TRIGGER END */ }); // END loadScripts callback })(); // END ICE wrap `; }; /** * entry 打包后的代码 wraper 用于自动加载 react 、babel * * @param {Object} options abc.options */ module.exports = function (options) { const reactVersion = options.reactVersion; const loadReact = options.wrapReact === true; return new WrapperPlugin({ header(filename, compilerEntry, compilationPreparedChunks) { const entiesChunkName = normalizeEntry(compilerEntry, compilationPreparedChunks); // 是 js 文件,而且去掉扩展名后在 entry 的 key 里 const loaderFilename = /\.js$/.test(filename) && filename.replace(/\.\w+$/, ''); // output文件名保持不变 if (entiesChunkName.length && entiesChunkName.indexOf(loaderFilename) !== -1) { return wrapBefore(loadReact, reactVersion, options); } return ''; }, footer(filename, compilerEntry, compilationPreparedChunks) { const entiesChunkName = normalizeEntry(compilerEntry, compilationPreparedChunks); const loaderFilename = /\.js$/.test(filename) && filename.replace(/\.\w+$/, ''); if (entiesChunkName.length && entiesChunkName.indexOf(loaderFilename) !== -1) { return wrapAfter(loadReact); } return ''; } }); };