From b68d2385a563ffd4fd025c127d9b1e8b458ef5dc Mon Sep 17 00:00:00 2001 From: wkj <278599135@qq.com> Date: Sat, 28 May 2022 16:57:22 +0800 Subject: [PATCH] =?UTF-8?q?xml=20=E7=BC=96=E8=AF=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/env.js | 2 + config/loaders/locales-loader.js | 101 ++++++++++++++++++ config/loaders/wxml-loader.js | 53 ++++++++- ...webpack.config.js => webpack.config.mp.js} | 30 +++--- lib/build.js | 2 +- package.json | 3 + plugins/WechatMpPlugin.js | 1 - scripts/webpack.js | 7 +- src/build.ts | 4 +- 9 files changed, 185 insertions(+), 18 deletions(-) create mode 100644 config/loaders/locales-loader.js rename config/{webpack.config.js => webpack.config.mp.js} (92%) diff --git a/config/env.js b/config/env.js index 1317b1d..cc876bd 100644 --- a/config/env.js +++ b/config/env.js @@ -2,6 +2,8 @@ const path = require('path'); /** 环境变量 */ exports.NODE_ENV = process.env.NODE_ENV || 'development'; +/** 环境变量 */ +exports.TARGET = process.env.TARGET || 'mp'; /** 项目路径 */ exports.ROOT = path.join(process.cwd(), 'wechatMp'); /** 源代码存放路径 */ diff --git a/config/loaders/locales-loader.js b/config/loaders/locales-loader.js new file mode 100644 index 0000000..33dcc9e --- /dev/null +++ b/config/loaders/locales-loader.js @@ -0,0 +1,101 @@ + +const { + ROOT, + SOURCE, + DESTINATION, + NODE_ENV, +} = require('./../env'); +const fs = require('fs'); +const path = require('path'); +const globby = require('globby'); +const parse = require('format-message-parse'); +const { merge, set, get } = require('lodash'); + +const DEFAULT_WXS_FILENAME = 'locales.wxs'; +const DEFAULT_JS_FILENAME = 'locales.js'; +const READ_LOCALES_DIR = SOURCE + '/locales'; +const WRITE_DIR = SOURCE + '/i18n'; + +const DEFAULT_LOCALE = 'zh-CN'; +const DEFAULT_FALLBACK_LOCALE = 'zh-CN'; +const CORE_PATH = path.dirname( + require.resolve( + '../../node_modules/oak-frontend-base/src/platforms/wechatMp/i18n/wxs/wxs.js' + ) +); +function getWxsCode() { + const code = fs.readFileSync(path.join(CORE_PATH, '/wxs.js'), 'utf-8'); + const runner = `module.exports = { \nt: Interpreter.getMessageInterpreter() \n}`; + return [code, runner].join('\n'); +} + +function i18nLocalesLoader() { + const defaultLocale = DEFAULT_LOCALE; + const fallbackLocale = DEFAULT_FALLBACK_LOCALE; + const wxsContent = `${getWxsCode()}`; + const translations = getTranslations(READ_LOCALES_DIR); + const translationsStr = JSON.stringify(translations, null, 2); + + const jsContent = `module.exports = { \nfallbackLocale: '${fallbackLocale}', \ndefaultLocale:'${defaultLocale}', \ntranslations: ${translationsStr} \n};`; + + if (!checkFileExists(WRITE_DIR)) { + fs.mkdirSync(WRITE_DIR); + } + fs.writeFileSync(WRITE_DIR + '/' + DEFAULT_JS_FILENAME, jsContent); + fs.writeFileSync(WRITE_DIR + '/' + DEFAULT_WXS_FILENAME, wxsContent); +} + + + +function getTranslations(source) { + const paths = globby.sync([source]); + const translations = {}; + + if (paths && paths.length) { + for (const p of paths) { + // zh_CN/house.json + const d = path2Escape(p).replace(path2Escape(source), ''); + const arr = d.split('/'); + const localeName = arr[1]; + const fileName = arr[2]; + const entityName = fileName.substring(0, fileName.lastIndexOf('.')); + const json = readJsonSync(p); + set( + translations, + `${localeName}.${entityName}`, + parseTranslations(json) + ); + } + } + + return translations; +} + +function path2Escape(str) { + return str.replace(/\\/g, '/'); +} + +function readJsonSync(path) { + const content = fs.readFileSync(path).toString('utf-8'); + return JSON.parse(content); +} + +function checkFileExists(path) { + return fs.existsSync(path); +} + +function parseTranslations(object) { + const keys = Object.keys(object); + for (const key of keys) { + const val = object[key]; + if (typeof val === 'string') { + object[key] = parse(val); + } + if (typeof val === 'object') { + object[key] = parseTranslations(val); + } + } + return object; +} + +module.exports = i18nLocalesLoader; \ No newline at end of file diff --git a/config/loaders/wxml-loader.js b/config/loaders/wxml-loader.js index 2be2147..a6702dd 100644 --- a/config/loaders/wxml-loader.js +++ b/config/loaders/wxml-loader.js @@ -3,7 +3,7 @@ * @param {*} content 文件信息 */ const { DOMParser, XMLSerializer } = require('@xmldom/xmldom'); -const { resolve } = require('path'); +const { resolve, relative } = require('path'); const { isUrlRequest, urlToRequest } = require('loader-utils'); const BOOLEAN_ATTRS = [ @@ -76,11 +76,38 @@ const isSrc = (name) => name === 'src'; const isDynamicSrc = (src) => /\{\{/.test(src); +const TranslationFunction = 't'; + +const I18nModuleName = 'i18n'; + +const CURRENT_LOCALE_KEY = '$_locale'; +const LOCALE_CHANGE_HANDLER_NAME = '$_localeChange'; +const COMMON_LOCALE_DATA = '$_common_translations'; +const CURRENT_LOCALE_DATA = '$_translations'; +const WXS_PATH = 'i18n/locales.wxs'; + +function existsT(str) { + if (!str) return false; + // if (str.indexOf('t(') === -1) return false; + // console.log(str.substring(str.indexOf('t(') - 1, str.indexOf('t('))); + return ( + str.indexOf('t(') !== -1 && + !/^[A-Za-z0-9]*$/.test( + str.substring(str.indexOf('t(') - 1, str.indexOf('t(')) + ) + ); +} + +function replaceT(str) { + return str.replace(/t\(/g, 'i18n.t('); +} + module.exports = async function (content) { // loader的缓存功能 // this.cacheable && this.cacheable(); const options = this.getOptions() || {}; //获取配置参数 + const { context: context2 } = options; const callback = this.async(); const { options: webpackLegacyOptions, _module = {}, _compilation = {}, resourcePath } = this; const { context, target } = webpackLegacyOptions || this; @@ -89,6 +116,10 @@ module.exports = async function (content) { const issuerContext = (issuer && issuer.context) || context; const root = resolve(context, issuerContext); let source = content; + if (existsT(source)) { + const relativePath = relative(context, context2 + '/' + WXS_PATH); + source = `` + source; + } if (/pages/.test(context)) { source = source + @@ -142,6 +173,26 @@ module.exports = async function (content) { node.setAttribute('focus', `{{!!oakFocused.${oakValue}}}`); } } + + } + if (node.nodeType === node.TEXT_NODE) { + // 处理i18n 把t()转成i18n.t() + if (existsT(node.nodeValue)) { + const val = replaceT(node.nodeValue); + const valArr = val.split('}}'); + let newVal = ''; + valArr.forEach((ele, index) => { + if (existsT(ele)) { + const head = ele.substring(0, ele.indexOf(')')); + const end = ele.substring(ele.indexOf(')')); + newVal += head + `,${CURRENT_LOCALE_KEY},${CURRENT_LOCALE_DATA} || ''` + end + '}}'; + } else if (ele) { + newVal += ele + '}}'; + } + }) + node.deleteData(0, node.nodeValue.length); + node.insertData(0, newVal); + } } }); diff --git a/config/webpack.config.js b/config/webpack.config.mp.js similarity index 92% rename from config/webpack.config.js rename to config/webpack.config.mp.js index aa82172..a86b5d6 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.mp.js @@ -19,11 +19,15 @@ const { PLATFORM_CONFIG, ENV_CONFIG, } = require('./env'); +// const localesLoader = require('../config/loaders/locales-loader'); + const isDev = NODE_ENV === 'development'; const pkg = require(`${process.cwd()}/package.json`) // process.env.OAK_PLATFORM: wechatMp | wechatPublic | web | node +// localesLoader(); + const relativeFileLoader = (ext = '[ext]') => { return { loader: 'file-loader', @@ -62,6 +66,7 @@ const copyPatterns = [] } : pattern ); +const oakReg = /oak-general-business\/wechatMp|oak-general-business\\wechatMp/; module.exports = { context: SOURCE, @@ -115,14 +120,13 @@ module.exports = { test: /\.wxs$/, include: /src/, type: 'javascript/auto', - use: [relativeFileLoader(), 'babel-loader'], + use: [relativeFileLoader()], }, { test: /\.wxs$/, - include: - /oak-general-business\/wechatMp|oak-general-business\\wechatMp/, + include: oakReg, type: 'javascript/auto', - use: [relativeFileLoader(), 'babel-loader'], + use: [relativeFileLoader()], }, { test: /\.(png|jpg|gif|svg)$/, @@ -132,8 +136,7 @@ module.exports = { }, { test: /\.(png|jpg|gif|svg)$/, - include: - /oak-general-business\/wechatMp|oak-general-business\\wechatMp/, + include: oakReg, type: 'javascript/auto', use: oakFileLoader(), }, @@ -150,8 +153,7 @@ module.exports = { }, { test: /\.less$/, - include: - /oak-general-business\/wechatMp|oak-general-business\\wechatMp/, + include: oakReg, type: 'javascript/auto', use: [ oakFileLoader('wxss'), @@ -202,19 +204,23 @@ module.exports = { relativeFileLoader('wxml'), { loader: 'wxml-loader', + options: { + context: SOURCE, + }, }, ], }, { test: /\.(xml|wxml)$/, - include: - /oak-general-business\/wechatMp|oak-general-business\\wechatMp/, + include: oakReg, type: 'javascript/auto', use: [ oakFileLoader('wxml'), { loader: 'wxml-loader', - options: {}, + options: { + context: SOURCE, + }, }, ], }, @@ -226,7 +232,7 @@ module.exports = { new OakWeChatMpPlugin({ exclude: ['*/weui-miniprogram/*'], include: ['project.config.json', 'sitemap.json'], - split: !isDev, + split: true, }), new webpack.DefinePlugin({ __DEV__: isDev, diff --git a/lib/build.js b/lib/build.js index 5d286b5..1f18f8c 100644 --- a/lib/build.js +++ b/lib/build.js @@ -7,7 +7,7 @@ const tip_style_1 = require("./tip-style"); const cross_spawn_1 = __importDefault(require("cross-spawn")); async function build(cmd) { (0, tip_style_1.Success)(`${(0, tip_style_1.success)(`build ${cmd.target} environment: ${cmd.mode}`)}`); - const result = cross_spawn_1.default.sync(`cross-env NODE_ENV=${cmd.mode} "${process.execPath}"`, [require.resolve('../scripts/' + 'webpack.js')], { + const result = cross_spawn_1.default.sync(`cross-env NODE_ENV=${cmd.mode} NODE_TARGET=${cmd.target} "${process.execPath}"`, [require.resolve('../scripts/webpack.js')], { stdio: 'inherit', shell: true, }); diff --git a/package.json b/package.json index 31b6381..ce041d8 100644 --- a/package.json +++ b/package.json @@ -38,11 +38,14 @@ "crypto-browserify": "^3.12.0", "dotenv-webpack": "^7.1.0", "ensure-posix-path": "^1.1.1", + "format-message-parse": "^6.2.4", "fs-extra": "^10.0.0", "globby": "^11.1.0", "inquirer": "^7.3.3", "loader-utils": "^3.2.0", + "lodash": "^4.17.21", "mini-css-extract-plugin": "^2.5.3", + "oak-frontend-base": "file:../oak-frontend-base", "postcss-less": "^6.0.0", "progress-bar-webpack-plugin": "^2.1.0", "required-path": "^1.0.1", diff --git a/plugins/WechatMpPlugin.js b/plugins/WechatMpPlugin.js index 3b24cd9..0516a57 100644 --- a/plugins/WechatMpPlugin.js +++ b/plugins/WechatMpPlugin.js @@ -1,7 +1,6 @@ /** * 把目录下所有的.ts和.less文件加入entry */ -const fs = require('fs'); const path = require('path'); const fsExtra = require('fs-extra'); const globby = require('globby'); diff --git a/scripts/webpack.js b/scripts/webpack.js index e596561..030f874 100644 --- a/scripts/webpack.js +++ b/scripts/webpack.js @@ -1,7 +1,12 @@ const webpack = require('webpack'); const chalk = require('chalk'); +const { TARGET, NODE_ENV } = require('../config/env'); + +let webpackConfig; +if (TARGET === 'mp') { + webpackConfig = require('../config/webpack.config.mp'); +} -const webpackConfig = require('../config/webpack.config'); webpack(webpackConfig, (err, stats) => { if (err) { diff --git a/src/build.ts b/src/build.ts index e2ce4f0..002b00c 100644 --- a/src/build.ts +++ b/src/build.ts @@ -12,8 +12,8 @@ import spawn from 'cross-spawn'; export default async function build(cmd: any) { Success(`${success(`build ${cmd.target} environment: ${cmd.mode}`)}`); const result = spawn.sync( - `cross-env NODE_ENV=${cmd.mode} "${process.execPath}"`, - [require.resolve('../scripts/' + 'webpack.js')], + `cross-env NODE_ENV=${cmd.mode} NODE_TARGET=${cmd.target} "${process.execPath}"`, + [require.resolve('../scripts/webpack.js')], { stdio: 'inherit', shell: true,