Skip to content

Commit 487af76

Browse files
eternalskyJackLian
authored andcommitted
feat(build-plugin-lowcode): 支持 lowcode 文件夹的构建输出
1 parent b9c55fa commit 487af76

File tree

9 files changed

+370
-77
lines changed

9 files changed

+370
-77
lines changed

packages/build-plugin-lowcode/README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
## 使用文档
44

5-
```
5+
```ts
66
export interface LowcodeOptions {
77
builtinAssets?: Array<string|Assets>; // 会作为内置资产保存到构建产物中
88
extraAssets?: Array<string|Assets>; // 只在调试环境加载到资产包中
@@ -21,11 +21,11 @@ export interface SetterMap {
2121
## 开发调试
2222
### 组件开发
2323

24-
`demo/component` 目录下是测试组件的项目,改项目引用了 build-plugin-lowcode ,相关配置在 `demo/component/build.lowcode.js` 中;
24+
`demo/component`目录下是测试组件的项目,改项目引用了 build-plugin-lowcode,相关配置在在 `demo/component/build.lowcode.js` 中;
2525

2626
可以修改 build-plugin-lowcode 的代码、修改 demo/component/build.lowcode.js 的配置进行调试;
2727

28-
```
29-
在 build-plugin-lowcode 根目录下执行启动调试环境
28+
```bash
29+
# 在 build-plugin-lowcode 根目录下执行启动调试环境
3030
npm run component:dev
3131
```

packages/build-plugin-lowcode/package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,20 @@
2424
"@alilc/lowcode-react-renderer": "^1.0.1",
2525
"@alilc/lowcode-types": "^1.0.1",
2626
"@alilc/lowcode-utils": "^1.0.1",
27+
"@babel/core": "^7.22.5",
2728
"axios": "^0.21.4",
29+
"build-plugin-component": "^1.12.0",
2830
"build-scripts-config": "^3.0.3",
2931
"chokidar": "^3.5.3",
3032
"cross-spawn-promise": "^0.10.2",
3133
"driver-universal": "^3.4.0",
34+
"fs-extra": "^11.1.1",
3235
"glob": "^7.1.7",
3336
"handlebars": "^4.4.0",
3437
"html-webpack-plugin": "^3.2.0",
3538
"is-wsl": "^2.2.0",
3639
"lodash": "^4.17.21",
40+
"rax-babel-config": "^2.0.4",
3741
"rpx-loader": "^1.0.1",
3842
"style-loader": "^2.0.0",
3943
"webpack": "^4.46.0"
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
const glob = require("glob");
2+
const babel = require("@babel/core");
3+
const path = require("path");
4+
const fs = require("fs-extra");
5+
const getRaxBabelConfig = require("rax-babel-config");
6+
const getCompileBabel = require("build-plugin-component/src/utils/getCompileBabel");
7+
const dts = require('build-plugin-component/src/compiler/dts');
8+
9+
const defaultDynamicImportLibraries = [
10+
"antd",
11+
"@alifd/next",
12+
"@alife/next",
13+
"@icedesign/base",
14+
];
15+
16+
const getBabelConfig = ({
17+
target,
18+
componentLibs = defaultDynamicImportLibraries,
19+
rootDir,
20+
babelPlugins,
21+
babelOptions,
22+
type,
23+
alias,
24+
root = 'lowcode',
25+
}) => {
26+
const params = target === "es" ? { modules: false } : {};
27+
let babelConfig;
28+
if (type === "react") {
29+
babelConfig = getCompileBabel(params, {
30+
babelPlugins,
31+
babelOptions,
32+
rootDir,
33+
});
34+
} else {
35+
babelConfig = getRaxBabelConfig({
36+
// Be careful~ change it's value by inlineStyle may cause break-change
37+
styleSheet: true,
38+
custom: {
39+
ignore: ["**/**/*.d.ts"],
40+
},
41+
...params,
42+
});
43+
babelConfig.presets.push([
44+
require.resolve("@babel/preset-typescript"),
45+
{ jsxPragma: "createElement" },
46+
]);
47+
48+
babelConfig.plugins = [...babelConfig.plugins, ...(babelPlugins || [])];
49+
}
50+
// generate babel-plugin-import config
51+
const plugins = [];
52+
componentLibs.forEach((libraryName) => {
53+
// check es folder if target is es
54+
const pluginOption = {
55+
libraryName,
56+
style: false, // style file will be require in style.js
57+
};
58+
if (target === "es") {
59+
["es", "esm"].some((item) => {
60+
const dirPath = path.join(rootDir, "node_modules", libraryName, item);
61+
const dirExist = fs.existsSync(dirPath);
62+
63+
if (dirExist) {
64+
pluginOption.libraryDirectory = item;
65+
}
66+
67+
return dirExist;
68+
});
69+
}
70+
plugins.push([
71+
require.resolve("babel-plugin-import"),
72+
pluginOption,
73+
libraryName,
74+
]);
75+
});
76+
babelConfig.plugins = babelConfig.plugins.concat(plugins);
77+
if (alias) {
78+
const aliasRelative = {};
79+
Object.keys(alias).forEach((aliasKey) => {
80+
aliasRelative[aliasKey] = alias[aliasKey].startsWith("./")
81+
? alias[aliasKey]
82+
: `./${alias[aliasKey]}`;
83+
});
84+
babelConfig.plugins = babelConfig.plugins.concat([
85+
[
86+
require.resolve("babel-plugin-module-resolver"),
87+
{
88+
root: [root],
89+
alias: aliasRelative,
90+
},
91+
],
92+
]);
93+
}
94+
return babelConfig;
95+
};
96+
97+
const findGitIgnorePath = (rootDir) => {
98+
let dir = rootDir;
99+
let gitignorePath;
100+
while (dir !== '/') {
101+
const tempPath = path.join(dir, ".gitignore")
102+
const fileExists = fs.pathExistsSync(tempPath);
103+
if (fileExists) {
104+
gitignorePath = tempPath;
105+
break;
106+
} else {
107+
dir = path.dirname(dir);
108+
}
109+
}
110+
return gitignorePath;
111+
}
112+
113+
const reg = {
114+
REG_TS: /\.(tsx?)$/,
115+
REG_D_TS: /\.d\.ts$/,
116+
117+
REG_JS: /\.(jsx?|tsx?)$/,
118+
REG_SASS: /\.(sa|sc|c)ss$/,
119+
REG_LESS: /\.(le|c)ss$/,
120+
121+
REG_JS_INDEX: /index\.(jsx?|tsx?)$/,
122+
REG_SASS_INDEX: /index\.(sa|sc|c)ss$/,
123+
REG_LESS_INDEX: /index\.(le|c)ss$/,
124+
};
125+
126+
127+
const babelCompile = async ({
128+
source,
129+
target,
130+
rootDir,
131+
userOptions,
132+
type = "react",
133+
}) => {
134+
const { REG_SASS, REG_LESS, REG_JS, REG_D_TS } = reg;
135+
const filesPath = glob.sync("**/*.*", {
136+
cwd: source,
137+
ignore: ["node_modules/**"],
138+
});
139+
const compileInfo = [];
140+
['lib', 'es'].forEach((target) => {
141+
const targetPath = `${source}_${target}`;
142+
const distDirPath = path.join(rootDir, targetPath);
143+
const { babelPlugins = [], babelOptions = [], alias } = userOptions;
144+
fs.removeSync(distDirPath);
145+
fs.ensureDirSync(distDirPath);
146+
filesPath.forEach((filePath) => {
147+
const sourceFile = path.join(rootDir, source, filePath);
148+
if (!REG_JS.test(filePath) || REG_D_TS.test(filePath)) {
149+
// copy file if it does not match REG_JS
150+
try {
151+
fs.copySync(sourceFile, path.join(distDirPath, filePath));
152+
console.log(`file ${filePath} copied`);
153+
} catch (err) {
154+
throw new Error(err);
155+
}
156+
} else {
157+
const distFile = path.join(distDirPath, filePath.replace(REG_JS, ".js"));
158+
const babelConfig = getBabelConfig({
159+
target,
160+
rootDir,
161+
babelOptions,
162+
babelPlugins,
163+
type,
164+
alias,
165+
});
166+
const { code } = babel.transformFileSync(sourceFile, {
167+
filename: distFile,
168+
...babelConfig,
169+
});
170+
fs.ensureDirSync(path.dirname(distFile));
171+
fs.writeFileSync(distFile, code, "utf-8");
172+
compileInfo.push({
173+
filePath,
174+
sourceFile,
175+
destPath: distDirPath,
176+
});
177+
}
178+
});
179+
// 检查 .gitignore 如果没有产出路径,则增加该配置
180+
const gitignorePath = findGitIgnorePath(rootDir);
181+
if (gitignorePath) {
182+
const gitignoreFile = fs.readFileSync(gitignorePath, "utf-8");
183+
if (!(new RegExp(`${targetPath}/`)).test(gitignoreFile)) {
184+
const newGitignoreFile = `${targetPath}/\r\n${gitignoreFile}`;
185+
fs.writeFileSync(gitignorePath, newGitignoreFile);
186+
}
187+
}
188+
});
189+
// 生成声明文件
190+
dts(compileInfo, {
191+
log: console,
192+
})
193+
};
194+
195+
babelCompile.getBabelConfig = getBabelConfig;
196+
module.exports = babelCompile;
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/** 生成 lowcode 的入口文件(meta.js, view.js) */
2+
const fs = require("fs-extra");
3+
const path = require("path");
4+
const babel = require("@babel/core");
5+
const { getBabelConfig } = require("./babel");
6+
const { glob } = require("glob");
7+
8+
const getAvailableFileName = ({ fileName, lowcodeDir, rootDir }) => {
9+
let finalName = fileName;
10+
11+
while (true) {
12+
const files = glob.sync(`${finalName}.*`, {
13+
cwd: lowcodeDir,
14+
ignore: ["node_modules/**"],
15+
});
16+
const fileHasExists = files.some((f) => {
17+
return /\.j|tsx?$/.test(f);
18+
});
19+
if (fileHasExists) {
20+
finalName = `${finalName}_entry`
21+
} else {
22+
break;
23+
}
24+
}
25+
return finalName;
26+
};
27+
28+
module.exports = async ({
29+
rootDir,
30+
tmpDir = ".tmp",
31+
lowcodeDir = "lowcode",
32+
userOptions,
33+
type = "react",
34+
package,
35+
}) => {
36+
const { babelPlugins = [], babelOptions = [], alias } = userOptions;
37+
const exportsData = {
38+
"./prototype": {},
39+
"./prototypeView": {},
40+
};
41+
const targetExportsMap = {
42+
'lib': 'require',
43+
'es': 'import',
44+
};
45+
const fileNameEntryMap = {
46+
meta: './prototype',
47+
view: './prototypeView',
48+
};
49+
['lib', 'es'].forEach((target) => {
50+
const babelConfigOptions = {
51+
target,
52+
babelOptions,
53+
babelPlugins,
54+
type,
55+
alias,
56+
rootDir,
57+
};
58+
const babelConfig = getBabelConfig(babelConfigOptions);
59+
["meta", "view"].forEach((fileName) => {
60+
const filePath = path.join(rootDir, tmpDir, `${fileName}.js`);
61+
let fileContent = fs.readFileSync(filePath, "utf-8");
62+
fileContent = fileContent
63+
.replace(new RegExp(path.join(rootDir, "lowcode"), "g"), ".")
64+
.replace(new RegExp(path.join(rootDir, "src"), "g"), `../${target}`)
65+
.replace(/\.ts(x)?('|")/g, "$2")
66+
.replace(/\\\\/g, "/");
67+
const targetPath = `${lowcodeDir}_${target}`;
68+
if (!package.files.includes(`${targetPath}/`)) {
69+
package.files.push(`${targetPath}/`);
70+
}
71+
const entryName = getAvailableFileName({ fileName, lowcodeDir, rootDir });
72+
exportsData[fileNameEntryMap[fileName]][targetExportsMap[target]] = `${targetPath}/${entryName}`;
73+
const distFilePath = path.join(rootDir, targetPath, `${entryName}.js`);
74+
const { code } = babel.transformSync(fileContent, {
75+
filename: distFilePath,
76+
...babelConfig,
77+
});
78+
fs.ensureDirSync(path.dirname(distFilePath));
79+
fs.writeFileSync(distFilePath, code, "utf-8");
80+
});
81+
});
82+
package.exports = {
83+
...exportsData,
84+
...package.exports,
85+
};
86+
};

0 commit comments

Comments
 (0)