locales.wxs 动态写入dist

This commit is contained in:
Wang Kejun 2022-06-25 17:27:35 +08:00
parent 72f01718a5
commit 0b52dde7e3
9 changed files with 312 additions and 238 deletions

View File

@ -5,6 +5,8 @@
const { DOMParser, XMLSerializer } = require('@xmldom/xmldom');
const { resolve, relative } = require('path');
const { isUrlRequest, urlToRequest } = require('loader-utils');
const fs = require('fs');
const path = require('path');
const BOOLEAN_ATTRS = [
'wx:else',
@ -82,7 +84,9 @@ 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';
const DEFAULT_WXS_FILENAME = 'locales.wxs';
const WXS_PATH = 'i18n' + '/' +DEFAULT_WXS_FILENAME;
function existsT(str) {
if (!str) return false;
@ -94,10 +98,25 @@ function existsT(str) {
);
}
function replaceDoubleSlash(str) {
return str.replace(/\\/g, '/');
}
function replaceT(str) {
return str.replace(/t\(/g, 'i18n.t(');
}
function getWxsCode() {
const BASE_PATH = path.dirname(
require.resolve(
`${process.cwd()}/node_modules/oak-frontend-base/src/platforms/wechatMp/i18n/wxs/wxs.js`
)
);
const code = fs.readFileSync(path.join(BASE_PATH, '/wxs.js'), 'utf-8');
const runner = `module.exports = { \nt: Interpreter.getMessageInterpreter() \n}`;
return [code, runner].join('\n');
}
module.exports = async function (content) {
// loader的缓存功能
// this.cacheable && this.cacheable();
@ -109,21 +128,24 @@ module.exports = async function (content) {
options: webpackLegacyOptions,
_module = {},
_compilation = {},
_compiler = {},
resourcePath,
} = this;
const { output } = _compiler.options;
const { path: outputPath } = output;
const { context, target } = webpackLegacyOptions || this;
const issuer = _compilation.moduleGraph.getIssuer(this._module);
const issuerContext = (issuer && issuer.context) || context;
const root = resolve(context, issuerContext);
let source = content;
let relativePath; // locales.wxs相对路径
let wxsRelativePath; // locales.wxs相对路径
//判断是否存在i18n的t函数
if (existsT(source)) {
//判断加载的xml是否为本项目自身的文件
const isSelf = context.indexOf(projectContext) !== -1;
if (isSelf) {
//本项目xml
relativePath = relative(
wxsRelativePath = relative(
context,
projectContext + '/' + WXS_PATH
).replace(/\\/g, '/');
@ -132,7 +154,7 @@ module.exports = async function (content) {
const index = context.lastIndexOf(WeChatMpDir);
if (index !== -1) {
const p = context.substring(index + WeChatMpDir.length);
relativePath = relative(
wxsRelativePath = relative(
projectContext + p,
projectContext + '/' + WXS_PATH
).replace(/\\/g, '/');
@ -140,7 +162,7 @@ module.exports = async function (content) {
}
source =
`<wxs src='${relativePath}' module='${I18nModuleName}'></wxs>` +
`<wxs src='${wxsRelativePath}' module='${I18nModuleName}'></wxs>` +
source;
}
// 注入全局message组件
@ -174,10 +196,13 @@ module.exports = async function (content) {
!isDynamicSrc(value) &&
isUrlRequest(value, root)
) {
if (relativePath === value) {
// 如果出现直接读取项目下i18n/locales.wxs
const path = projectContext + '/' + WXS_PATH;
requests.push(path);
if (wxsRelativePath === value) {
// dist目录下生成一个i18n/locales.wxs文件
const path = resolve(outputPath, WXS_PATH);
if (!fs.existsSync(replaceDoubleSlash(path))) {
const wxsContent = `${getWxsCode()}`;
this.emitFile(WXS_PATH, wxsContent);
}
} else {
const path = resolve(root, value);
// const request = urlToRequest(value, root);

View File

@ -37,6 +37,7 @@ module.exports = {
dotenv: resolveApp('.env'),
appPath: resolveApp('.'),
appBuild: resolveApp(buildPath),
appIndexDevJs: resolveModule(resolveApp, 'src/app.dev'),
appIndexJs: resolveModule(resolveApp, 'src/app'),
appPackageJson: resolveRoot('package.json'),
appSrc: resolveApp('src'),
@ -48,6 +49,7 @@ module.exports = {
appTsBuildInfoFile: resolveRoot('node_modules/.cache/tsconfig.tsbuildinfo'),
publicUrlOrPath: '/',
appOutSrc: resolveRoot('src'),
oakConfigJson: resolveApp('src/oak.config.json'),
};

View File

@ -14,41 +14,10 @@ const getClientEnvironment = require('./env');
const paths = require('./paths');
const env = getClientEnvironment();
const isEnvDevelopment = env.raw.NODE_ENV === 'development';
const isEnvProduction = env.raw.NODE_ENV === 'production';
const pkg = require(paths.appPackageJson);
// process.env.OAK_PLATFORM: wechatMp | wechatPublic | web | node
const relativeFileLoader = (ext = '[ext]') => {
return {
loader: 'file-loader',
options: {
useRelativePath: true,
name: `[path][name].${ext}`,
context: paths.appSrc,
},
};
};
const oakFileLoader = (ext = '[ext]') => {
return {
loader: 'file-loader',
options: {
useRelativePath: true,
name: `[path][name].${ext}`,
outputPath: (url, resourcePath, context) => {
const outputPath = url.split(
'oak-general-business/wechatMp/'
)[1];
return outputPath;
},
context: paths.appSrc,
},
};
};
const copyPatterns = [].concat(pkg.copyWebpack || []).map((pattern) =>
typeof pattern === 'string'
? {
@ -57,203 +26,237 @@ const copyPatterns = [].concat(pkg.copyWebpack || []).map((pattern) =>
}
: pattern
);
const oakReg = /oak-general-business\/wechatMp|oak-general-business\\wechatMp/;
const oakRegex = /oak-general-business\/wechatMp|oak-general-business\\wechatMp/;
module.exports = {
context: paths.appSrc,
devtool: isEnvDevelopment ? 'source-map' : false,
mode: isEnvProduction ? 'production' : isEnvDevelopment && 'development',
target: 'web',
entry: {
app: paths.appIndexJs,
},
output: {
path: paths.appBuild,
filename: '[name].js',
publicPath: paths.publicUrlOrPath,
globalObject: 'global',
},
resolve: {
alias: {
'@': paths.appSrc,
assert: require.resolve('assert'),
module.exports = function (webpackEnv) {
const isEnvDevelopment = webpackEnv === 'development';
const isEnvProduction = webpackEnv === 'production';
const relativeFileLoader = (ext = '[ext]') => {
return {
loader: 'file-loader',
options: {
useRelativePath: true,
name: `[path][name].${ext}`,
context: paths.appSrc,
},
};
};
const oakFileLoader = (ext = '[ext]') => {
return {
loader: 'file-loader',
options: {
useRelativePath: true,
name: `[path][name].${ext}`,
outputPath: (url, resourcePath, context) => {
const outputPath = url.split(
'oak-general-business/wechatMp/'
)[1];
return outputPath;
},
context: paths.appSrc,
},
};
};
return {
context: paths.appSrc,
devtool: isEnvDevelopment ? 'source-map' : false,
mode: isEnvProduction
? 'production'
: isEnvDevelopment && 'development',
target: 'web',
entry: {
app: isEnvProduction ? paths.appIndexJs : paths.appIndexDevJs,
},
extensions: paths.moduleFileExtensions.map((ext) => `.${ext}`),
symlinks: true,
fallback: {
crypto: require.resolve('crypto-browserify'),
buffer: require.resolve('safe-buffer'),
stream: require.resolve('stream-browserify'),
output: {
path: paths.appBuild,
filename: '[name].js',
publicPath: paths.publicUrlOrPath,
globalObject: 'global',
},
},
resolveLoader: {
// 第一种使用别名的方式引入自定义的loader
alias: {
'wxml-loader': path.resolve(__dirname, '../loaders/wxml-loader.js'),
resolve: {
alias: {
'@': paths.appSrc,
assert: require.resolve('assert'),
},
extensions: paths.moduleFileExtensions.map((ext) => `.${ext}`),
symlinks: true,
fallback: {
crypto: require.resolve('crypto-browserify'),
buffer: require.resolve('safe-buffer'),
stream: require.resolve('stream-browserify'),
},
},
// 第二种方式选查找自己的loaders文件中有没有这个loader再查找node_modules文件
// modules: [path.resolve(__dirname, 'loaders'), 'node_modules'],
},
optimization: {
// 标记未被使用的代码
usedExports: true,
// 删除 usedExports 标记的未使用的代码
minimize: isEnvProduction,
minimizer: [
new TerserPlugin({
extractComments: false,
}),
],
},
module: {
rules: [
{
test: /\.wxs$/,
include: /src/,
type: 'javascript/auto',
use: [relativeFileLoader()],
resolveLoader: {
// 第一种使用别名的方式引入自定义的loader
alias: {
'wxml-loader': path.resolve(
__dirname,
'../loaders/wxml-loader.js'
),
},
{
test: /\.wxs$/,
include: oakReg,
type: 'javascript/auto',
use: [relativeFileLoader()],
},
{
test: /\.(png|jpg|gif|svg)$/,
include: /src/,
type: 'javascript/auto',
use: relativeFileLoader(),
},
{
test: /\.(png|jpg|gif|svg)$/,
include: oakReg,
type: 'javascript/auto',
use: oakFileLoader(),
},
{
test: /\.less$/,
include: /src/,
exclude: /node_modules/,
use: [
relativeFileLoader('wxss'),
{
loader: 'less-loader',
},
],
},
{
test: /\.less$/,
include: oakReg,
type: 'javascript/auto',
use: [
oakFileLoader('wxss'),
{
loader: 'less-loader',
options: {
lessOptions: () => {
const oakConfigJson = require(`${paths.appSrc}/oak.config.json`);
return {
javascriptEnabled: true,
modifyVars: oakConfigJson.theme,
};
// 第二种方式选查找自己的loaders文件中有没有这个loader再查找node_modules文件
// modules: [path.resolve(__dirname, 'loaders'), 'node_modules'],
},
optimization: {
// 标记未被使用的代码
usedExports: true,
// 删除 usedExports 标记的未使用的代码
minimize: isEnvProduction,
minimizer: [
new TerserPlugin({
extractComments: false,
}),
],
},
module: {
rules: [
{
test: /\.wxs$/,
include: /src/,
type: 'javascript/auto',
use: [relativeFileLoader()],
},
{
test: /\.wxs$/,
include: oakRegex,
type: 'javascript/auto',
use: [relativeFileLoader()],
},
{
test: /\.(png|jpg|gif|svg)$/,
include: /src/,
type: 'javascript/auto',
use: relativeFileLoader(),
},
{
test: /\.(png|jpg|gif|svg)$/,
include: oakRegex,
type: 'javascript/auto',
use: oakFileLoader(),
},
{
test: /\.less$/,
include: /src/,
exclude: /node_modules/,
use: [
relativeFileLoader('wxss'),
{
loader: 'less-loader',
},
],
},
{
test: /\.less$/,
include: oakRegex,
type: 'javascript/auto',
use: [
oakFileLoader('wxss'),
{
loader: 'less-loader',
options: {
lessOptions: () => {
const oakConfigJson = require(paths.oakConfigJson);
return {
javascriptEnabled: true,
modifyVars: oakConfigJson.theme,
};
},
},
},
},
],
},
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
},
{
test: /\.ts$/,
exclude: /node_modules/,
loader: 'ts-loader',
},
// {
// test: /\.json$/,
// include: /src/,
// exclude: /node_modules/,
// type: 'asset/resource',
// generator: {
// filename: `[path][name].[ext]`,
// },
// // type: 'javascript/auto',
// // use: [relativeFileLoader('json')],
// },
{
test: /\.(xml|wxml)$/,
include: /src/,
// type: 'asset/resource',
// generator: {
// filename: `[path][name].[ext]`,
],
},
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
},
{
test: /\.ts$/,
exclude: /node_modules/,
loader: 'ts-loader',
},
// {
// test: /\.json$/,
// include: /src/,
// exclude: /node_modules/,
// type: 'asset/resource',
// generator: {
// filename: `[path][name].[ext]`,
// },
// // type: 'javascript/auto',
// // use: [relativeFileLoader('json')],
// },
type: 'javascript/auto',
use: [
relativeFileLoader('wxml'),
{
loader: 'wxml-loader',
options: {
context: paths.appSrc,
{
test: /\.(xml|wxml)$/,
include: /src/,
type: 'javascript/auto',
use: [
relativeFileLoader('wxml'),
{
loader: 'wxml-loader',
options: {
context: paths.appSrc,
},
},
},
],
},
{
test: /\.(xml|wxml)$/,
include: oakReg,
type: 'javascript/auto',
use: [
oakFileLoader('wxml'),
{
loader: 'wxml-loader',
options: {
context: paths.appSrc,
],
},
{
test: /\.(xml|wxml)$/,
include: oakRegex,
type: 'javascript/auto',
use: [
oakFileLoader('wxml'),
{
loader: 'wxml-loader',
options: {
context: paths.appSrc,
},
},
},
],
},
],
},
plugins: [
new UiExtractPlugin({ context: paths.appSrc }),
new OakWeChatMpPlugin({
exclude: ['*/weui-miniprogram/*'],
include: ['project.config.json', 'sitemap.json'],
split: !isEnvDevelopment,
}),
new webpack.DefinePlugin(env.stringified),
new StylelintPlugin({
fix: true,
files: '**/*.(sa|sc|le|wx|c)ss',
}),
new ProgressBarPlugin({
summary: false,
format: ':msg :percent (:elapsed seconds)',
customSummary: (buildTime) =>
console.log(
chalk.gray(`\n[${new Date().toLocaleDateString()}`),
chalk.green(`Compiled successfully!(${buildTime})\n`)
),
}),
new webpack.ProvidePlugin({
Buffer: ['buffer', 'Buffer'],
}),
].concat(
copyPatterns.length > 0
? [
new CopyWebpackPlugin({
patterns: copyPatterns,
}),
]
: []
),
watch: true,
watchOptions: {
aggregateTimeout: 600,
ignored: '**/node_modules',
followSymlinks: true,
},
],
},
],
},
plugins: [
new UiExtractPlugin({ context: paths.appSrc }),
new OakWeChatMpPlugin({
exclude: ['*/weui-miniprogram/*'],
include: ['project.config.json', 'sitemap.json'],
split: !isEnvDevelopment,
}),
new webpack.DefinePlugin(env.stringified),
new StylelintPlugin({
fix: true,
files: '**/*.(sa|sc|le|wx|c)ss',
}),
new ProgressBarPlugin({
summary: false,
format: ':msg :percent (:elapsed seconds)',
customSummary: (buildTime) =>
console.log(
chalk.gray(`\n[${new Date().toLocaleDateString()}`),
chalk.green(`Compiled successfully!(${buildTime})\n`)
),
}),
new webpack.ProvidePlugin({
Buffer: ['buffer', 'Buffer'],
}),
].concat(
copyPatterns.length > 0
? [
new CopyWebpackPlugin({
patterns: copyPatterns,
}),
]
: []
),
watch: true,
watchOptions: {
aggregateTimeout: 600,
ignored: '**/node_modules',
followSymlinks: true,
},
};
};

View File

@ -58,6 +58,7 @@ module.exports = {
appBuild: resolveApp(buildPath),
appPublic: resolveApp('public'),
appHtml: resolveApp('public/index.html'),
appIndexDevJs: resolveModule(resolveApp, 'src/index.dev'),
appIndexJs: resolveModule(resolveApp, 'src/index'),
appPackageJson: resolveRoot('package.json'),
appSrc: resolveApp('src'),

View File

@ -7,12 +7,16 @@ const tip_style_1 = require("./tip-style");
const cross_spawn_1 = __importDefault(require("cross-spawn"));
async function build(cmd) {
if (!cmd.target) {
(0, tip_style_1.Error)(`${(0, tip_style_1.error)(`Please add --target web or --target mp to he command`)}`);
(0, tip_style_1.Error)(`${(0, tip_style_1.error)(`Please add --target web or --target mp or --target wechatMp to he command`)}`);
return;
}
(0, tip_style_1.Success)(`${(0, tip_style_1.success)(`build ${cmd.target} environment: ${cmd.mode}`)}`);
if (cmd.target === 'mp') {
const result = cross_spawn_1.default.sync(`cross-env NODE_ENV=${cmd.mode} NODE_TARGET=${cmd.target} "${process.execPath}"`, [require.resolve('../scripts/build-mp.js')], {
if (cmd.target === 'mp' || cmd.target === 'wechatMp') {
const result = cross_spawn_1.default.sync(`cross-env NODE_ENV=${cmd.mode} NODE_TARGET=${cmd.target} "${process.execPath}"`, [
require.resolve(`../scripts/${cmd.mode === 'production'
? 'build-mp.js'
: 'start-mp.js'}`),
], {
stdio: 'inherit',
shell: true,
});

View File

@ -1,5 +1,4 @@
/// <reference types="node" />
/// <reference types="node" />
import { PathLike } from 'fs';
import { checkFileExistsAndCreateType } from './enum';
/**

View File

@ -2,13 +2,14 @@ require('../config/mp/env');
const webpack = require('webpack');
const chalk = require('chalk');
const webpackConfig = require('../config/mp/webpack.config');
const configFactory = require('../config/mp/webpack.config');
const config = configFactory('production');
const paths = require('../config/mp/paths');
const getClientEnvironment = require('../config/mp/env');
const env = getClientEnvironment();
webpack(webpackConfig, (err, stats) => {
webpack(config, (err, stats) => {
if (err) {
console.log(chalk.red(err.stack || err));
if (err.details) {

29
scripts/start-mp.js Normal file
View File

@ -0,0 +1,29 @@
require('../config/mp/env');
const webpack = require('webpack');
const chalk = require('chalk');
const configFactory = require('../config/mp/webpack.config');
const config = configFactory('development');
const paths = require('../config/mp/paths');
const getClientEnvironment = require('../config/mp/env');
const env = getClientEnvironment();
webpack(config, (err, stats) => {
if (err) {
console.log(chalk.red(err.stack || err));
if (err.details) {
console.log(chalk.red(err.details));
}
return undefined;
}
if (stats) {
const info = stats.toJson();
if (stats.hasErrors()) {
info.errors.forEach((ele) => console.warn(ele));
}
if (stats.hasWarnings()) {
info.warnings.forEach((ele) => console.warn(ele));
}
}
});

View File

@ -12,15 +12,25 @@ import spawn from 'cross-spawn';
export default async function build(cmd: any) {
if (!cmd.target) {
Error(
`${error(`Please add --target web or --target mp to he command`)}`
`${error(
`Please add --target web or --target mp or --target wechatMp to he command`
)}`
);
return;
}
Success(`${success(`build ${cmd.target} environment: ${cmd.mode}`)}`);
if (cmd.target === 'mp') {
if (cmd.target === 'mp' || cmd.target === 'wechatMp') {
const result = spawn.sync(
`cross-env NODE_ENV=${cmd.mode} NODE_TARGET=${cmd.target} "${process.execPath}"`,
[require.resolve('../scripts/build-mp.js')],
[
require.resolve(
`../scripts/${
cmd.mode === 'production'
? 'build-mp.js'
: 'start-mp.js'
}`
),
],
{
stdio: 'inherit',
shell: true,