@iam-com/snack-scripts 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.
- package/README.md +2 -0
- package/bin/main.js +225 -0
- package/config/webpack.dev.config.js +183 -0
- package/config/webpack.entry.config.js +180 -0
- package/config/webpack.index.config.js +184 -0
- package/config/webpack.loader.config.js +125 -0
- package/config/webpack.snack.config.js +187 -0
- package/package.json +71 -0
- package/template/dev/App.tsx +164 -0
- package/template/dev/index.html +34 -0
- package/template/dev/index.scss +126 -0
- package/template/dev/index.tsx +66 -0
- package/template/entry/index.html +94 -0
- package/template/entry/index.scss +0 -0
- package/template/entry/index.tsx +69 -0
- package/template/env.d.ts +7 -0
- package/template/favicon.svg +1 -0
- package/template/logo.svg +1 -0
- package/template/minified.min.js +1 -0
- package/template/normalize.scss +411 -0
- package/template/proxy.min.js +12 -0
- package/template/snack/index.html +90 -0
- package/template/snack/index.tsx +45 -0
- package/tsconfig.json +27 -0
- package/utils/fs.js +169 -0
- package/utils/package.js +198 -0
package/utils/fs.js
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const nPath = require('path');
|
|
3
|
+
|
|
4
|
+
/* 获取目录或文件信息 null 为不存在 */
|
|
5
|
+
const Stat = function (path) {
|
|
6
|
+
return new Promise((resolve, reject) => {
|
|
7
|
+
fs.stat(path, (err, info) => {
|
|
8
|
+
if (err) return resolve(null);
|
|
9
|
+
return resolve(info);
|
|
10
|
+
});
|
|
11
|
+
});
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/* 是否文件夹 null 为不存在 */
|
|
15
|
+
const IsDirectory = async function (path) {
|
|
16
|
+
const info = await $stat(path);
|
|
17
|
+
if (!info) return null;
|
|
18
|
+
return info.isDirectory();
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/* 是否文件 null 为不存在 */
|
|
22
|
+
const IsFile = async function (path) {
|
|
23
|
+
const info = await $stat(path);
|
|
24
|
+
if (!info) return null;
|
|
25
|
+
return !info.isDirectory();
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/* 遍历目录 */
|
|
29
|
+
const Readdir = function (path) {
|
|
30
|
+
return new Promise((resolve, reject) => {
|
|
31
|
+
fs.readdir(path, (err, menu) => {
|
|
32
|
+
if (err) return resolve(null);
|
|
33
|
+
return resolve(menu);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
};
|
|
37
|
+
const ReadFiles = async function (dirPath) {
|
|
38
|
+
return new Promise(async (resolve, reject) => {
|
|
39
|
+
const walk = async (path, callback) => {
|
|
40
|
+
const files = await $readdir(path); //返回目录名和文件名的字符串数组
|
|
41
|
+
if (!files) return resolve([]);
|
|
42
|
+
files.forEach(async function (file) {
|
|
43
|
+
const stat = await $stat(nPath.join(path, file));
|
|
44
|
+
if (stat && stat.isFile()) {
|
|
45
|
+
const name = nPath.join(path, file).replace(dirPath, '');
|
|
46
|
+
callback({
|
|
47
|
+
name,
|
|
48
|
+
size: stat.size,
|
|
49
|
+
lastModified: stat.mtime.toLocaleString()
|
|
50
|
+
});
|
|
51
|
+
} else {
|
|
52
|
+
walk(path + '/' + file, callback); //判断文件夹,继续递归
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
let timeout, list = [];
|
|
57
|
+
walk(dirPath, function (file) {
|
|
58
|
+
list.push(file);
|
|
59
|
+
if (timeout) clearTimeout(timeout);
|
|
60
|
+
timeout = setTimeout(() => resolve(list), 200);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
/* 创建目录 */
|
|
66
|
+
const Mkdir = function (path) {
|
|
67
|
+
return new Promise((resolve, reject) => {
|
|
68
|
+
fs.ensureDir(path, (err) => {
|
|
69
|
+
if (err) return reject(err);
|
|
70
|
+
resolve(true);
|
|
71
|
+
});
|
|
72
|
+
/*fs.mkdir(path, 0x0755, function (err) {
|
|
73
|
+
if (err) {
|
|
74
|
+
return resolve(err.message.indexOf('file already exists, mkdir') !== -1);
|
|
75
|
+
}
|
|
76
|
+
return resolve(true);
|
|
77
|
+
})*/
|
|
78
|
+
});
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
/* 读取文件 */
|
|
82
|
+
const ReadFile = function (path, ops = {}) {
|
|
83
|
+
if (ops.encoding === undefined) ops.encoding = 'utf8';
|
|
84
|
+
return new Promise((resolve, reject) => {
|
|
85
|
+
fs.readFile(path, ops.encoding, (err, bytes) => {
|
|
86
|
+
try {
|
|
87
|
+
if (err) return reject(err);
|
|
88
|
+
bytes = bytes.toString();
|
|
89
|
+
if (ops.json)
|
|
90
|
+
bytes = JSON.parse(bytes);
|
|
91
|
+
return resolve(bytes);
|
|
92
|
+
} catch (e) {
|
|
93
|
+
return resolve(null);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
};
|
|
98
|
+
const ReadStream = function (path, ops = {}) {
|
|
99
|
+
return new Promise(async (resolve, reject) => {
|
|
100
|
+
if (await $isFile(path)) {
|
|
101
|
+
const stream = fs.createReadStream(path);
|
|
102
|
+
stream.on('error', (err) => {$WARN('$readStream', err);});
|
|
103
|
+
resolve(stream);
|
|
104
|
+
} else {
|
|
105
|
+
reject(new Error('file does not exist'));
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
/* 写入文件 */
|
|
111
|
+
const WriteFile = function (path, data) {
|
|
112
|
+
return new Promise((resolve, reject) => {
|
|
113
|
+
if ($getType(data) === 'object')
|
|
114
|
+
data = JSON.stringify(data, null, 4);
|
|
115
|
+
fs.outputFile(path, data, (err) => {
|
|
116
|
+
if (err) return reject(err);
|
|
117
|
+
return resolve(true);
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
/* 复制文件 */
|
|
123
|
+
const CcopyFile = function (inPath, outPath) {
|
|
124
|
+
return new Promise((resolve, reject) => {
|
|
125
|
+
fs.copy(inPath, outPath, err => {
|
|
126
|
+
if (err) return reject(err);
|
|
127
|
+
resolve(true);
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
/* 删除文件 */
|
|
133
|
+
const Unlink = function (path) {
|
|
134
|
+
return new Promise((resolve, reject) => {
|
|
135
|
+
fs.remove(path, (err) => {
|
|
136
|
+
if (err) return reject(err);
|
|
137
|
+
return resolve(true);
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const Rmdir = function (path) {
|
|
143
|
+
return new Promise((resolve, reject) => {
|
|
144
|
+
fs.remove(path, (err) => {
|
|
145
|
+
if (err) return reject(err);
|
|
146
|
+
return resolve(true);
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
module.exports = {
|
|
152
|
+
Stat,
|
|
153
|
+
IsFile,
|
|
154
|
+
IsDirectory,
|
|
155
|
+
Readdir,
|
|
156
|
+
ReadFiles,
|
|
157
|
+
Mkdir,
|
|
158
|
+
ReadFile,
|
|
159
|
+
ReadStream,
|
|
160
|
+
WriteFile,
|
|
161
|
+
CopyFile,
|
|
162
|
+
Unlink,
|
|
163
|
+
Rmdir,
|
|
164
|
+
fs
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
function $getType(obj) {
|
|
168
|
+
return Object.prototype.toString.call(obj).match(/^\[.*?\s+(.*?)\]$/)[1].toLocaleLowerCase();
|
|
169
|
+
};
|
package/utils/package.js
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
// 获取snack包列表
|
|
4
|
+
let ignoreDir = ['.DS_Store']; // ignore package filename
|
|
5
|
+
const safeParse = (str, key1, key2) => {
|
|
6
|
+
try {
|
|
7
|
+
const strJson = JSON.parse(str);
|
|
8
|
+
if(key1 || key2) return strJson[key1] || strJson[key2];
|
|
9
|
+
return strJson;
|
|
10
|
+
} catch (err) {
|
|
11
|
+
return str
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
module.exports.safeParse = safeParse;
|
|
15
|
+
|
|
16
|
+
module.exports.GetPackageList = (projectPath) => {
|
|
17
|
+
const isProduction = process.env.RUN_MODE === 'production';
|
|
18
|
+
const isDevelopment = process.env.RUN_MODE === 'development';
|
|
19
|
+
const packageJson = JSON.parse(process.env.PROJECT_PACKAGE_JSON);
|
|
20
|
+
const snackConfig = packageJson.snack || {};
|
|
21
|
+
// build 过滤打包
|
|
22
|
+
if (isProduction && snackConfig.buildIgnore instanceof Array) {
|
|
23
|
+
ignoreDir = [...ignoreDir, ...snackConfig.buildIgnore];
|
|
24
|
+
}
|
|
25
|
+
const list = fs.readdirSync(projectPath);
|
|
26
|
+
if (!list) return [];
|
|
27
|
+
const newList = [];
|
|
28
|
+
for (let i = 0, l = list.length; i < l; i++) {
|
|
29
|
+
const dirName = list[i];
|
|
30
|
+
if (ignoreDir.indexOf(dirName) !== -1) continue; // 忽略打包
|
|
31
|
+
// 开发模式下指定启动的模块,配置必须数组长度 > 1
|
|
32
|
+
if (isDevelopment && snackConfig.devPackage instanceof Array && snackConfig.devPackage.length > 0 && snackConfig.devPackage.indexOf(dirName) === -1) continue;
|
|
33
|
+
const snackPath = path.resolve(projectPath, dirName);
|
|
34
|
+
const stat = fs.statSync(snackPath);
|
|
35
|
+
if (!stat.isDirectory()) continue; // filter file
|
|
36
|
+
let snackJson;
|
|
37
|
+
try {
|
|
38
|
+
snackJson = fs.readJsonSync(path.resolve(snackPath, 'snack.json'));
|
|
39
|
+
// package.json "name" = snackType
|
|
40
|
+
snackJson.type = packageJson.name;
|
|
41
|
+
snackJson.id = dirName.toLocaleLowerCase();
|
|
42
|
+
} catch (e) {
|
|
43
|
+
throw new Error(`Package(${dirName}) "snack.json" is missing`);
|
|
44
|
+
}
|
|
45
|
+
if(process.env.RUN_MODE === 'development') {
|
|
46
|
+
snackJson.name = safeParse(snackJson.name, 'zh', 'zh-CN') ;
|
|
47
|
+
snackJson.description = safeParse(snackJson.description, 'zh', 'zh-CN');
|
|
48
|
+
}
|
|
49
|
+
/* check snack.json */
|
|
50
|
+
if (!snackJson.name) {
|
|
51
|
+
throw new Error(`Package(${dirName}) "snack.json" "name" is required`);
|
|
52
|
+
}
|
|
53
|
+
if (!snackJson.version) {
|
|
54
|
+
throw new Error(`Package(${dirName}) "snack.json" "version" is required`);
|
|
55
|
+
} else {
|
|
56
|
+
const spiltVer = snackJson.version.split('.');
|
|
57
|
+
if (spiltVer.length !== 4) {
|
|
58
|
+
throw new Error(`Package(${dirName}) "snack.json" "version" must be "x.x.x.x" `);
|
|
59
|
+
}
|
|
60
|
+
if (isNaN(spiltVer.join(''))) {
|
|
61
|
+
throw new Error(`Package(${dirName}) "snack.json" "version" must be number `);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/* check entry */
|
|
65
|
+
const entry = path.resolve(snackPath, 'index.ts');
|
|
66
|
+
const entryExists = fs.existsSync(entry);
|
|
67
|
+
if (!entryExists) throw new Error(`Package(${dirName}) entry "${entryExists}" is missing`);
|
|
68
|
+
const runtimeEntry = path.resolve(snackPath, 'src', 'index.tsx');
|
|
69
|
+
const runtimeEntryExists = fs.existsSync(runtimeEntry);
|
|
70
|
+
if (!runtimeEntryExists) throw new Error(`Package(${dirName}) entry "${runtimeEntry}" is missing`);
|
|
71
|
+
newList.push({
|
|
72
|
+
name: dirName.toLocaleLowerCase(),
|
|
73
|
+
dirName,
|
|
74
|
+
info: snackJson,
|
|
75
|
+
entry: entry.replace(/\\/g, () => {
|
|
76
|
+
return '\\/';
|
|
77
|
+
}), // 总入口
|
|
78
|
+
runtimeEntry: runtimeEntry.replace(/\\/g, () => {
|
|
79
|
+
return '\\/';
|
|
80
|
+
}) // 运行时入口
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
return newList;
|
|
84
|
+
};
|
|
85
|
+
// replace requrie "define" function to "snackdefine"
|
|
86
|
+
module.exports.SnackPlugin = class SnackPlugin {
|
|
87
|
+
// 在构造函数中获取用户给该插件传入的配置
|
|
88
|
+
constructor(options) {
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Webpack 会调用 BasicPlugin 实例的 apply 方法给插件实例传入 compiler 对象
|
|
92
|
+
apply(compiler) {
|
|
93
|
+
const packageJson = JSON.parse(process.env.PROJECT_PACKAGE_JSON);
|
|
94
|
+
const packageList = JSON.parse(process.env.PROJECT_PACKAGE_LIST);
|
|
95
|
+
compiler.hooks.emit.tapAsync('MyExampleWebpackPlugin', (compilation, callback) => {
|
|
96
|
+
Object.keys(compilation.assets).forEach((key) => {
|
|
97
|
+
const item = compilation.assets[key];
|
|
98
|
+
const ops = {
|
|
99
|
+
name: key.split('/')[0], packageJson, packageList
|
|
100
|
+
};
|
|
101
|
+
if (typeof item._cachedSource === 'string') {
|
|
102
|
+
item._cachedSource = replaceStr(item._cachedSource, ops);
|
|
103
|
+
} else if (typeof item._valueAsString === 'string') {
|
|
104
|
+
item._valueAsString = replaceStr(item._valueAsString, ops);
|
|
105
|
+
} else if (item._source && item._source._children && item._source._children[1] && typeof item._source._children[1]._value === 'string') {
|
|
106
|
+
item._source._children[1]._value = replaceStr(item._source._children[1]._value, ops);
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
callback();
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
function replaceStr(str, ops) {
|
|
115
|
+
//key.split('/')[0]
|
|
116
|
+
const getInfo = (name) => {
|
|
117
|
+
for (let i = 0, l = ops.packageList.length; i < l; i++) {
|
|
118
|
+
const item = ops.packageList[i];
|
|
119
|
+
if (item.name === name) return {
|
|
120
|
+
dirName: item.dirName, ...item.info
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
const info = getInfo(ops.name);
|
|
125
|
+
if (!info) return str;
|
|
126
|
+
const snackPath = `window["__snack_static_${process.env.PROJECT_TYPE}__"] ? window["__snack_static_${process.env.PROJECT_TYPE}__"] : ""`;
|
|
127
|
+
str = str
|
|
128
|
+
.replace(/\bdefine\b/g, 'snackdefine')
|
|
129
|
+
.replace('"{__snack_static__}"', snackPath);
|
|
130
|
+
|
|
131
|
+
let infoName = safeParse(info.name, 'zh', 'zh-CN');
|
|
132
|
+
let pkgName = safeParse(ops.packageJson.name, 'zh', 'zh-CN')
|
|
133
|
+
// 添加console输出
|
|
134
|
+
str += `
|
|
135
|
+
(function(){
|
|
136
|
+
try{
|
|
137
|
+
console.groupCollapsed(
|
|
138
|
+
"%c SnackModule: ${infoName}(${info.dirName}) %c v${info.version}",
|
|
139
|
+
"color: white; background: forestgreen;line-height: 20px;height:20px;border: 1px solid forestgreen;border-right: none;border-radius: 3px 0 0 3px;",
|
|
140
|
+
"color: forestgreen; background: white; font-weight: 700;line-height: 20px;height:20px;border: 1px solid forestgreen;border-left: none;border-radius: 0 3px 3px 0;padding-right: 10px;padding-left: 4px"
|
|
141
|
+
);
|
|
142
|
+
console.log("@iam-com/snack-scripts: v${process.env.SNACK_CLI_VERSION}");
|
|
143
|
+
console.log("Project: v${ops.packageJson.version}");
|
|
144
|
+
console.log("Type: ${pkgName}");
|
|
145
|
+
console.log("Author: ${info.author}");
|
|
146
|
+
console.log("buildTime: ${new Date().toLocaleString()}");
|
|
147
|
+
console.groupEnd();
|
|
148
|
+
} catch (e){
|
|
149
|
+
console.log("SnackModule: ${infoName}(${info.dirName}) v${info.version}");
|
|
150
|
+
}
|
|
151
|
+
})();`;
|
|
152
|
+
return str;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
module.exports.injectIndexFile = (templatePath, hook) => {
|
|
156
|
+
const snackConfig = JSON.parse(process.env.PROJECT_SNACK_CONFIG);
|
|
157
|
+
const filePath = path.join(templatePath, 'index.tsx');
|
|
158
|
+
let jsFile = fs.readFileSync(filePath, {encoding: 'utf8'});
|
|
159
|
+
if (snackConfig.plugin) {
|
|
160
|
+
// 外挂文件
|
|
161
|
+
const pluginJs = snackConfig.plugin.js;
|
|
162
|
+
const pluginCss = snackConfig.plugin.css;
|
|
163
|
+
// 外部自定义样式
|
|
164
|
+
if (pluginJs) {
|
|
165
|
+
const p = `../../../${pluginJs.replace(/^(\/|\.\/)/, '')}`;
|
|
166
|
+
jsFile = jsFile.replace('/** import plugin js **/', `import '${p}'`);
|
|
167
|
+
console.log('外挂JS: ', path.resolve(templatePath, p));
|
|
168
|
+
}
|
|
169
|
+
if (pluginCss) {
|
|
170
|
+
const p = `../../../${pluginCss.replace(/^(\/|\.\/)/, '')}`;
|
|
171
|
+
jsFile = jsFile.replace('/** import plugin css **/', `import '${p}'`);
|
|
172
|
+
console.log('外挂CSS: ', path.resolve(templatePath, p));
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
if (typeof hook === 'function') jsFile = hook(jsFile);
|
|
176
|
+
fs.outputFileSync(filePath, jsFile);
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
module.exports.createExternals = (packageJson, packagePath) => {
|
|
180
|
+
const externals = packageJson.snack && packageJson.snack.externals || {};
|
|
181
|
+
if (externals['@para-ui'] === 'all') {
|
|
182
|
+
const corePath = path.join(packagePath, 'node_modules/@iam-com/ui-core');
|
|
183
|
+
const coreList = fs.readdirSync(corePath);
|
|
184
|
+
for (let i = 0, l = coreList.length; i < l; i++) {
|
|
185
|
+
const name = coreList[i];
|
|
186
|
+
if (!fs.statSync(path.join(corePath, name)).isDirectory()) continue;
|
|
187
|
+
externals[`@iam-com/ui-core/${name}`] = `@iam-com/ui-core/${name}`;
|
|
188
|
+
}
|
|
189
|
+
const iconsPath = path.join(packagePath, 'node_modules/@para-ui/icons');
|
|
190
|
+
const iconsList = fs.readdirSync(iconsPath);
|
|
191
|
+
for (let i = 0, l = iconsList.length; i < l; i++) {
|
|
192
|
+
const name = iconsList[i];
|
|
193
|
+
if (!fs.statSync(path.join(iconsPath, name)).isDirectory()) continue;
|
|
194
|
+
externals[`@para-ui/icons/${name}`] = `@para-ui/icons/${name}`;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return externals;
|
|
198
|
+
}
|