1 | import * as fs from "fs-extra";
|
2 | import * as path from "path";
|
3 | import {
|
4 | IClientPackageConfig,
|
5 | ILibraryPackageConfig,
|
6 | IProjectConfig,
|
7 | IServerPackageConfig
|
8 | } from "../commons/IProjectConfig";
|
9 | import {LocalUpdater} from "../builders/LocalUpdater";
|
10 | import {LibraryPackageBuilder} from "../builders/LibraryPackageBuilder";
|
11 | import {Logger, Wait} from "@simplism/core";
|
12 | import {ServerPackageBuilder} from "../builders/ServerPackageBuilder";
|
13 | import {ClientPackageBuilder} from "../builders/ClientPackageBuilder";
|
14 | import * as semver from "semver";
|
15 | import * as os from "os";
|
16 | import * as child_process from "child_process";
|
17 | import {FileWatcher} from "../utils/FileWatcher";
|
18 |
|
19 | export async function buildAsync(argv: { watch: boolean; package?: string; config?: string; env?: any }): Promise<void> {
|
20 | process.env.NODE_ENV = argv.watch ? "development" : "production";
|
21 |
|
22 | let configFilePath = argv.config;
|
23 | configFilePath = configFilePath ? path.resolve(process.cwd(), configFilePath)
|
24 | : fs.existsSync(path.resolve(process.cwd(), "simplism.ts")) ? path.resolve(process.cwd(), "simplism.ts")
|
25 | : fs.existsSync(path.resolve(process.cwd(), "simplism.js")) ? path.resolve(process.cwd(), "simplism.js")
|
26 | : path.resolve(process.cwd(), "simplism.json");
|
27 |
|
28 | if (path.extname(configFilePath) === ".ts") {
|
29 |
|
30 | require("ts-node/register");
|
31 | Object.assign(process.env, argv.env);
|
32 | }
|
33 |
|
34 |
|
35 | const projectConfig = eval("require(configFilePath)") as IProjectConfig;
|
36 |
|
37 | let promiseList: Promise<void>[] = [];
|
38 | if (argv.watch && projectConfig.localDependencies) {
|
39 | for (const packageName of Object.keys(projectConfig.localDependencies)) {
|
40 | const packagePath = projectConfig.localDependencies[packageName];
|
41 |
|
42 | if (fs.existsSync(packagePath)) {
|
43 | promiseList.push(new LocalUpdater(packageName, packagePath).runAsync(true));
|
44 | }
|
45 | }
|
46 | }
|
47 | await Promise.all(promiseList);
|
48 | promiseList = [];
|
49 |
|
50 | const runAsync = async (config: ILibraryPackageConfig | IClientPackageConfig | IServerPackageConfig) => {
|
51 | if (!argv.watch) {
|
52 | if (config.type === "client") {
|
53 | await new ClientPackageBuilder({
|
54 | ...config,
|
55 | "env": {...projectConfig.env, ...config.env, ...argv.env},
|
56 | "env.production": {...projectConfig["env.production"], ...config["env.production"]}
|
57 | }).buildAsync();
|
58 | }
|
59 | else if (config.type === "server") {
|
60 | await new ServerPackageBuilder({
|
61 | ...config,
|
62 | "env": {...projectConfig.env, ...config.env, ...argv.env},
|
63 | "env.production": {...projectConfig["env.production"], ...config["env.production"]}
|
64 | }).buildAsync();
|
65 | }
|
66 | else {
|
67 | await new LibraryPackageBuilder(config).buildAsync();
|
68 | }
|
69 | }
|
70 | else {
|
71 | if (config.type === "client") {
|
72 | await new ClientPackageBuilder({
|
73 | ...config,
|
74 | "env": {...projectConfig.env, ...config.env, ...argv.env},
|
75 | "env.development": {...projectConfig["env.development"], ...config["env.development"]}
|
76 | }).watchAsync();
|
77 | }
|
78 | else if (config.type === "server") {
|
79 | await new ServerPackageBuilder({
|
80 | ...config,
|
81 | "env": {...projectConfig.env, ...config.env, ...argv.env},
|
82 | "env.development": {...projectConfig["env.development"], ...config["env.development"]}
|
83 | }).watchAsync();
|
84 | }
|
85 | else {
|
86 | await new LibraryPackageBuilder(config).watchAsync();
|
87 | }
|
88 | }
|
89 | };
|
90 |
|
91 | const _loadersPath = (...args: string[]): string => {
|
92 | return fs.existsSync(path.resolve(process.cwd(), "node_modules/@simplism/cli/loaders"))
|
93 | ? path.resolve(process.cwd(), "node_modules/@simplism/cli/loaders", ...args)
|
94 | : path.resolve(__dirname, "../../loaders", ...args);
|
95 | };
|
96 |
|
97 | const lintAsync = async (packName: string) => {
|
98 | const logger = new Logger("@simplism/cli", `${packName}(LINT):`);
|
99 |
|
100 | logger.log("코드검사...");
|
101 | let worker: child_process.ChildProcess;
|
102 | await new Promise<void>((resolve, reject) => {
|
103 | worker = child_process.fork(
|
104 | _loadersPath("ts-lint-worker.js"),
|
105 | [
|
106 | packName,
|
107 | argv.watch ? "watch" : "build",
|
108 | path.resolve(process.cwd(), "packages", packName, "tsconfig.json")
|
109 | ].filterExists(),
|
110 | {
|
111 | stdio: [undefined, undefined, undefined, "ipc"]
|
112 | }
|
113 | );
|
114 | worker.on("message", message => {
|
115 | if (message === "finish") {
|
116 | logger.info("코드검사 완료");
|
117 | resolve();
|
118 | }
|
119 | else {
|
120 | logger.warn("코드검사 경고 발생", message);
|
121 | }
|
122 | });
|
123 |
|
124 | worker.send([], err => {
|
125 | if (err) {
|
126 | reject(err);
|
127 | }
|
128 | });
|
129 | });
|
130 |
|
131 | if (argv.watch) {
|
132 | await FileWatcher.watch(path.resolve(process.cwd(), "packages", packName, "src*.ts"), ["add", "change"], files => {
|
133 | try {
|
134 | worker.send(files.map(item => item.filePath));
|
135 | }
|
136 | catch (err) {
|
137 | logger.error(err);
|
138 | }
|
139 | });
|
140 | }
|
141 | };
|
142 |
|
143 | if (!argv.watch) {
|
144 | // 최상위 package.json 설정 가져오기
|
145 | const rootPackageJsonPath = path.resolve(process.cwd(), "package.json");
|
146 | const rootPackageJson = fs.readJsonSync(rootPackageJsonPath);
|
147 |
|
148 | // 프로젝트의 버전 업
|
149 | rootPackageJson.version = semver.inc(rootPackageJson.version, "patch")!;
|
150 |
|
151 | for (const pack of projectConfig.packages) {
|
152 | // package.json 설정 가져오기
|
153 | const packageJsonPath = path.resolve(process.cwd(), `packages/${pack.name}`, "package.json");
|
154 | const packageJson = fs.readJsonSync(packageJsonPath);
|
155 |
|
156 | // 최상위 package.json 에서 버전 복사
|
157 | packageJson.version = rootPackageJson.version;
|
158 |
|
159 | // 최상위 package.json 에서 Repository 복사
|
160 | packageJson.repository = rootPackageJson.repository;
|
161 |
|
162 | // 의존성 버전 재구성
|
163 | const depTypeNames = ["dependencies", "peerDependencies", "optionalDependencies"];
|
164 | for (const depTypeName of depTypeNames) {
|
165 | for (const depName of Object.keys(packageJson[depTypeName] || {})) {
|
166 | if (depName.startsWith("@" + rootPackageJson.name)) {
|
167 | packageJson[depTypeName][depName] = `~${rootPackageJson.version}`;
|
168 | }
|
169 | else if ({...rootPackageJson.dependencies, ...rootPackageJson.devDependencies}[depName]) {
|
170 | packageJson[depTypeName][depName] = {...rootPackageJson.dependencies, ...rootPackageJson.devDependencies}[depName];
|
171 | }
|
172 | else {
|
173 | throw new Error(`'${pack.name}'패키지의 의존성 패키지인 "${depName}" 정보가 루트 패키지에 없습니다.`);
|
174 | }
|
175 | }
|
176 | }
|
177 |
|
178 | // 최상위 package.json 파일 다시쓰기
|
179 | fs.writeJsonSync(rootPackageJsonPath, rootPackageJson, {spaces: 2, EOL: os.EOL});
|
180 |
|
181 | // package.json 파일 다시쓰기
|
182 | fs.writeJsonSync(packageJsonPath, packageJson, {spaces: 2, EOL: os.EOL});
|
183 | }
|
184 | }
|
185 |
|
186 | if (!argv.package) {
|
187 | for (const pack of projectConfig.packages) {
|
188 | promiseList.push(lintAsync(pack.name));
|
189 | }
|
190 | }
|
191 | else {
|
192 | for (const packName of argv.package.split(",")) {
|
193 | promiseList.push(lintAsync(packName));
|
194 | }
|
195 | }
|
196 |
|
197 | if (!argv.package) {
|
198 | const packageConfigs = (
|
199 | argv.package
|
200 | ? projectConfig.packages.filter(item => argv.package!.split(",").includes(item.name))
|
201 | : projectConfig.packages
|
202 | )
|
203 | .map(pack => ({
|
204 | ...pack,
|
205 | config: fs.readJsonSync(path.resolve(process.cwd(), "packages", pack.name, "package.json"))
|
206 | }));
|
207 |
|
208 | const completedPackNames: string[] = [];
|
209 |
|
210 | for (const pack of argv.package ? argv.package.split(",").map(item => packageConfigs.single(item1 => item1.name === item)!) : projectConfig.packages) {
|
211 | promiseList.push(
|
212 | new Promise<void>(async (resolve, reject) => {
|
213 | try {
|
214 | const thisPackageConfig = packageConfigs.single(item => item.name === pack.name)!;
|
215 | const thisPackageDependencies = {
|
216 | ...thisPackageConfig.config.peerDependencies,
|
217 | ...thisPackageConfig.config.dependencies
|
218 | };
|
219 | if (thisPackageDependencies) {
|
220 | const depPackNames = packageConfigs
|
221 | .filter(otherPackageConfig => Object.keys(thisPackageDependencies).some(depKey => otherPackageConfig.config.name === depKey))
|
222 | .map(item => item.name);
|
223 | await Wait.true(() => depPackNames.every(depPackName => completedPackNames.includes(depPackName)));
|
224 | }
|
225 |
|
226 | await runAsync(pack);
|
227 | completedPackNames.push(pack.name);
|
228 | resolve();
|
229 | }
|
230 | catch (err) {
|
231 | reject(err);
|
232 | }
|
233 | })
|
234 | );
|
235 | }
|
236 | }
|
237 | else {
|
238 | for (const packName of argv.package.split(",")) {
|
239 | promiseList.push(runAsync(projectConfig.packages.single(item => item.name === packName)!));
|
240 | }
|
241 | }
|
242 |
|
243 | await Promise.all(promiseList);
|
244 | } |
\ | No newline at end of file |