neo-cmp-cli 1.0.0

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.
Files changed (76) hide show
  1. package/README.md +236 -0
  2. package/bin/neo.js +2 -0
  3. package/package.json +74 -0
  4. package/src/config/default.config.js +136 -0
  5. package/src/config/index.js +9 -0
  6. package/src/initData/defaultTemplate.html +13 -0
  7. package/src/initData/neo.config.js +99 -0
  8. package/src/module/index.js +244 -0
  9. package/src/module/inspect.js +40 -0
  10. package/src/module/main.js +109 -0
  11. package/src/module/neoInit.js +44 -0
  12. package/src/module/neoInitByCopy.js +37 -0
  13. package/src/oss/publish2oss.js +218 -0
  14. package/src/plugins/README.md +2 -0
  15. package/src/template/react-custom-widget-template/.editorconfig +10 -0
  16. package/src/template/react-custom-widget-template/.prettierrc.js +12 -0
  17. package/src/template/react-custom-widget-template/README.md +51 -0
  18. package/src/template/react-custom-widget-template/commitlint.config.js +59 -0
  19. package/src/template/react-custom-widget-template/neo.config.js +112 -0
  20. package/src/template/react-custom-widget-template/package.json +56 -0
  21. package/src/template/react-custom-widget-template/public/css/base.css +283 -0
  22. package/src/template/react-custom-widget-template/public/scripts/app/bluebird.js +6679 -0
  23. package/src/template/react-custom-widget-template/public/template.html +13 -0
  24. package/src/template/react-custom-widget-template/src/assets/css/common.scss +127 -0
  25. package/src/template/react-custom-widget-template/src/assets/css/mixin.scss +47 -0
  26. package/src/template/react-custom-widget-template/src/components/info-card/index.jsx +45 -0
  27. package/src/template/react-custom-widget-template/src/components/info-card/model.js +81 -0
  28. package/src/template/react-custom-widget-template/src/components/info-card/register.js +4 -0
  29. package/src/template/react-custom-widget-template/src/components/info-card/style.scss +67 -0
  30. package/src/template/react-custom-widget-template/src/preview.js +5 -0
  31. package/src/template/react-ts-custom-widget-template/.editorconfig +10 -0
  32. package/src/template/react-ts-custom-widget-template/.prettierrc.js +12 -0
  33. package/src/template/react-ts-custom-widget-template/README.md +53 -0
  34. package/src/template/react-ts-custom-widget-template/commitlint.config.js +59 -0
  35. package/src/template/react-ts-custom-widget-template/neo.config.js +106 -0
  36. package/src/template/react-ts-custom-widget-template/package.json +59 -0
  37. package/src/template/react-ts-custom-widget-template/public/css/base.css +283 -0
  38. package/src/template/react-ts-custom-widget-template/public/scripts/app/bluebird.js +6679 -0
  39. package/src/template/react-ts-custom-widget-template/public/template.html +13 -0
  40. package/src/template/react-ts-custom-widget-template/src/assets/css/common.scss +127 -0
  41. package/src/template/react-ts-custom-widget-template/src/assets/css/mixin.scss +47 -0
  42. package/src/template/react-ts-custom-widget-template/src/components/info-card/index.tsx +70 -0
  43. package/src/template/react-ts-custom-widget-template/src/components/info-card/plugin.ts +80 -0
  44. package/src/template/react-ts-custom-widget-template/src/components/info-card/register.ts +5 -0
  45. package/src/template/react-ts-custom-widget-template/src/components/info-card/style.scss +105 -0
  46. package/src/template/react-ts-custom-widget-template/src/components/list-widget/README.md +2 -0
  47. package/src/template/react-ts-custom-widget-template/src/components/list-widget/index.tsx +208 -0
  48. package/src/template/react-ts-custom-widget-template/src/components/list-widget/model.ts +92 -0
  49. package/src/template/react-ts-custom-widget-template/src/components/list-widget/register.ts +5 -0
  50. package/src/template/react-ts-custom-widget-template/src/components/list-widget/style.scss +350 -0
  51. package/src/template/react-ts-custom-widget-template/src/preview.tsx +37 -0
  52. package/src/template/react-ts-custom-widget-template/tsconfig.json +68 -0
  53. package/src/template/vue2-neo-custom-widget/.editorconfig +10 -0
  54. package/src/template/vue2-neo-custom-widget/.prettierrc.js +12 -0
  55. package/src/template/vue2-neo-custom-widget/README.md +52 -0
  56. package/src/template/vue2-neo-custom-widget/commitlint.config.js +59 -0
  57. package/src/template/vue2-neo-custom-widget/neo.config.js +122 -0
  58. package/src/template/vue2-neo-custom-widget/package.json +59 -0
  59. package/src/template/vue2-neo-custom-widget/public/css/base.css +283 -0
  60. package/src/template/vue2-neo-custom-widget/public/scripts/app/bluebird.js +6679 -0
  61. package/src/template/vue2-neo-custom-widget/public/template.html +13 -0
  62. package/src/template/vue2-neo-custom-widget/src/assets/css/common.scss +126 -0
  63. package/src/template/vue2-neo-custom-widget/src/assets/css/mixin.scss +47 -0
  64. package/src/template/vue2-neo-custom-widget/src/preview.js +9 -0
  65. package/src/template/vue2-neo-custom-widget/src/widgets/info-card/index.vue +131 -0
  66. package/src/template/vue2-neo-custom-widget/src/widgets/info-card/plugin.js +81 -0
  67. package/src/template/vue2-neo-custom-widget/src/widgets/info-card/register.js +8 -0
  68. package/src/utils/getConfigObj.js +18 -0
  69. package/src/utils/getEntries.js +54 -0
  70. package/src/utils/neoConfigInit.js +13 -0
  71. package/src/utils/neoParams.js +12 -0
  72. package/src/utils/pathUtils.js +23 -0
  73. package/src/utils/projectNameValidator.js +76 -0
  74. package/src/utils/replaceInFiles.js +47 -0
  75. package/src/utils/replaceInPackage.js +134 -0
  76. package/test/demo.js +3 -0
@@ -0,0 +1,13 @@
1
+ <html lang="en">
2
+ <head>
3
+ <meta charset="UTF-8">
4
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
5
+ <meta name="format-detection" content="telephone=no"/>
6
+ <meta name="viewport" content="initial-scale=1.0,user-scalable=no,width=device-width,viewport-fit=cover">
7
+ <meta http-equiv="X-UA-Compatible" content="ie=edge">
8
+ <title>自定义组件预览页</title>
9
+ </head>
10
+ <body>
11
+ <div id="root"></div>
12
+ </body>
13
+ </html>
@@ -0,0 +1,126 @@
1
+ /* 公共的自定义函数 */
2
+ @function px2vw($px, $screen-width: 750) {
3
+ @return ($px / $screen-width) * 100vw;
4
+ }
5
+
6
+ @function px2rem($px, $remRate: 100) {
7
+ @return ($px / $remRate) + rem;
8
+ }
9
+
10
+ @function px2vmin($px, $screen-width: 750) {
11
+ @return ($px / $screen-width) * 100vmin;
12
+ }
13
+
14
+ @mixin fillBox {
15
+ width: 100%;
16
+ height: 100%;
17
+ }
18
+
19
+ /* 头部细线 */
20
+ @mixin borderTop {
21
+ content: '';
22
+ position: absolute;
23
+ left: 0;
24
+ right: 0;
25
+ top: 0;
26
+ width: 100%;
27
+ height: 1px;
28
+ background: #ddd;
29
+ transform: scaleY(0.5);
30
+ }
31
+
32
+ /* 底部细线 */
33
+ @mixin borderBtm {
34
+ content: '';
35
+ position: absolute;
36
+ left: 0;
37
+ right: 0;
38
+ bottom: 0;
39
+ width: 100%;
40
+ height: 1px;
41
+ background: #ddd;
42
+ transform: scaleY(0.5);
43
+ }
44
+
45
+ /* 统一的内边距 */
46
+ @mixin unifiedPadding($value: 40) {
47
+ padding: 0 px2rem($value) 0 px2rem($value);
48
+ }
49
+
50
+ /* 统一的左内边距 */
51
+ @mixin unifiedLeftPadding($value: 40) {
52
+ padding-left: px2rem($value);
53
+ }
54
+
55
+ /* 统一的右内边距 */
56
+ @mixin unifiedRightPadding($value: 40) {
57
+ padding-right: px2rem($value);
58
+ }
59
+
60
+ /* 统一的底部边框样式 */
61
+ @mixin unifiedBottomBorder {
62
+ border-bottom: 1px solid #ddd;
63
+ }
64
+
65
+ /* 统一的上边框样式 */
66
+ @mixin unifiedTopBorder {
67
+ border-top: 1px solid #ddd;
68
+ }
69
+
70
+ /* 统一的Item高度 */
71
+ @mixin unifiedItemHeight {
72
+ line-height: px2rem(120);
73
+ height: px2rem(120);
74
+ }
75
+
76
+ /* 设置行高样式 */
77
+ @mixin setItemHeight($value: 120) {
78
+ line-height: px2rem($value);
79
+ height: px2rem($value);
80
+ }
81
+
82
+ /* 统一的Item样式 */
83
+ @mixin unifiedItemStyle {
84
+ font-family: PingFangSC-Regular;
85
+ font-size: px2rem(28);
86
+ color: #828282;
87
+ letter-spacing: 0;
88
+ }
89
+
90
+ /* 统一的弹性盒子样式 */
91
+ @mixin unifiedFlexBoxStyle {
92
+ display: flex;
93
+ flex-wrap: nowrap;
94
+ justify-content: center;
95
+ align-items: center;
96
+ }
97
+
98
+ /* 统一的Title样式 */
99
+ @mixin unifiedTitleStyle {
100
+ font-family: PingFangSC-Regular;
101
+ font-size: px2rem(40);
102
+ color: #1e1e1e;
103
+ }
104
+
105
+ /* 统一的内容样式 */
106
+ @mixin unifiedContentStyle {
107
+ font-family: PingFangSC-Regular;
108
+ font-size: px2rem(28);
109
+ color: #1e1e1e;
110
+ letter-spacing: 0;
111
+ text-align: right;
112
+ line-height: px2rem(28);
113
+ }
114
+
115
+ /* 底部导航盒子样式 */
116
+ @mixin fixedBottomBox {
117
+ position: fixed;
118
+ left: 0;
119
+ bottom: 0;
120
+ width: 100%;
121
+ }
122
+
123
+ // 常用的变量
124
+ $background-color: #fafafa;
125
+ $border-color: #f7f7f7;
126
+ $page-padding-top: px2rem(20);
@@ -0,0 +1,47 @@
1
+ // 通用mixin
2
+ $borderColor: #ddd;
3
+
4
+ // type 为top 或者 bottom
5
+ @mixin borderTopOrBtm($type) {
6
+ &::after {
7
+ content: '';
8
+ position: absolute;
9
+ left: 0;
10
+ right: 0;
11
+ #{$type}: 0;
12
+ width: 100%;
13
+ height: 1px;
14
+ background: $borderColor;
15
+ transform: scaleY(0.5);
16
+ }
17
+ }
18
+
19
+ // type为 right 或者 left
20
+ @mixin borderRtOrLt($type) {
21
+ &::after {
22
+ content: '';
23
+ position: absolute;
24
+ top: 0;
25
+ bottom: 0;
26
+ #{$type}: 0;
27
+ height: 100%;
28
+ width: 1px;
29
+ background: $borderColor;
30
+ transform: scaleX(0.5);
31
+ }
32
+ }
33
+
34
+ //超出1行显示...
35
+ @mixin ellipsis1 {
36
+ overflow: hidden;
37
+ text-overflow: ellipsis;
38
+ white-space: nowrap;
39
+ }
40
+
41
+ // 超出多行显示...
42
+ @mixin ellipsis($num) {
43
+ overflow: hidden;
44
+ display: -webkit-box;
45
+ -webkit-line-clamp: $num;
46
+ -webkit-box-orient: vertical;
47
+ }
@@ -0,0 +1,9 @@
1
+ import Vue from 'vue';
2
+ import InfoCard from './components/info-card';
3
+
4
+ /* 引入公共的静态资源 */
5
+ import '$public/css/base.css';
6
+
7
+ new Vue({
8
+ render: (h) => h(InfoCard),
9
+ }).$mount('#root');
@@ -0,0 +1,131 @@
1
+ <template>
2
+ <div class="info-card-container">
3
+ <div class="news-title">
4
+ {{ title }}
5
+ </div>
6
+ <div class="item-imgbox">
7
+ <div
8
+ class="news-img"
9
+ :style="{ backgroundImage: 'url(' + backgroundImage + ')' }"
10
+ ></div>
11
+ <div v-if="img_count > 0" class="img-count">
12
+ {{ img_count }}
13
+ </div>
14
+ </div>
15
+ <div class="news-info">
16
+ <div class="left media-mark">NeoCRM · 低代码平台@Vue2.0组件</div>
17
+ <div v-if="comment_count && comment_count != 0" class="cmt-num right">
18
+ {{ agreeDataFormat(comment_count) }}评
19
+ </div>
20
+ </div>
21
+ </div>
22
+ </template>
23
+ <script>
24
+ // 按需引入element-ui中的组件
25
+ import { Rate } from 'element-ui';
26
+
27
+ export default {
28
+ data() {
29
+ return {
30
+ isAlive: true,
31
+ isFirstVisit: true,
32
+ img_count: 3,
33
+ title:
34
+ '营销服全场景智能CRM,帮助企业搭建数字化客户经营平台,实现业绩高质量增长。',
35
+ backgroundImage: 'https://neo-widgets.bj.bcebos.com/NeoCRM.jpg',
36
+ comment_count: 2025,
37
+ rateVal: 3,
38
+ };
39
+ },
40
+ components: {
41
+ Rate,
42
+ },
43
+ mounted() {
44
+ this.isFirstVisit = false;
45
+ },
46
+ activated() {
47
+ this.isAlive = true;
48
+ },
49
+ deactivated() {
50
+ this.isAlive = false;
51
+ },
52
+ methods: {
53
+ agreeDataFormat(agreeData) {
54
+ if (agreeData && agreeData <= 9999) {
55
+ return agreeData;
56
+ } else if (agreeData && agreeData > 9999) {
57
+ return `${Math.floor(agreeData / 1000) / 10}w`;
58
+ }
59
+ },
60
+ },
61
+ };
62
+ </script>
63
+ <style lang="scss">
64
+ .info-card-container {
65
+ position: relative;
66
+ box-sizing: border-box;
67
+ // border-bottom: 1px solid #ececec;
68
+ margin: 6px 12px;
69
+ padding: 6px 12px;
70
+ background-color: #fff;
71
+
72
+ .news-title {
73
+ padding: 6px 0;
74
+ font-family: PingFangSC-Regular;
75
+ font-size: 16px;
76
+ line-height: 22px;
77
+ color: #5f5e5e;
78
+ }
79
+
80
+ .item-imgbox {
81
+ position: relative;
82
+ height: 395px;
83
+ background: #f0f0f0;
84
+ cursor: pointer;
85
+ box-sizing: border-box;
86
+ text-align: center;
87
+ overflow: hidden;
88
+
89
+ .news-img {
90
+ width: 100%;
91
+ height: 100%;
92
+ box-sizing: border-box;
93
+ background-size: contain;
94
+ }
95
+
96
+ .img-count {
97
+ position: absolute;
98
+ top: 0;
99
+ right: 0;
100
+ padding: 6px 8px;
101
+ color: #fff;
102
+ min-width: 60px;
103
+ text-align: center;
104
+ line-height: 1.2;
105
+ background: rgba(0, 0, 0, 0.4);
106
+ font-size: 25px;
107
+ box-sizing: border-box;
108
+ overflow: hidden;
109
+ }
110
+ }
111
+
112
+ .news-info {
113
+ font-family: PingFangSC-Light;
114
+ height: 28px;
115
+ box-sizing: border-box;
116
+ display: flex;
117
+ justify-content: space-between;
118
+ align-items: center;
119
+ }
120
+
121
+ .media-mark,
122
+ .cmt-num {
123
+ display: inline-block;
124
+ height: 28px;
125
+ line-height: 28px;
126
+ font-family: PingFangSC-Light;
127
+ font-size: 18px;
128
+ color: #828282;
129
+ }
130
+ }
131
+ </style>
@@ -0,0 +1,81 @@
1
+ /**
2
+ * @file 自定义组件对接编辑器的描述文件
3
+ */
4
+ // @ts-ignore
5
+ import { registerNeoEditorModel } from 'neo-register';
6
+
7
+ export class InfoCardModel {
8
+ // 自定义组件名称,用于标识组件的唯一性
9
+ cmpType = 'vue-info-card';
10
+
11
+ // 组件名称,用于设置在编辑器左侧组件面板中展示的名称
12
+ label = 'vue 信息卡片';
13
+
14
+ // 组件描述,用于设置在编辑器左侧组件面板中展示的描述
15
+ description = '信息展示卡片';
16
+
17
+ // 分类标签,用于设置在编辑器左侧组件面板哪个分类中展示(可设置多个分类标签)
18
+ tags = ['自定义组件'];
19
+
20
+ // 组件图标,用于设置在编辑器左侧组件面板中展示的图标
21
+ iconSrc = 'https://neo-widgets.bj.bcebos.com/custom-widget.svg';
22
+ // iconSrc = 'https://neo-widgets.bj.bcebos.com/favicon.png';
23
+
24
+ // 初次插入页面的默认属性数据
25
+ defaultComProps = {
26
+ title:
27
+ '营销服全场景智能CRM,帮助企业搭建数字化客户经营平台,实现业绩高质量增长。',
28
+ label: 'vue 信息卡片',
29
+ backgroundImage: 'https://neo-widgets.bj.bcebos.com/NeoCRM.jpg',
30
+ img_count: 3,
31
+ comment_count: 2025,
32
+ };
33
+
34
+ // 设计器端预览时展示的默认数据
35
+ previewComProps = {
36
+ label: 'vue 信息卡片',
37
+ };
38
+
39
+ /**
40
+ * 组件面板配置,用于生成编辑器右侧属性配置面板内容
41
+ * 备注:自定义组件中可用的配置项类型 请见 [当前可用表单项](https://github.com/wibetter/neo-register/blob/master/docs/FormItemType.md
42
+ */
43
+ propsSchema = [
44
+ {
45
+ type: 'textarea',
46
+ name: 'title',
47
+ label: '卡片title',
48
+ value:
49
+ '营销服全场景智能CRM,帮助企业搭建数字化客户经营平台,实现业绩高质量增长。',
50
+ },
51
+ {
52
+ type: 'text',
53
+ name: 'backgroundImage',
54
+ label: '展示图片',
55
+ value: 'https://neo-widgets.bj.bcebos.com/NeoCRM.jpg',
56
+ },
57
+ {
58
+ type: 'number',
59
+ name: 'img_count',
60
+ label: '图片数量',
61
+ value: 3,
62
+ },
63
+ {
64
+ type: 'number',
65
+ name: 'comment_count',
66
+ label: '评论数',
67
+ value: 2025,
68
+ },
69
+ ];
70
+
71
+ // 支持 函数式写法:propsSchemaCreator,com 为页面设计器中的组件实例。优先级比 propsSchema 高
72
+ /*
73
+ propsSchemaCreator = (com: any) => {
74
+ return [];
75
+ };
76
+ */
77
+ }
78
+
79
+ registerNeoEditorModel(InfoCardModel);
80
+
81
+ export default InfoCardModel;
@@ -0,0 +1,8 @@
1
+ import InfoCard from './index';
2
+ // @ts-ignore
3
+ import { registerNeoCmp } from 'neo-register';
4
+
5
+ /* 引入公共的静态资源 */
6
+ // import '$public/css/base.css'; // 备注: 全局样式要注意不要干扰平台和编辑器端的正常展示
7
+
8
+ registerNeoCmp(InfoCard, 'vue-info-card');
@@ -0,0 +1,18 @@
1
+ const glob = require('glob');
2
+
3
+ const fileExists = function (fileDir) {
4
+ let exists = false;
5
+ const files = glob.sync(fileDir); // 同步读取本地文件
6
+ if (files.length > 0) {
7
+ exists = true;
8
+ }
9
+ return exists;
10
+ };
11
+
12
+ module.exports = function (currentConfigDir) {
13
+ let currentConfig = {};
14
+ if (fileExists(currentConfigDir)) {
15
+ currentConfig = require(currentConfigDir);
16
+ }
17
+ return currentConfig;
18
+ };
@@ -0,0 +1,54 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const _ = require('lodash');
4
+ const { resolveToCurrentRoot } = require('./pathUtils');
5
+
6
+ module.exports = (buildType = 'build2lib', defaultComponentsDir = './src/components') => {
7
+ const widgetEntries = {};
8
+ const linkDebugEntries = {
9
+ index: []
10
+ };
11
+ const componentsDir = resolveToCurrentRoot(defaultComponentsDir);
12
+ if (!fs.existsSync(componentsDir)) {
13
+ console.error(`未找到组件目录,请检查 ${defaultComponentsDir} 目录是否存在`);
14
+ return widgetEntries;
15
+ }
16
+
17
+ try {
18
+ // 读取指定目录下的所有文件
19
+ const widgetDirs = fs.readdirSync(componentsDir);
20
+
21
+ // 遍历所有目录
22
+ widgetDirs.forEach((dir) => {
23
+ const filePath = path.join(componentsDir, dir);
24
+ // 获取文件状态
25
+ const stat = fs.statSync(filePath);
26
+ // 如果文件是目录,则递归处理子目录
27
+ if (stat.isDirectory() && !dir.startsWith('.') && dir !== 'node_modules') {
28
+ const widgetPath = path.join(defaultComponentsDir, dir);
29
+ const curWidgetName = _.camelCase(dir);
30
+
31
+ fs.readdirSync(widgetPath)
32
+ .filter((file) => file.match(/[register|model]\.[tj]sx?$/))
33
+ .map((file) => path.join(widgetPath, file))
34
+ .forEach((filePath) => {
35
+ const curPath = `./${filePath}`;
36
+ if (filePath.match(/register\.[tj]sx?$/)) {
37
+ widgetEntries[curWidgetName] = curPath;
38
+ } else if (filePath.match(/model\.[tj]sx?$/)) {
39
+ widgetEntries[`${curWidgetName}Model`] = curPath;
40
+ }
41
+ linkDebugEntries.index.push(curPath);
42
+ });
43
+ }
44
+ });
45
+
46
+ if (buildType === 'linkDebug') {
47
+ return linkDebugEntries;
48
+ }
49
+ } catch (error) {
50
+ console.error('replaceInFiles 运行失败:', error);
51
+ }
52
+
53
+ return widgetEntries;
54
+ };
@@ -0,0 +1,13 @@
1
+ const path = require('path');
2
+ const { createFile } = require('akfun');
3
+
4
+ // 将脚手架的默认配置文件拷贝到当前项目根目录
5
+ const createDefaultConfig = function (_configProjectName) {
6
+ const configProjectName = _configProjectName || 'neo.config.js';
7
+ createFile(
8
+ path.resolve(__dirname, `../initData/${configProjectName}`),
9
+ path.resolve(process.cwd(), `./${configProjectName}`)
10
+ );
11
+ };
12
+
13
+ module.exports = createDefaultConfig;
@@ -0,0 +1,12 @@
1
+ let consoleTag = '[neo-cmp-cli]'; // 输出标记
2
+
3
+ function setConsoleTag(newText) {
4
+ consoleTag = newText;
5
+ }
6
+
7
+ module.exports = {
8
+ get consoleTag() {
9
+ return consoleTag;
10
+ },
11
+ setConsoleTag
12
+ };
@@ -0,0 +1,23 @@
1
+ const path = require('path');
2
+ const fs = require('fs');
3
+
4
+ // 当前命令执行的路径
5
+ const currentRoot = () => fs.realpathSync(process.cwd());
6
+ const resolveToCurrentRoot = (filePath) => path.resolve(currentRoot(), filePath);
7
+ const resolveToCurrentDist = (filePath, outDir) =>
8
+ path.resolve(currentRoot(), outDir || 'dist/', filePath);
9
+ const currentSrc = () => resolveToCurrentRoot('src');
10
+ const currentBuild = () => resolveToCurrentRoot('build');
11
+
12
+ // 获取当前项目的package文件(从当前命令执行的路径下查找)
13
+ const catchCurPackageJson = () => resolveToCurrentRoot('package.json');
14
+
15
+ module.exports = {
16
+ currentRoot,
17
+ resolveToCurrentRoot,
18
+ resolveToCurrentDist,
19
+ resolve: resolveToCurrentRoot,
20
+ currentSrc,
21
+ currentBuild,
22
+ catchCurPackageJson
23
+ };
@@ -0,0 +1,76 @@
1
+ /**
2
+ * 文件名验证工具
3
+ * 验证规则:
4
+ * 1. 至少5个字符
5
+ * 2. 必须以字母开头
6
+ * 3. 只能包含字母、数字、连字符(-)
7
+ */
8
+
9
+ /**
10
+ * 验证文件名是否符合规范
11
+ * @param {string} ProjectName 文件名(不包含路径和扩展名)
12
+ * @returns {Object} 验证结果对象
13
+ */
14
+ function validateProjectName(ProjectName) {
15
+ const errors = [];
16
+
17
+ // 检查文件名是否为空
18
+ if (!ProjectName || ProjectName.trim().length === 0) {
19
+ errors.push('文件名不能为空');
20
+ return { isValid: false, errors };
21
+ }
22
+
23
+ // 检查长度是否至少5个字符
24
+ if (ProjectName.length < 5) {
25
+ errors.push(`文件名长度必须至少5个字符,当前长度: ${ProjectName.length}`);
26
+ }
27
+
28
+ // 检查是否以字母开头
29
+ if (!/^[a-zA-Z]/.test(ProjectName)) {
30
+ errors.push('文件名必须以字母开头');
31
+ }
32
+
33
+ // 检查是否只包含字母、数字、连字符
34
+ if (!/^[a-zA-Z][a-zA-Z0-9-]*$/.test(ProjectName)) {
35
+ errors.push('文件名只能包含字母、数字和连字符(-),且必须以字母开头');
36
+ }
37
+
38
+ // 检查是否包含连续连字符
39
+ if (ProjectName.includes('--')) {
40
+ errors.push('文件名不能包含连续连字符(--)');
41
+ }
42
+
43
+ // 检查是否以连字符结尾
44
+ if (ProjectName.endsWith('-')) {
45
+ errors.push('文件名不能以连字符结尾');
46
+ }
47
+
48
+ return {
49
+ isValid: errors.length === 0,
50
+ errors
51
+ };
52
+ }
53
+
54
+ /**
55
+ * 验证文件名并返回布尔值
56
+ * @param {string} ProjectName 文件名
57
+ * @returns {boolean} 是否有效
58
+ */
59
+ function isValidProjectName(ProjectName) {
60
+ return validateProjectName(ProjectName).isValid;
61
+ }
62
+
63
+ /**
64
+ * 获取文件名验证的错误信息
65
+ * @param {string} ProjectName 文件名
66
+ * @returns {Array} 错误信息数组
67
+ */
68
+ function getProjectNameErrors(ProjectName) {
69
+ return validateProjectName(ProjectName).errors;
70
+ }
71
+
72
+ module.exports = {
73
+ validateProjectName,
74
+ isValidProjectName,
75
+ getProjectNameErrors
76
+ };
@@ -0,0 +1,47 @@
1
+ const fs = require('fs-extra');
2
+ const path = require('path');
3
+
4
+ /**
5
+ * 全局替换项目中的指定字符串
6
+ * @param dir 项目目录
7
+ * @param oldStr 旧字符串
8
+ * @param newStr 新字符串
9
+ * @param fileExtensions 文件扩展名
10
+ */
11
+ function replaceInFiles(
12
+ dir,
13
+ oldStr,
14
+ newStr,
15
+ fileExtensions = ['.js', '.jsx', '.ts', '.tsx', '.vue', '.html', '.css', '.scss', '.json']
16
+ ) {
17
+ try {
18
+ // 读取指定目录下的所有文件
19
+ const files = fs.readdirSync(dir);
20
+
21
+ // 遍历所有文件
22
+ files.forEach((file) => {
23
+ const filePath = path.join(dir, file);
24
+ // 获取文件状态
25
+ const stat = fs.statSync(filePath);
26
+ // 如果文件是目录,则递归处理子目录
27
+ if (stat.isDirectory() && !file.startsWith('.') && file !== 'node_modules') {
28
+ // 递归处理子目录
29
+ replaceInFiles(filePath, oldStr, newStr, fileExtensions);
30
+ } else if (stat.isFile()) {
31
+ // 检查文件扩展名
32
+ const ext = path.extname(file);
33
+ if (fileExtensions.includes(ext)) {
34
+ let content = fs.readFileSync(filePath, 'utf8');
35
+ if (content.includes(oldStr)) {
36
+ content = content.replace(new RegExp(oldStr, 'g'), newStr); // 全局替换
37
+ fs.writeFileSync(filePath, content, 'utf8');
38
+ }
39
+ }
40
+ }
41
+ });
42
+ } catch (error) {
43
+ console.error('replaceInFiles 运行失败:', error);
44
+ }
45
+ }
46
+
47
+ module.exports = replaceInFiles;