@tmsfe/tmskit 0.0.37 → 0.0.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tmsfe/tmskit",
3
- "version": "0.0.37",
3
+ "version": "0.0.39",
4
4
  "description": "tmskit",
5
5
  "main": "dist/index.cjs",
6
6
  "bin": {
@@ -53,6 +53,8 @@
53
53
  "gulp-watch": "^5.0.1",
54
54
  "htmlparser2": "^7.2.0",
55
55
  "inquirer": "^7.3.3",
56
+ "json5": "^2.2.3",
57
+ "preprocess": "^3.2.0",
56
58
  "leven": "3.1.0",
57
59
  "lodash": "^4.17.21",
58
60
  "metalsmith": "^2.3.0",
@@ -13,13 +13,13 @@ const getTargetFile = (sourceFile, module, outputDir) => {
13
13
  return targetFile;
14
14
  };
15
15
 
16
- const addPlugins = function (tmsConfig, srcPipe, pluginParams) {
16
+ const addPlugins = function (tmsConfig, srcPipe, pluginParams) {
17
17
  const { plugins = [] } = tmsConfig;
18
18
  const newPlugins = [{
19
19
  glob: '**/project.config.json',
20
20
  action: mpProjectJson(),
21
21
  }, ...plugins];
22
- return newPlugins.reduce((srcPipe, pluginItem) => srcPipe.pipe(gulpif(
22
+ return newPlugins.reduce((currentPipe, pluginItem) => currentPipe.pipe(gulpif(
23
23
  vinyl => minimatch(vinyl.path, pluginItem.glob),
24
24
  through.obj(function (vinyl, enc, next) {
25
25
  const sourceFile = vinyl.history[0];
@@ -55,6 +55,12 @@ module.exports = function (
55
55
  const newGlobValue = Array.isArray(sourceFile) ? sourceFile : glob;
56
56
  const newDestPath = targetPath ? targetPath : destPath;
57
57
  let srcPipe = src(newGlobValue, { ...srcOption });
58
+
59
+ // 在文件读取后,插件处理之前先加入模板渲染插件
60
+ const templateRender = require('./plugins/tmsTemplateRender');
61
+ srcPipe = srcPipe.pipe(templateRender(tmsConfig));
62
+
63
+ // 后续步骤继续执行其他插件
58
64
  const pluginParams = { module, isDev };
59
65
  srcPipe = addPlugins(tmsConfig, srcPipe, pluginParams);
60
66
  return srcPipe
@@ -72,8 +72,12 @@ module.exports = async (tmsConfig, modules, isDev = true) => {
72
72
  return `!${resolve(srcModulePath, newPath)}/**/*`;
73
73
  });
74
74
 
75
+ const moduleConfigPath = `${srcModulePath}/**/module.config.json`;
76
+
77
+ const moduleConfigGlob = tmsConfig.isKeepModuleConfig ? `${moduleConfigPath}` : `!${moduleConfigPath}`;
78
+
75
79
  mergeMap(compileTasksMap, compile(tmsConfig, {
76
- glob: [`${srcModulePath}/**/*`, `!${srcModulePath}/**/module.config.json`, ...excludes],
80
+ glob: [`${srcModulePath}/**/*`, moduleConfigGlob, ...excludes],
77
81
  destPath: buildModulePath,
78
82
  module: { from: srcModulePath, to: buildModulePath },
79
83
  srcOption: { allowEmpty: true },
@@ -0,0 +1,122 @@
1
+ /* eslint-disable @typescript-eslint/no-require-imports */
2
+ const through = require('through2');
3
+ const pp = require('preprocess');
4
+ const path = require('path');
5
+ const chalk = require('chalk');
6
+ /* eslint-enable @typescript-eslint/no-require-imports */
7
+
8
+ // 允许渲染的文件类型列表
9
+ const TEMPLATE_FILE_EXTENSIONS = [
10
+ '.json',
11
+ '.js',
12
+ '.ts',
13
+ '.wxml',
14
+ '.wxss',
15
+ '.wxs',
16
+ '.css',
17
+ '.less',
18
+ '.scss',
19
+ ];
20
+
21
+ /**
22
+ * 获取预处理器类型
23
+ * @param {string} extName 文件扩展名
24
+ * @returns {string} 预处理器类型
25
+ */
26
+ const getPreprocessType = (extName) => {
27
+ const type = extName.replace('.', '');
28
+ if (type === 'wxml') return 'html';
29
+ if (type === 'wxs') return 'js';
30
+ if (type === 'wxss') return 'css';
31
+ return type;
32
+ };
33
+
34
+ /**
35
+ * 获取错误上下文信息
36
+ * @param {string} content 文件内容
37
+ * @param {number} errorLine 错误行号
38
+ * @param {number} contextLines 上下文行数
39
+ * @returns {string} 格式化的错误上下文
40
+ */
41
+ const getErrorContext = (content, errorLine, contextLines = 2) => {
42
+ const lines = content.split('\n');
43
+ const start = Math.max(1, errorLine - contextLines);
44
+ const end = Math.min(lines.length, errorLine + contextLines);
45
+
46
+ let context = '';
47
+ for (let i = start; i <= end; i++) {
48
+ const marker = i === errorLine ? ' >> ' : ' ';
49
+ context += `${marker}${i}| ${lines[i - 1]}\n`;
50
+ }
51
+ return context;
52
+ };
53
+
54
+ /**
55
+ * 打印错误信息
56
+ * @param {string} filePath 文件路径
57
+ * @param {string} context 错误上下文
58
+ * @param {Error} error 错误对象
59
+ */
60
+ const printError = (filePath, context, error) => {
61
+ console.error(`${chalk.yellow('渲染模板出错,请检查处理:')
62
+ }\n${
63
+ chalk.yellow('文件:')
64
+ }\n${
65
+ chalk.yellow(filePath)
66
+ }\n${
67
+ chalk.yellow('错误位置:')
68
+ }\n${
69
+ chalk.gray(context)
70
+ }\n${
71
+ chalk.red('错误信息:')
72
+ }\n${
73
+ chalk.red(error.message)
74
+ }\n${
75
+ chalk.gray('--------------------------------')}`);
76
+ };
77
+
78
+ /**
79
+ * 模板渲染插件
80
+ * @param {Object} tmsConfig 配置对象
81
+ * @returns {Object} through2 转换流
82
+ */
83
+ module.exports = function tmsTemplateRender(tmsConfig) {
84
+ return through.obj((file, encoding, callback) => {
85
+ if (!file.isBuffer()) {
86
+ return callback(null, file);
87
+ }
88
+
89
+ const extName = path.extname(file.path).toLowerCase();
90
+ if (!TEMPLATE_FILE_EXTENSIONS.includes(extName)) {
91
+ return callback(null, file);
92
+ }
93
+
94
+ const content = file.contents.toString(encoding);
95
+ try {
96
+ const vars = tmsConfig.templateVars || {};
97
+ const ppType = getPreprocessType(extName);
98
+ // 手动检查 @echo 引用的变量是否存在
99
+ const echoPattern = /\/\* @echo (\w+) \*\//g;
100
+ let match;
101
+ while ((match = echoPattern.exec(content)) !== null) {
102
+ const variableName = match[1];
103
+ // eslint-disable-next-line no-prototype-builtins
104
+ if (!vars.hasOwnProperty(variableName)) {
105
+ const error = new Error(`变量 ${variableName} 未定义`);
106
+ error.line = content.substring(0, match.index).split('\n').length;
107
+ throw error;
108
+ }
109
+ }
110
+ const rendered = pp.preprocess(content, vars, ppType);
111
+ // eslint-disable-next-line no-param-reassign
112
+ file.contents = Buffer.from(rendered);
113
+ } catch (err) {
114
+ const context = getErrorContext(content, err.line || 1);
115
+ printError(file.path, context, err);
116
+ // 直接抛出异常,中断编译流程
117
+ throw new Error(`模板渲染失败: ${file.path}\n${err.message}`);
118
+ }
119
+
120
+ callback(null, file);
121
+ });
122
+ };
@@ -3,6 +3,7 @@
3
3
  */
4
4
  /* eslint-disable no-param-reassign */
5
5
  const fs = require('fs');
6
+ const pp = require('preprocess');
6
7
  const { getSubPackages, getModulesConfig } = require('./tmsMpconfig');
7
8
  const { fail, info } = require('../utils/log');
8
9
  const { resolve, filterField } = require('../utils/widgets');
@@ -19,7 +20,7 @@ const report = require('../core/report');
19
20
  */
20
21
  function updateMainPackages(appJson, mainPackages = []) {
21
22
  let foundMainPackages = appJson.subpackages.filter(subpackage => mainPackages.includes(subpackage.name));
22
- if (foundMainPackages.length === 0) {
23
+ if (foundMainPackages.length === 0 && appJson.pages.length === 0) {
23
24
  // 没找到主包
24
25
  foundMainPackages = [appJson.subpackages[0]];
25
26
  }
@@ -30,7 +31,10 @@ function updateMainPackages(appJson, mainPackages = []) {
30
31
  process.exit(-1);
31
32
  }
32
33
  subpackage.pages.forEach((page) => {
33
- appJson.pages.push(`${subpackage.root}/${page}`);
34
+ const pagePath = `${subpackage.root}/${page}`;
35
+ if (!appJson.pages.includes(pagePath)) {
36
+ appJson.pages.push(pagePath);
37
+ }
34
38
  });
35
39
  if (subpackage.plugins) {
36
40
  Object.assign(appJson.plugins, subpackage.plugins);
@@ -55,8 +59,8 @@ const getAppJsonContent = (sourceAppJsonPath) => {
55
59
  }
56
60
  const appJson = JSON.parse(fs.readFileSync(sourceAppJsonPath), 'utf-8');
57
61
  // 加入默认值
58
- appJson.subpackages = [];
59
- appJson.pages = [];
62
+ appJson.subpackages = appJson.subpackages || [];
63
+ appJson.pages = appJson.pages || [];
60
64
  return appJson;
61
65
  };
62
66
 
@@ -123,11 +127,11 @@ const fixAppJson = (appJson) => {
123
127
  async function buildOutputAppJson(tmsConfig, modules) {
124
128
  try {
125
129
  // 获取所有模块,合并模块依赖的模块
126
- const modulesConfig = getModulesConfig(modules, tmsConfig.appName, false);
130
+ const modulesConfig = getModulesConfig(modules, tmsConfig, false);
127
131
  // 获取所有的分包
128
132
  const subPackages = getSubPackages(modulesConfig);
129
133
  // 获取app.json的配置
130
- const appJson = getAppJsonContent(resolve('./app.json'));
134
+ let appJson = getAppJsonContent(resolve('./app.json'));
131
135
 
132
136
  // 更新app.json中的subpackages
133
137
  appJson.subpackages = subPackages;
@@ -138,7 +142,12 @@ async function buildOutputAppJson(tmsConfig, modules) {
138
142
  // 更新主包,需在subpackages处理完成后执行, pages/
139
143
  updateMainPackages(appJson, tmsConfig.mainPackages);
140
144
 
141
- fs.writeFileSync(resolve(`${tmsConfig.outputDir}/app.json`), JSON.stringify(appJson, null, 2), 'utf8');
145
+ // 模板渲染:先将 app.json 转为字符串,然后通过 preprocess 渲染
146
+ const appJsonStr = JSON.stringify(appJson, null, 2);
147
+ const preprocessedStr = pp.preprocess(appJsonStr, tmsConfig.templateVars || {}, 'json');
148
+ fs.writeFileSync(resolve(`${tmsConfig.outputDir}/app.json`), preprocessedStr, 'utf8');
149
+
150
+ appJson = JSON.parse(preprocessedStr);
142
151
 
143
152
  if (typeof tmsConfig?.hooks?.updateAppJson === 'function') {
144
153
  await tmsConfig?.hooks?.updateAppJson({
package/src/core/mpCi.js CHANGED
@@ -114,8 +114,22 @@ const uploadMp = async (params = {}) => {
114
114
  });
115
115
  };
116
116
 
117
+ const getDevSourceMap = async (params = {}) => {
118
+ const { appId, projectPath, privateKey, robot, sourceMapSavePath } = params;
119
+ const project = await getMpCi({
120
+ appId,
121
+ projectPath,
122
+ privateKey,
123
+ });
124
+ return await ci.getDevSourceMap({
125
+ project,
126
+ robot,
127
+ sourceMapSavePath,
128
+ });
129
+ };
117
130
  module.exports = {
118
131
  buildMpNpm,
119
132
  previewMp,
120
133
  uploadMp,
134
+ getDevSourceMap,
121
135
  };
@@ -5,6 +5,8 @@
5
5
  const loadash = require('lodash');
6
6
  const fs = require('fs');
7
7
  const path = require('path');
8
+ const pp = require('preprocess');
9
+ const JSON5 = require('json5');
8
10
  const { TMS_CONFIG_FILENAME, MODULE_CONFIG_FILENAME, TMS_PRIVATE_FILENAME } = require('../config/constant');
9
11
  const { resolve, isObject, isArray, getAbsolutePath } = require('../utils/widgets');
10
12
  const defaultTmsConfig = require('../config/defaultTmsConfig');
@@ -57,9 +59,15 @@ const getTmsConfig = (configPath) => {
57
59
  modules.all = tmsConfig.modules;
58
60
  tmsConfig.modules = modules;
59
61
  }
60
- // 合并默认值
61
- const res = loadash.mergeWith(tmsConfig, tmsPrivateCf, (objValue, srcValue) => {
62
- if (loadash.isArray(objValue) && objValue[0] && loadash.isObject(objValue[0])) {
62
+ // 使用自定义的合并策略:
63
+ // 1. templateVars 字段做浅合并(即把 tms.config.js 中的和 tms.private.config.js 中的对象合并,
64
+ // 如果存在同名字段则以 tms.private.config.js 中的为准)。
65
+ // 2. 对数组(并且数组元素为对象的情况)执行拼接
66
+ const res = loadash.mergeWith({}, tmsConfig, tmsPrivateCf, (objValue, srcValue, key) => {
67
+ if (key === 'templateVars') {
68
+ return { ...(objValue || {}), ...(srcValue || {}) };
69
+ }
70
+ if (Array.isArray(objValue) && objValue[0] && isObject(objValue[0])) {
63
71
  return objValue.concat(srcValue);
64
72
  }
65
73
  });
@@ -128,13 +136,18 @@ const adaptSubPackages = function (moduleConfig, appName) {
128
136
  * @param {array} modules 用户要编译的模块列表
129
137
  * @param { string } appName 小程序的名称
130
138
  */
131
- function getModulesConfig(modules = [], appName) {
139
+ function getModulesConfig(modules = [], tmsConfig) {
140
+ const { appName } = tmsConfig;
132
141
  const modulesConfig = [];
133
142
  modules.forEach((moduleItem) => {
134
143
  const moduleConfigPath = resolve(moduleItem.path, MODULE_CONFIG_FILENAME);
135
144
  let moduleConfig;
136
145
  try {
137
- moduleConfig = JSON.parse(fs.readFileSync(moduleConfigPath, 'utf-8'));
146
+ // 模板渲染:先将 module.config.json 转为字符串,然后通过 preprocess 渲染
147
+ const moduleConfigJsonStr = fs.readFileSync(moduleConfigPath, 'utf-8');
148
+ const preprocessedStr = pp.preprocess(moduleConfigJsonStr, tmsConfig.templateVars || {}, 'json');
149
+ // fs.writeFileSync(resolve(moduleConfigPath), moduleConfigJsonStr, 'utf8');
150
+ moduleConfig = JSON5.parse(preprocessedStr);
138
151
  } catch (e) {
139
152
  throw new Error(`${moduleConfigPath}json解析报错: ${e}`);
140
153
  }
@@ -241,11 +254,11 @@ const getModulesByMergeDepModules = (tmsConfig, modules, errorIsQuit = false) =>
241
254
  allModules.set(moduleItem.moduleName, moduleItem);
242
255
  }
243
256
  if (errorIsQuit) {
244
- throw new Error(`${moduleItem.moduleName}模块的配置文件module.config.json在${moduleItem.path}目录下没有找到`);
257
+ throw new Error(`${moduleItem.moduleName}模块的配置文件module.config.json在${moduleItem.path}目录下没有找到 ${moduleConfigPath}`);
245
258
  }
246
259
  return;
247
260
  }
248
- const [moduleConfig = {}] = getModulesConfig([moduleItem], tmsConfig.appName);
261
+ const [moduleConfig = {}] = getModulesConfig([moduleItem], tmsConfig);
249
262
 
250
263
  if (!allModules.has(moduleItem.moduleName)) {
251
264
  allModules.set(moduleItem.moduleName, checkModuleItem(tmsConfig, moduleItem, moduleConfig));
package/src/entry.js CHANGED
@@ -81,6 +81,15 @@ module.exports = [
81
81
  require('./scripts/run/index')('upload', cmdOptions);
82
82
  },
83
83
  },
84
+ {
85
+ command: 'sourcemap',
86
+ description: '获取sourcemap',
87
+ options: [
88
+ ],
89
+ action: (cmdOptions) => {
90
+ require('./scripts/run/index')('sourcemap', cmdOptions);
91
+ },
92
+ },
84
93
  {
85
94
  command: 'cloud-dev',
86
95
  description: '云函数开发',
@@ -2,6 +2,7 @@ const shelljs = require('shelljs');
2
2
  const compileDev = require('../../../compile/dev');
3
3
  const { resolve, filterField } = require('../../../utils/widgets');
4
4
  const init = require('../init/index');
5
+ const { buildOutputAppJson } = require('../../../core/buildAppJson');
5
6
  const { getModulesByMergeDepModules, getSubPackages } = require('../../../core/tmsMpconfig');
6
7
  const { info } = require('../../../utils/log');
7
8
  const { global } = require('../../../utils/global');
@@ -40,6 +41,9 @@ async function dev(tmsConfig, targetModules) {
40
41
  // 初始化操作
41
42
  const { subPackages, modules: newModules } = await init(tmsConfig, targetModules);
42
43
 
44
+ // 在启动开发环境前主动执行生成 app.json
45
+ await buildOutputAppJson(tmsConfig, newModules);
46
+
43
47
  info('当前dev启动的有效模块', newModules.map(item => item.moduleName).sort());
44
48
  if (typeof tmsConfig?.hooks?.beforeFirstCompile === 'function') {
45
49
  await tmsConfig?.hooks?.beforeFirstCompile({
@@ -5,6 +5,7 @@ const build = require('./build/index');
5
5
  const install = require('./install/index');
6
6
  const preview = require('./preview/index');
7
7
  const upload = require('./upload/index');
8
+ const sourcemap = require('./sourcemap/index');
8
9
  const cloudLink = require('./cloud/link');
9
10
  const cloudDev = require('./cloud/dev');
10
11
  const { fail, info } = require('../../utils/log');
@@ -121,6 +122,10 @@ function otherCommands(tmsConfig, commandName, cmdOptions) {
121
122
  upload(tmsConfig, cmdOptions);
122
123
  report('run:upload', { appName: tmsConfig.appName });
123
124
  return;
125
+ case 'sourcemap':
126
+ sourcemap(tmsConfig);
127
+ report('run:sourcemap', { appName: tmsConfig.appName });
128
+ return;
124
129
  default:
125
130
  return;
126
131
  }
@@ -0,0 +1,48 @@
1
+ const mpCi = require('../../../core/mpCi');
2
+ const { resolve, createTask } = require('../../../utils/widgets');
3
+ const { handleError } = require('../../../core/handleError');
4
+ const { info } = require('../../../utils/log');
5
+ const { getDesc } = require('../preview/utils');
6
+ const report = require('../../../core/report');
7
+
8
+ const handleParams = (tmsConfig, cmdOptions) => {
9
+ const params = {
10
+ ...(tmsConfig.upload ? tmsConfig.upload : {}),
11
+ ...cmdOptions,
12
+ };
13
+
14
+ return {
15
+ sourceMapSavePath: './sourcemap.zip',
16
+ ...params,
17
+ appId: params.appId || tmsConfig.appId,
18
+ projectPath: resolve(tmsConfig.outputDir),
19
+ privateKey: params.privateKey || tmsConfig.privateKey,
20
+ robot: params.robot || 30,
21
+ desc: params.desc || getDesc(params.desc),
22
+ };
23
+ };
24
+
25
+ /**
26
+ * 获取sourcemap
27
+ * @param {object} tmsConfig
28
+ * @param {object} cmdOptions {version: '2022.28.5', desc: '', robot: 2, infoOutput: './a.txt' }
29
+ */
30
+ async function sourcemap(tmsConfig, cmdOptions) {
31
+ try {
32
+ const params = handleParams(tmsConfig, cmdOptions);
33
+ report('sourcemap');
34
+
35
+ await createTask(
36
+ mpCi.getDevSourceMap,
37
+ '正在获取小程序代码sourcemap',
38
+ '获取小程序代码sourcemap完成',
39
+ )({
40
+ ...params,
41
+ });
42
+ info(`sourcemap文件已保存到${params.sourceMapSavePath}`);
43
+ } catch (e) {
44
+ console.log('详细错误:', e);
45
+ handleError(`获取sourcemap错误: ${e.message}`, true);
46
+ }
47
+ }
48
+ module.exports = sourcemap;