UNPKG

@plugin-light/project-config-pixui

Version:
346 lines (336 loc) 13.9 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var path = require('path'); var fs = require('fs'); var cleanWebpackPlugin = require('clean-webpack-plugin'); var ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin'); var HtmlWebpackPlugin = require('html-webpack-plugin'); var webpack = require('webpack'); function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } var path__default = /*#__PURE__*/_interopDefaultLegacy(path); var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs); var ForkTsCheckerWebpackPlugin__default = /*#__PURE__*/_interopDefaultLegacy(ForkTsCheckerWebpackPlugin); var HtmlWebpackPlugin__default = /*#__PURE__*/_interopDefaultLegacy(HtmlWebpackPlugin); var webpack__default = /*#__PURE__*/_interopDefaultLegacy(webpack); const DEFAULT_TSX_LOADER_EXCLUDE = /node_modules\/(?!@tencent\/pmd|@tencent\/press)/; const DEFAULT_PORT = 8080; /* eslint-disable @typescript-eslint/no-require-imports */ const pr$1 = path__default["default"].resolve; function readEnv() { const envPath = '.env.local'; if (!fs__default["default"].existsSync(envPath)) { return; } try { // eslint-disable-next-line @typescript-eslint/no-require-imports require('dotenv').config({ path: envPath }); } catch (e) { } } function getHtmlName(v) { switch (v.name) { case 'app': { return 'index.html'; // app不允许自定义 } case 'popup': { return 'index.html'; // app不允许自定义 } case 'preprocess': { return 'preprocess.html'; // preprocess不允许自定义 } default: { return v.output ? `${v.output}.html` : `${v.name}.html`; } } } function getAppDefAndSettings(appSettingDir) { let appDefs = require(pr$1(appSettingDir, 'apps.json')); if (fs__default["default"].existsSync(pr$1(appSettingDir, 'custom.json'))) { appDefs = [...appDefs, ...require(pr$1(appSettingDir, 'custom.json'))]; } const appSettings = require(pr$1(appSettingDir, 'appsettings.json')); return { appDefs, appSettings, }; } function getPlugins({ appDefs, appSettings, rootDir, isShipping, }) { const htmlCopyPlugins = appDefs.map((app) => (app.template ? new HtmlWebpackPlugin__default["default"]({ hash: isShipping, minify: isShipping, chunks: [app.name], template: app.template, filename: `${app.name}/${getHtmlName(app)}`, }) : false)); const plugins = [ new webpack__default["default"].DefinePlugin({ APP_SETTING: JSON.stringify(appSettings), 'process.env': JSON.stringify({ MOCK_LOGIN_PARAMS: process.env.MOCK_LOGIN_PARAMS || '', }), }), new cleanWebpackPlugin.CleanWebpackPlugin({ cleanOnceBeforeBuildPatterns: [ '**/*', path__default["default"].join(rootDir, '.build'), ], }), new ForkTsCheckerWebpackPlugin__default["default"]({ async: false }), ...htmlCopyPlugins, !isShipping ? new webpack__default["default"].HotModuleReplacementPlugin() : undefined, ].filter(v => !!v); return plugins; } function getRewrites(appDefs) { const rewrites = appDefs.map((v) => ({ from: new RegExp(`^/${v.name}/`), to: `/${v.name}` })); return rewrites; } const { rtCompilerToolPath } = process.env; function getLocalIP() { const os = require('os'); const ifaces = os.networkInterfaces(); const ipList = []; Object.keys(ifaces).forEach((ifname) => { // const alias = 0; ifaces[ifname].forEach((iface) => { if ('IPv4' !== iface.family || iface.internal !== false) { // skip over internal (i.e. 127.0.0.1) and non-ipv4 addresses return; } ipList.push(iface.address); }); }); return ipList; } const setupFilter = (appDef, _port) => function (app) { let log = (..._args) => { }; log = console.log; app.disable('etag'); app.get('/log0', (req, res) => { log = () => { }; res.send('ok'); }); app.get('/log1', (req, res) => { log = console.log; res.send('ok'); }); // 拦截输出,当agent为PixUI且输出类型为html时,转换为fbs二进制数据再返回 app.use((req, res, next) => { // 去掉缓存标记,因为很可能源文件没变,但转换后的表示变了 delete req.headers['if-none-match']; const ua = req.get('User-Agent'); log('req', req.url, ua, JSON.stringify(req.headers)); const _write = res.write; const _end = res.end; const _send = res.send; const buffers = []; const addBuffer = (chunk, encoding) => { if (chunk === undefined) return; if (typeof chunk === 'string') { chunk = Buffer.from(chunk, encoding); } buffers.push(chunk); }; // 处理直接裸写的情况,一般见于发送静态文件 res.write = function (chunk, encoding) { const type = res.get('Content-Type'); log('hook write...', type, chunk); addBuffer(chunk, encoding); }; res.end = function (chunk, encoding) { const type = res.get('Content-Type'); if (chunk) addBuffer(chunk, encoding); const data = Buffer.concat(buffers); log('hook end...', type, data.length); res.write = _write; res.end = _end; _send.call(this, data); }; // 处理通过send发送的情况,如webpack合成的html res.send = function (c) { const type = res.get('Content-Type') || 'text/plain'; const isPixUI = /PixUI/.test(ua); const ver = /(\d)\.(\d)\.(\d+)/.exec(ua); // 0.2.*以上 const isHTML = type.indexOf('text/html') >= 0; const isJS = type.indexOf('javascript') >= 0; // const isCSS = type.indexOf('text/css') >= 0; log('hook send...', c.length, type); if (isPixUI && parseInt(`${ver?.[2]}`, 10) >= 2 && (isHTML || isJS)) { const cp = require('child_process'); const opts = isJS ? ['--js'] : []; const child = cp.spawn(rtCompilerToolPath, ['--src', 'stdin', ...opts]); child.stdin.write(c); child.stdout.on('data', (data) => { // console.log('get data from fbs', req.url, data.byteLength); addBuffer(data); }); child.stdout.on('close', () => { // console.log('get data from fbs finish!!', req.url); res.end(); }); child.stderr.on('data', (data) => { console.log('get err from fbs', data.toString()); }); child.stdin.end(); } else { _send.call(this, c); } }; next(); }); // 定制首页显示所有entry,方便浏览 app.get('/', (req, res) => { res.setHeader('content-type', 'text/html'); res.send(`${appDef .concat([{ name: 'test' }]) .map(v => `<a href="./${v.name}" style='font-size:36px'>${v.name}</a>`) .join('<br>')}<div style='font-size:40pt' onclick='window.close();'>close</div>`); }); }; /* eslint-disable @typescript-eslint/no-require-imports */ readEnv(); const pr = path__default["default"].resolve; const isShipping = process.env.mode != 'development'; console.log({ isShipping }); function getPixuiWebpackConfig(options) { const { appSettingDir, } = options || {}; const { appDefs, appSettings, } = getAppDefAndSettings(appSettingDir); const innerTsxLoaderExclude = options?.tsxLoaderExclude ?? DEFAULT_TSX_LOADER_EXCLUDE; const innerPort = options?.port || process.env.port || DEFAULT_PORT; const useTailwind = options?.useTailwind ?? false; const rootDir = pr(appSettingDir, '..', 'dist'); const pandoraHtmlDir = pr(appSettingDir, '..'); const buildOutputPath = pr(rootDir, `html${isShipping ? '-pro' : '-dev'}`); const result = { entry: Object.fromEntries(appDefs.map(v => [v.name, v.entry])), output: { path: buildOutputPath, filename: '[name]/[name].js', publicPath: isShipping ? '../' // 发布后都从文件系统加载,使用相对路径引用素材 : '/', // `http://${getLocalIP()[0]}:${innerPort}/`, //开发时绑定到外部ip,方便手机访问 }, resolve: { modules: [`${pandoraHtmlDir}/src/node_modules`, pandoraHtmlDir, `${pandoraHtmlDir}/node_modules`], extensions: ['.js', '.jsx', '.ts', '.tsx'], alias: { ['preact$']: path__default["default"].resolve('./lib/preact/src'), ['preact/hooks$']: path__default["default"].resolve('./lib/preact/hooks/src'), // lib 中的 preact-router 不支持 history属性,改用 node_modules 中的 // ['preact-router$']: path.resolve('./lib/preact-router/src'), ['preact-router/match']: path__default["default"].resolve('./lib/preact-router/match/src'), ['react-window']: path__default["default"].resolve('./lib/react-window/src'), ['@improbable-eng/grpc-web']: path__default["default"].resolve('./lib/grpc-web/dist'), react: path__default["default"].resolve('./lib/preact/compat/src'), }, }, module: { rules: [ { test: /.(js|ts)x?$/, use: { loader: require.resolve('babel-loader'), options: { presets: [['@babel/preset-typescript', { allowNamespaces: true }]], plugins: [ '@babel/plugin-transform-flow-strip-types', '@babel/plugin-syntax-jsx', ['@babel/plugin-proposal-decorators', { legacy: true }], ['@babel/plugin-proposal-class-properties'], ['@babel/plugin-proposal-optional-chaining'], ['@babel/plugin-transform-react-jsx', { pragma: 'makeDOM' }], ['babel-plugin-jsx-pragmatic', { module: `${appSettingDir}/../lib/dom`, import: 'makeDOM' }], ], }, }, exclude: innerTsxLoaderExclude, }, { test: /\.css$/, use: [ require.resolve('style-loader'), { loader: require.resolve('css-loader'), options: useTailwind ? {} : { modules: true }, }, useTailwind ? require.resolve('postcss-loader') : '', ].filter(Boolean), }, { test: /\.less$/, use: [ 'style-loader', { loader: require.resolve('css-loader'), options: { modules: { localIdentName: '[name]_[local]__[hash:base64:6]', }, }, }, { loader: require.resolve('less-loader'), options: { javascriptEnabled: true }, }, ], }, { test: /\.s[ac]ss$/i, use: [ 'style-loader', { loader: require.resolve('css-loader'), options: { modules: true }, }, 'sass-loader', ], }, { test: /\.(png|jpg|gif|lottie|mp4)$/, loader: require.resolve('file-loader'), options: { name: 'static/media/[name].[hash:8].[ext]', }, }, ], }, optimization: { minimize: false, }, devtool: 'source-map', plugins: getPlugins({ appDefs, appSettings, rootDir, isShipping, }), devServer: { sockPath: `/${innerPort}/sockjs-node`, sockPort: innerPort, port: innerPort, host: '0.0.0.0', transportMode: 'ws', hot: true, liveReload: true, disableHostCheck: true, overlay: true, writeToDisk: true, contentBase: [`${appSettingDir}/../lib/assets`], watchContentBase: true, historyApiFallback: { rewrites: getRewrites(appDefs), }, headers: { 'Access-Control-Allow-Origin': '*', // 方便本机用localhost打开,难记ip }, before: setupFilter(appDefs), }, }; return result; } exports.getLocalIP = getLocalIP; exports.getPixuiWebpackConfig = getPixuiWebpackConfig; exports.setupFilter = setupFilter;