UNPKG

silki

Version:

Cli tool for build react app, based on generator-react-multipage, create-react-app, roadhog. Support react multiple pages app develop.

331 lines (317 loc) 11.8 kB
const webpack = require("@ali/webpackcc/lib/build"); const nativeWebpack = require("webpack"); const reactDocs = require("react-docgen"); const path = require("path"); const fs = require("fs"); const transform = require("./transformer"); const transformHOC = require("./transform_hoc"); const resolve = require("resolve"); const async = require("async"); const ROOT = process.cwd(); const generator = require("babel-generator").default; const BASIC_PATH_PREFIX = path.join(ROOT, "./src"); // 组件的位置 const components = "./src/components"; const DEFAULT_WEBPACK_MODULES_PATH = path.join(ROOT, "./node_modules"); const indexTestJS = path.join(process.cwd(), "./src/index.test.js"); const EXPORTS_FILE_PATH = path.join(process.cwd(), "./src/index.js"); const DEFAULT_FOLDER_PATH = path.join(process.cwd(), "./src"); const indexTransformer = require("./transform_index"); // "./node_modules" const program = { cwd: ROOT, dev: true }; /** * 得到完整的文件路径 */ function getFullPath(array) { const fullPath = []; for (let i = 0, len = array.length; i < len; i++) { if (/^(\.|components)\S*/.test(array[i])) { if (/^\.\S*/) { // 相对地址,是相对index.test.js的,即src目录 fullPath.push(path.join(BASIC_PATH_PREFIX, array[i])); } else { //components这种 fullPath.push(path.join(components, array[i])); } } else { fullPath.push(array[i]); } } return fullPath; } /** * 打包具体的文件"./src/test.js" * nodejs中fs.readFile这些方法返回的对象都是什么? * [{"source":"./lib/AutoSeekCtr/index.js","name":"AutoSeekCtr"},{"source":"./lib/MaterialUps/index.js","name":"MaterialUps"},{"source":"./lib/VideoPreview/index.js","name":"VideoPreview"},{"source":"./lib/YouKuH5Player/index.js","name":"KuVideo"}] * 导出的对象为====["AutoSeekCtr","MaterialUps","VideoPreview","KuVideo"] */ function packJsBundle(jsFile, callback) { // 这里是处理index.test.js内容,我关心的是里面的import {A,B,C} from "c";未导入的组件我并不关系 fs.readFile(jsFile, "utf8", (err, data) => { const id0imports = transform(data); const localIds = JSON.parse(JSON.stringify(id0imports)); delete localIds.inputAst; // console.log("组件名称++++为===" + JSON.stringify(localIds)); const componentName = id0imports.componentName; const relativeExports = id0imports.relativeExports; let fullPath = getFullPath(id0imports.imports); const antdImports = id0imports.antdExports; const inputAst = id0imports.inputAst; const sourceCode = generator(inputAst, null, data).code; // 已经知道每一个文件的完整路径,我们需要构建webpack.config.js来完成构建打包到window上,借助async来顺序完成 const rules = [], outputFiles = [], entry = { main: require.resolve(indexTestJS) }; for (let i = 0, len = fullPath.length; i < len; i++) { let specifier = id0imports.specifiers[i]; if (/^\//.test(fullPath[i])) { // 非npm包,相对路径直接expose出去并输出为一个单独文件到output目录 // entry[`${specifier}`] = fullPath[i]; // 不需要单独输出一个文件,直接expose-loader出去 outputFiles.push(specifier); rules.push({ // 在低版本情况下已经是绝对路径,require.resolve即使忽略第二个参数也无所谓 test: require.resolve(fullPath[i], { basedir: DEFAULT_WEBPACK_MODULES_PATH }), use: [ { loader: require.resolve("expose-loader"), options: specifier } ] }); } else { // 此时是npm包,expose出去不需要添加到entry中 // 对于antd这种import {A,B} from "xxx";要暴露到window上的临时做法 if (fullPath[i] == "antd") { specifier = "antd"; } //第三方类库,查看es目录+require.resolve出来的目录 const resolvedLoc = resolve.sync(fullPath[i], { basedir: DEFAULT_WEBPACK_MODULES_PATH }); // console.log("resolvedLoc=====>" + resolvedLoc); const esDirLoc = resolvedLoc.split("lib").join("es"); // console.log("esDirLoc=====>" + esDirLoc); if (fs.existsSync(esDirLoc) && esDirLoc !== resolvedLoc) { rules.push({ test: require.resolve(esDirLoc), use: [ { loader: require.resolve("expose-loader"), options: specifier } ] }); } rules.push({ test: require.resolve( resolve.sync(fullPath[i], { basedir: DEFAULT_WEBPACK_MODULES_PATH }) ), use: [ { loader: require.resolve("expose-loader"), options: specifier } ] }); } } // console.log("rules=====>" + JSON.stringify(rules)); //通过相对路径引入的,比如"./UserSelect""将会被挂载到window.UserSelect中 for (let i = 0, len = relativeExports.length; i < len; i++) { const { key, imports, source } = relativeExports[i]; // "./2exports.js";直接挂载到window的一个随机数上~~~~ rules.push({ test: require.resolve(path.join(BASIC_PATH_PREFIX, source)), use: [ { loader: require.resolve("expose-loader"), options: key } ] }); } fs.readFile(path.join(process.cwd(), "./.silkrc"), (err, data) => { if (err) { console.log(".silkrc读取异常,进程将退出....."); process.exit(0); } let useBabelrc = false; let disableCSSModules = false; let silkrcJSON = {}; try { silkrcJSON = JSON.parse(data); } catch (error) { console.log(".silkrc解析为JSON失败,程序退出..."); process.exit(0); } try { disableCSSModules = JSON.parse(data).disableCSSModules; } catch (error) { console.log(".silkrc文件不符合JSON...disableCSSModules默认设置为false"); } try { useBabelrc = JSON.parse(data).useBabelrc; } catch (error) { console.log(".silkrc文件不符合JSON...useBabelrc默认设置为false"); } // 目前只考虑antd这种情况,后续要优化第三方类库 const requirePlusLoaders = { antd: "antd" }; const concatPlugin = {}; // "antdExports":["Button","Alert"] for (let t = 0, len = antdImports.length; t < len; t++) { concatPlugin[`${antdImports[t]}`] = `window.antd.${antdImports[t]}`; } const webpackConfig = { entry, disableCSSModules: disableCSSModules, useBabelrc: useBabelrc, // 是否使用userBabelrc文件 resolve: { modules: [DEFAULT_WEBPACK_MODULES_PATH, "node_modules"] }, enableRequirePlusLoader: true, // 启动该Loader添加require命令 requirePlusLoaders, concatPlugin, // concatPlugin添加的内容 fromSilki: true, // 来源于siki output: { path: path.join(ROOT, "./build") }, node: { child_process: "empty", cluster: "empty", dgram: "empty", dns: "empty", fs: "empty", module: "empty", net: "empty", readline: "empty", repl: "empty", tls: "empty", "aws-sdk": "empty", console: false, url: false }, resolve: { extensions: [ ".web.js", ".web.jsx", ".web.ts", ".web.tsx", ".js", ".json", ".jsx", ".ts", "tsx" ], alias: { components: `${BASIC_PATH_PREFIX}/components/`, containers: `${BASIC_PATH_PREFIX}/containers/`, utils: `${BASIC_PATH_PREFIX}/utils/`, mods: `${BASIC_PATH_PREFIX}/mods/`, images: `${BASIC_PATH_PREFIX}/images/`, "react/lib/ReactMount": "react-dom/lib/ReactMount" } }, module: { rules }, plugins: [ new nativeWebpack.DefinePlugin({ __DEVELOPMENT__: false, __CLIENT__: true, __SERVER__: false, __DEVELOPMENT__: false, __DEVTOOLS__: false }) ] }; // extraBabelPlugins和extraBabelPresets只有在useBabelrc为false的情况下使用 if (!silkrcJSON.useBabelrc) { webpackConfig.extraBabelPlugins = silkrcJSON.extraBabelPlugins; webpackConfig.extraBabelPresets = silkrcJSON.extraBabelPresets; } program.config = webpackConfig; // console.log("webpack打包的配置为:" + JSON.stringify(webpackConfig)); // 下面是准备对index.test.js中的所有模块进行打包,打包之前,我先解析index.js,得到打包后应该生成的table内容 // 开始解析index.js,此处可以使用async,await fs.readFile(EXPORTS_FILE_PATH, "utf8", (err, data) => { if (err) { console.log("文件下不存在index.js,程序将会退出..."); process.exit(0); } const { imports, exportNames } = indexTransformer(data); // exportNames表示导出的对象名称 const indexExposeModules = imports.map((item, index) => { return path.join(DEFAULT_FOLDER_PATH, item.source); }); const functionPools = [], componentInfoArray = []; // 产生函数数组 for (let i = 0, len = indexExposeModules.length; i < len; i++) { const localFunc = function(callback) { return fs.readFile(indexExposeModules[i], "utf8", (err, data) => { if (err) { console.log(`读取文件${indexExposeModules[i]}失败!`); } callback(null, data); }); }; functionPools.push(localFunc); } // 得到所有的index.js中exports的组件的文档信息,然后开始开始打包index.test.js将所有的exports组件信息导出用于生成文档 async.parallel(functionPools, (err, results) => { if (err) { console.log(`并发读取文件失败,程序将会退出!`); } // 这里是result类型 for (let t = 0, len = results.length; t < len; t++) { const ast = transformHOC(results[t]).inputAst; const sourceCode = generator(ast, null, results[t]).code; const documentation = reactDocs.parse(sourceCode); componentInfoArray.push({ specifier: exportNames[t], documentation }); } // fs.readFile('/Users/qinliang.ql/Desktop/search-form/src/index.js',(err,data)=>{ // console.log('data=='+data); // }); // console.log("打包的最后配置为====" + JSON.stringify(program.config)); webpack(program, (err, stats) => { if (!err) { console.log("打包demo资源成功....."); // 回调:打包输出文件+div#id+源码+specifiers callback( outputFiles, id0imports.id, sourceCode, id0imports.specifiers, antdImports, relativeExports, componentName, componentInfoArray ); } }); }); // 准备生成表格内容 }); }); }); } module.exports = packJsBundle;