@simplysm/sd-cli 14.0.19 → 14.0.22
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/dist/angular/vite-postcss-inline-plugin.d.ts.map +1 -1
- package/dist/angular/vite-postcss-inline-plugin.js +4 -1
- package/dist/angular/vite-postcss-inline-plugin.js.map +1 -1
- package/dist/capacitor/capacitor-android.d.ts +16 -0
- package/dist/capacitor/capacitor-android.d.ts.map +1 -0
- package/dist/capacitor/capacitor-android.js +289 -0
- package/dist/capacitor/capacitor-android.js.map +1 -0
- package/dist/capacitor/capacitor.d.ts +0 -49
- package/dist/capacitor/capacitor.d.ts.map +1 -1
- package/dist/capacitor/capacitor.js +4 -244
- package/dist/capacitor/capacitor.js.map +1 -1
- package/dist/commands/check.js +2 -2
- package/dist/commands/check.js.map +1 -1
- package/dist/commands/lint.d.ts +1 -42
- package/dist/commands/lint.d.ts.map +1 -1
- package/dist/commands/lint.js +1 -151
- package/dist/commands/lint.js.map +1 -1
- package/dist/commands/publish.d.ts.map +1 -1
- package/dist/commands/publish.js +2 -1
- package/dist/commands/publish.js.map +1 -1
- package/dist/commands/typecheck.d.ts +3 -40
- package/dist/commands/typecheck.d.ts.map +1 -1
- package/dist/commands/typecheck.js +3 -232
- package/dist/commands/typecheck.js.map +1 -1
- package/dist/electron/electron.js +11 -4
- package/dist/electron/electron.js.map +1 -1
- package/dist/engines/ViteEngine.js +1 -1
- package/dist/engines/ViteEngine.js.map +1 -1
- package/dist/engines/types.d.ts +2 -0
- package/dist/engines/types.d.ts.map +1 -1
- package/dist/infra/ResultCollector.d.ts +2 -2
- package/dist/infra/ResultCollector.d.ts.map +1 -1
- package/dist/infra/ResultCollector.js +1 -1
- package/dist/orchestrators/DevWatchOrchestrator.d.ts +2 -1
- package/dist/orchestrators/DevWatchOrchestrator.d.ts.map +1 -1
- package/dist/orchestrators/DevWatchOrchestrator.js +28 -16
- package/dist/orchestrators/DevWatchOrchestrator.js.map +1 -1
- package/dist/orchestrators/TypecheckOrchestrator.d.ts +74 -0
- package/dist/orchestrators/TypecheckOrchestrator.d.ts.map +1 -0
- package/dist/orchestrators/TypecheckOrchestrator.js +285 -0
- package/dist/orchestrators/TypecheckOrchestrator.js.map +1 -0
- package/dist/sd-cli.js +6 -1
- package/dist/sd-cli.js.map +1 -1
- package/dist/utils/lint-core.d.ts +43 -0
- package/dist/utils/lint-core.d.ts.map +1 -0
- package/dist/utils/lint-core.js +154 -0
- package/dist/utils/lint-core.js.map +1 -0
- package/dist/utils/lint-utils.d.ts +1 -1
- package/dist/utils/lint-utils.d.ts.map +1 -1
- package/dist/utils/output-utils.d.ts +2 -2
- package/dist/utils/output-utils.d.ts.map +1 -1
- package/dist/utils/output-utils.js.map +1 -1
- package/dist/utils/server-production-files.d.ts +22 -0
- package/dist/utils/server-production-files.d.ts.map +1 -0
- package/dist/utils/server-production-files.js +162 -0
- package/dist/utils/server-production-files.js.map +1 -0
- package/dist/utils/vite-config.js.map +1 -1
- package/dist/workers/client.worker.d.ts.map +1 -1
- package/dist/workers/client.worker.js +7 -1
- package/dist/workers/client.worker.js.map +1 -1
- package/dist/workers/lint.worker.d.ts +1 -1
- package/dist/workers/lint.worker.d.ts.map +1 -1
- package/dist/workers/lint.worker.js +1 -1
- package/dist/workers/lint.worker.js.map +1 -1
- package/dist/workers/server-build.worker.d.ts.map +1 -1
- package/dist/workers/server-build.worker.js +12 -161
- package/dist/workers/server-build.worker.js.map +1 -1
- package/package.json +4 -4
- package/src/angular/vite-postcss-inline-plugin.ts +5 -1
- package/src/capacitor/capacitor-android.ts +368 -0
- package/src/capacitor/capacitor.ts +4 -317
- package/src/commands/check.ts +2 -2
- package/src/commands/lint.ts +1 -201
- package/src/commands/publish.ts +2 -1
- package/src/commands/typecheck.ts +7 -292
- package/src/electron/electron.ts +4 -4
- package/src/engines/ViteEngine.ts +1 -1
- package/src/engines/types.ts +3 -0
- package/src/infra/ResultCollector.ts +2 -2
- package/src/orchestrators/DevWatchOrchestrator.ts +35 -20
- package/src/orchestrators/TypecheckOrchestrator.ts +364 -0
- package/src/sd-cli.ts +6 -1
- package/src/utils/lint-core.ts +205 -0
- package/src/utils/lint-utils.ts +1 -1
- package/src/utils/output-utils.ts +3 -3
- package/src/utils/server-production-files.ts +186 -0
- package/src/utils/vite-config.ts +1 -1
- package/src/workers/client.worker.ts +7 -1
- package/src/workers/lint.worker.ts +1 -1
- package/src/workers/server-build.worker.ts +11 -185
- package/tests/angular/vite-postcss-inline-plugin.spec.ts +10 -0
- package/tests/capacitor/capacitor-android-exports.verify.md +11 -0
- package/tests/capacitor/capacitor-android.spec.ts +219 -0
- package/tests/capacitor/capacitor-build.spec.ts +17 -21
- package/tests/capacitor/capacitor-icon.spec.ts +17 -19
- package/tests/capacitor/capacitor-init.spec.ts +18 -14
- package/tests/capacitor/capacitor-run.spec.ts +10 -24
- package/tests/capacitor/capacitor-workspace.spec.ts +10 -15
- package/tests/commands/check.spec.ts +2 -2
- package/tests/commands/lint.spec.ts +33 -194
- package/tests/commands/publish-set.verify.md +7 -0
- package/tests/electron/electron-symlink-cleanup.verify.md +8 -0
- package/tests/engines/vite-engine.spec.ts +29 -0
- package/tests/infra/result-collector.spec.ts +11 -0
- package/tests/orchestrators/dev-watch-orchestrator.spec.ts +70 -0
- package/tests/orchestrators/dist-delete-watcher.verify.md +10 -0
- package/tests/orchestrators/typecheck-orchestrator.spec.ts +180 -0
- package/tests/sd-cli-catch-all.verify.md +7 -0
- package/tests/utils/lint-core-import-paths.verify.md +10 -0
- package/tests/utils/lint-core.spec.ts +188 -0
- package/tests/utils/server-production-files-import-paths.verify.md +14 -0
- package/tests/workers/client-worker.spec.ts +92 -0
- package/tests/workers/server-build-context-dispose.verify.md +8 -0
- package/tests/workers/server-build-worker.spec.ts +39 -0
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import type { ServerBuildInfo } from "../workers/server-build.worker";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import { cpx } from "@simplysm/core-node";
|
|
5
|
+
import { consola } from "consola";
|
|
6
|
+
import { collectAllDependencyExternals } from "./esbuild-config";
|
|
7
|
+
|
|
8
|
+
const logger = consola.withTag("sd:cli:server-production-files");
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* 세 가지 소스에서 외부 모듈을 수집하고 병합한다.
|
|
12
|
+
* collectAllDependencyExternals를 통한 단일 패스 의존성 트리 순회를 사용한다.
|
|
13
|
+
*/
|
|
14
|
+
export function collectAllExternals(pkgDir: string, manualExternals?: string[]): string[] {
|
|
15
|
+
logger.debug("의존성 트리 스캔 중...");
|
|
16
|
+
const { optionalPeerDeps, nativeModules } = collectAllDependencyExternals(pkgDir);
|
|
17
|
+
|
|
18
|
+
const manual = manualExternals ?? [];
|
|
19
|
+
return [...new Set([...optionalPeerDeps, ...nativeModules, ...manual])];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* pnpm-lock.yaml의 packages 섹션을 파싱하여 name→version 맵을 생성한다.
|
|
24
|
+
* Lockfile v9 형식: `packages:` 섹션의 `'name@version':` 키를 파싱한다.
|
|
25
|
+
* YAML 파서 의존성을 피하기 위해 단순 라인 기반 파싱을 사용한다.
|
|
26
|
+
*/
|
|
27
|
+
export function parseLockfileVersions(cwd: string): Map<string, string> {
|
|
28
|
+
const lockfilePath = path.join(cwd, "pnpm-lock.yaml");
|
|
29
|
+
if (!fs.existsSync(lockfilePath)) {
|
|
30
|
+
throw new Error(`pnpm-lock.yaml not found in ${cwd}. Run "pnpm install" first.`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const content = fs.readFileSync(lockfilePath, "utf-8");
|
|
34
|
+
const map = new Map<string, string>();
|
|
35
|
+
|
|
36
|
+
// "packages:" 섹션을 찾고 "'@scope/name@1.2.3':" 또는 "'name@1.2.3':" 형태의 항목을 파싱
|
|
37
|
+
const lines = content.split("\n");
|
|
38
|
+
let inPackages = false;
|
|
39
|
+
for (const line of lines) {
|
|
40
|
+
if (line === "packages:") {
|
|
41
|
+
inPackages = true;
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
if (inPackages && line.length > 0 && !line.startsWith(" ") && !line.startsWith("'")) {
|
|
45
|
+
break; // 다음 최상위 섹션
|
|
46
|
+
}
|
|
47
|
+
if (!inPackages) continue;
|
|
48
|
+
|
|
49
|
+
// "'@scope/name@version':" 또는 "'name@version':" 매칭
|
|
50
|
+
const match = /^\s{2}'(.+)@(\d[^']*)':\s*$/.exec(line);
|
|
51
|
+
if (match != null) {
|
|
52
|
+
const name = match[1];
|
|
53
|
+
const version = match[2];
|
|
54
|
+
// 첫 번째 항목 유지 (lockfile은 각 버전을 한 번만 기록)
|
|
55
|
+
if (!map.has(name)) {
|
|
56
|
+
map.set(name, version);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return map;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* pnpm-lock.yaml에서 주어진 모든 패키지의 잠긴 버전을 확인한다.
|
|
66
|
+
* lockfile에서 패키지를 찾을 수 없으면 에러를 던진다.
|
|
67
|
+
*/
|
|
68
|
+
export function resolveLockedVersions(cwd: string, pkgNames: string[]): Record<string, string> {
|
|
69
|
+
const versionMap = parseLockfileVersions(cwd);
|
|
70
|
+
const result: Record<string, string> = {};
|
|
71
|
+
for (const name of pkgNames) {
|
|
72
|
+
const version = versionMap.get(name);
|
|
73
|
+
if (version == null) {
|
|
74
|
+
throw new Error(
|
|
75
|
+
`External dependency "${name}" not found in pnpm-lock.yaml. ` +
|
|
76
|
+
`Run "pnpm install" and try again.`,
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
result[name] = version;
|
|
80
|
+
}
|
|
81
|
+
return result;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* 프로덕션 배포용 파일을 생성한다
|
|
86
|
+
*/
|
|
87
|
+
export function generateProductionFiles(
|
|
88
|
+
info: ServerBuildInfo,
|
|
89
|
+
externals: string[],
|
|
90
|
+
): void {
|
|
91
|
+
const distDir = path.join(info.pkgDir, "dist");
|
|
92
|
+
const pkgJson = JSON.parse(fs.readFileSync(path.join(info.pkgDir, "package.json"), "utf-8"));
|
|
93
|
+
|
|
94
|
+
// dist/package.json
|
|
95
|
+
const distPkgJson: Record<string, unknown> = {
|
|
96
|
+
name: pkgJson.name,
|
|
97
|
+
version: pkgJson.version,
|
|
98
|
+
type: pkgJson.type,
|
|
99
|
+
};
|
|
100
|
+
if (externals.length > 0) {
|
|
101
|
+
distPkgJson["dependencies"] = resolveLockedVersions(info.cwd, externals);
|
|
102
|
+
}
|
|
103
|
+
if (info.packageManager === "volta") {
|
|
104
|
+
const nodeVersion = cpx.spawnSync("node", ["-v"]).stdout.trim();
|
|
105
|
+
distPkgJson["volta"] = { node: nodeVersion };
|
|
106
|
+
}
|
|
107
|
+
fs.writeFileSync(path.join(distDir, "package.json"), JSON.stringify(distPkgJson, undefined, 2));
|
|
108
|
+
|
|
109
|
+
// dist/mise.toml
|
|
110
|
+
if (info.packageManager === "mise") {
|
|
111
|
+
const rootMiseTomlPath = path.join(info.cwd, "mise.toml");
|
|
112
|
+
let nodeVersion = "20";
|
|
113
|
+
if (fs.existsSync(rootMiseTomlPath)) {
|
|
114
|
+
const miseContent = fs.readFileSync(rootMiseTomlPath, "utf-8");
|
|
115
|
+
const match = /node\s*=\s*"([^"]+)"/.exec(miseContent);
|
|
116
|
+
if (match != null) {
|
|
117
|
+
nodeVersion = match[1];
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
fs.writeFileSync(path.join(distDir, "mise.toml"), `[tools]\nnode = "${nodeVersion}"\n`);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// dist/openssl.cnf
|
|
124
|
+
fs.writeFileSync(
|
|
125
|
+
path.join(distDir, "openssl.cnf"),
|
|
126
|
+
[
|
|
127
|
+
"nodejs_conf = openssl_init",
|
|
128
|
+
"",
|
|
129
|
+
"[openssl_init]",
|
|
130
|
+
"providers = provider_sect",
|
|
131
|
+
"ssl_conf = ssl_sect",
|
|
132
|
+
"",
|
|
133
|
+
"[provider_sect]",
|
|
134
|
+
"default = default_sect",
|
|
135
|
+
"legacy = legacy_sect",
|
|
136
|
+
"",
|
|
137
|
+
"[default_sect]",
|
|
138
|
+
"activate = 1",
|
|
139
|
+
"",
|
|
140
|
+
"[legacy_sect]",
|
|
141
|
+
"activate = 1",
|
|
142
|
+
"",
|
|
143
|
+
"[ssl_sect]",
|
|
144
|
+
"system_default = system_default_sect",
|
|
145
|
+
"",
|
|
146
|
+
"[system_default_sect]",
|
|
147
|
+
"Options = UnsafeLegacyRenegotiation",
|
|
148
|
+
].join("\n"),
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
// dist/pm2.config.cjs
|
|
152
|
+
if (info.pm2 != null) {
|
|
153
|
+
const pm2Name = info.pm2.name ?? pkgJson.name.replace(/@/g, "").replace(/[/\\]/g, "-");
|
|
154
|
+
const ignoreWatch = JSON.stringify([
|
|
155
|
+
"node_modules",
|
|
156
|
+
"www",
|
|
157
|
+
...(info.pm2.ignoreWatchPaths ?? []),
|
|
158
|
+
]);
|
|
159
|
+
const envObj: Record<string, string> = {
|
|
160
|
+
NODE_ENV: "production",
|
|
161
|
+
TZ: "Asia/Seoul",
|
|
162
|
+
...(info.env ?? {}),
|
|
163
|
+
};
|
|
164
|
+
const envStr = JSON.stringify(envObj, undefined, 4);
|
|
165
|
+
|
|
166
|
+
const useInterpreter = info.packageManager !== "volta";
|
|
167
|
+
|
|
168
|
+
const pm2Config = [
|
|
169
|
+
...(useInterpreter ? [`const cp = require("child_process");`, ``] : []),
|
|
170
|
+
`module.exports = {`,
|
|
171
|
+
` name: ${JSON.stringify(pm2Name)},`,
|
|
172
|
+
` script: "main.js",`,
|
|
173
|
+
` watch: true,`,
|
|
174
|
+
` watch_delay: 2000,`,
|
|
175
|
+
` ignore_watch: ${ignoreWatch},`,
|
|
176
|
+
...(useInterpreter ? [` interpreter: cp.execSync("mise which node").toString().trim(),`] : []),
|
|
177
|
+
` interpreter_args: "--openssl-config=openssl.cnf",`,
|
|
178
|
+
` env: ${envStr.replace(/\n/g, "\n ")},`,
|
|
179
|
+
` arrayProcess: "concat",`,
|
|
180
|
+
` useDelTargetNull: true,`,
|
|
181
|
+
`};`,
|
|
182
|
+
].join("\n");
|
|
183
|
+
|
|
184
|
+
fs.writeFileSync(path.join(distDir, "pm2.config.cjs"), pm2Config);
|
|
185
|
+
}
|
|
186
|
+
}
|
package/src/utils/vite-config.ts
CHANGED
|
@@ -412,6 +412,9 @@ async function build(info: ClientBuildInfo): Promise<ClientBuildResult> {
|
|
|
412
412
|
const polyfills = fs.existsSync(polyfillsPath) ? ["./src/polyfills.ts"] : undefined;
|
|
413
413
|
|
|
414
414
|
let lintResult: LintWithProgramResult | undefined;
|
|
415
|
+
let buildSuccess = true;
|
|
416
|
+
let buildErrors: string[] | undefined;
|
|
417
|
+
let buildWarnings: string[] | undefined;
|
|
415
418
|
|
|
416
419
|
const viteConfig = await createClientViteConfig({
|
|
417
420
|
framework: info.framework,
|
|
@@ -423,6 +426,9 @@ async function build(info: ClientBuildInfo): Promise<ClientBuildResult> {
|
|
|
423
426
|
env: info.env,
|
|
424
427
|
enableLint: info.enableLint,
|
|
425
428
|
onBuild: (result) => {
|
|
429
|
+
buildSuccess = result.success;
|
|
430
|
+
buildErrors = result.errors;
|
|
431
|
+
buildWarnings = result.warnings;
|
|
426
432
|
if (result.lint != null) {
|
|
427
433
|
lintResult = result.lint;
|
|
428
434
|
}
|
|
@@ -443,7 +449,7 @@ async function build(info: ClientBuildInfo): Promise<ClientBuildResult> {
|
|
|
443
449
|
writeConfigJson(path.join(info.pkgDir, "dist"), info.configs);
|
|
444
450
|
|
|
445
451
|
logger.debug(`[${info.name}] client worker build 완료`);
|
|
446
|
-
return { success:
|
|
452
|
+
return { success: buildSuccess, errors: buildErrors, warnings: buildWarnings, lint: lintResult };
|
|
447
453
|
} catch (err) {
|
|
448
454
|
const message = errNs.message(err);
|
|
449
455
|
logger.debug(`[${info.name}] client worker build 예외: ${message}`);
|
|
@@ -2,7 +2,7 @@ import type ts from "typescript";
|
|
|
2
2
|
import path from "path";
|
|
3
3
|
import fs from "fs";
|
|
4
4
|
import esbuild from "esbuild";
|
|
5
|
-
import {
|
|
5
|
+
import { createWorker, FsWatcher, pathx } from "@simplysm/core-node";
|
|
6
6
|
import { err as errNs } from "@simplysm/core-common";
|
|
7
7
|
import { consola } from "consola";
|
|
8
8
|
import type { BuildOutput } from "../engines/types";
|
|
@@ -14,9 +14,9 @@ import {
|
|
|
14
14
|
} from "../utils/tsconfig";
|
|
15
15
|
import {
|
|
16
16
|
createServerEsbuildOptions,
|
|
17
|
-
collectAllDependencyExternals,
|
|
18
17
|
writeChangedOutputFiles,
|
|
19
18
|
} from "../utils/esbuild-config";
|
|
19
|
+
import { collectAllExternals, generateProductionFiles } from "../utils/server-production-files";
|
|
20
20
|
import { runTscPackageBuild } from "../utils/tsc-build";
|
|
21
21
|
import { LintWithProgramRunner } from "../utils/lint-with-program";
|
|
22
22
|
import { registerCleanupHandlers, createOnceGuard, setupWorkerConsola } from "../utils/worker-utils";
|
|
@@ -136,184 +136,6 @@ async function cleanup(): Promise<void> {
|
|
|
136
136
|
}
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
-
/**
|
|
140
|
-
* 세 가지 소스에서 외부 모듈을 수집하고 병합한다.
|
|
141
|
-
* collectAllDependencyExternals를 통한 단일 패스 의존성 트리 순회를 사용한다.
|
|
142
|
-
*/
|
|
143
|
-
function collectAllExternals(pkgDir: string, manualExternals?: string[]): string[] {
|
|
144
|
-
logger.debug("의존성 트리 스캔 중...");
|
|
145
|
-
const { optionalPeerDeps, nativeModules } = collectAllDependencyExternals(pkgDir);
|
|
146
|
-
|
|
147
|
-
const manual = manualExternals ?? [];
|
|
148
|
-
return [...new Set([...optionalPeerDeps, ...nativeModules, ...manual])];
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* pnpm-lock.yaml의 packages 섹션을 파싱하여 name→version 맵을 생성한다.
|
|
153
|
-
* Lockfile v9 형식: `packages:` 섹션의 `'name@version':` 키를 파싱한다.
|
|
154
|
-
* YAML 파서 의존성을 피하기 위해 단순 라인 기반 파싱을 사용한다.
|
|
155
|
-
*/
|
|
156
|
-
function parseLockfileVersions(cwd: string): Map<string, string> {
|
|
157
|
-
const lockfilePath = path.join(cwd, "pnpm-lock.yaml");
|
|
158
|
-
if (!fs.existsSync(lockfilePath)) {
|
|
159
|
-
throw new Error(`pnpm-lock.yaml not found in ${cwd}. Run "pnpm install" first.`);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
const content = fs.readFileSync(lockfilePath, "utf-8");
|
|
163
|
-
const map = new Map<string, string>();
|
|
164
|
-
|
|
165
|
-
// "packages:" 섹션을 찾고 "'@scope/name@1.2.3':" 또는 "'name@1.2.3':" 형태의 항목을 파싱
|
|
166
|
-
const lines = content.split("\n");
|
|
167
|
-
let inPackages = false;
|
|
168
|
-
for (const line of lines) {
|
|
169
|
-
if (line === "packages:") {
|
|
170
|
-
inPackages = true;
|
|
171
|
-
continue;
|
|
172
|
-
}
|
|
173
|
-
if (inPackages && line.length > 0 && !line.startsWith(" ") && !line.startsWith("'")) {
|
|
174
|
-
break; // 다음 최상위 섹션
|
|
175
|
-
}
|
|
176
|
-
if (!inPackages) continue;
|
|
177
|
-
|
|
178
|
-
// "'@scope/name@version':" 또는 "'name@version':" 매칭
|
|
179
|
-
const match = /^\s{2}'(.+)@(\d[^']*)':\s*$/.exec(line);
|
|
180
|
-
if (match != null) {
|
|
181
|
-
const name = match[1];
|
|
182
|
-
const version = match[2];
|
|
183
|
-
// 첫 번째 항목 유지 (lockfile은 각 버전을 한 번만 기록)
|
|
184
|
-
if (!map.has(name)) {
|
|
185
|
-
map.set(name, version);
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
return map;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
* pnpm-lock.yaml에서 주어진 모든 패키지의 잠긴 버전을 확인한다.
|
|
195
|
-
* lockfile에서 패키지를 찾을 수 없으면 에러를 던진다.
|
|
196
|
-
*/
|
|
197
|
-
function resolveLockedVersions(cwd: string, pkgNames: string[]): Record<string, string> {
|
|
198
|
-
const versionMap = parseLockfileVersions(cwd);
|
|
199
|
-
const result: Record<string, string> = {};
|
|
200
|
-
for (const name of pkgNames) {
|
|
201
|
-
const version = versionMap.get(name);
|
|
202
|
-
if (version == null) {
|
|
203
|
-
throw new Error(
|
|
204
|
-
`External dependency "${name}" not found in pnpm-lock.yaml. ` +
|
|
205
|
-
`Run "pnpm install" and try again.`,
|
|
206
|
-
);
|
|
207
|
-
}
|
|
208
|
-
result[name] = version;
|
|
209
|
-
}
|
|
210
|
-
return result;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
* 프로덕션 배포용 파일을 생성한다
|
|
215
|
-
*/
|
|
216
|
-
function generateProductionFiles(
|
|
217
|
-
info: ServerBuildInfo,
|
|
218
|
-
externals: string[],
|
|
219
|
-
): void {
|
|
220
|
-
const distDir = path.join(info.pkgDir, "dist");
|
|
221
|
-
const pkgJson = JSON.parse(fs.readFileSync(path.join(info.pkgDir, "package.json"), "utf-8"));
|
|
222
|
-
|
|
223
|
-
// dist/package.json
|
|
224
|
-
const distPkgJson: Record<string, unknown> = {
|
|
225
|
-
name: pkgJson.name,
|
|
226
|
-
version: pkgJson.version,
|
|
227
|
-
type: pkgJson.type,
|
|
228
|
-
};
|
|
229
|
-
if (externals.length > 0) {
|
|
230
|
-
distPkgJson["dependencies"] = resolveLockedVersions(info.cwd, externals);
|
|
231
|
-
}
|
|
232
|
-
if (info.packageManager === "volta") {
|
|
233
|
-
const nodeVersion = cpx.spawnSync("node", ["-v"]).stdout.trim();
|
|
234
|
-
distPkgJson["volta"] = { node: nodeVersion };
|
|
235
|
-
}
|
|
236
|
-
fs.writeFileSync(path.join(distDir, "package.json"), JSON.stringify(distPkgJson, undefined, 2));
|
|
237
|
-
|
|
238
|
-
// dist/mise.toml
|
|
239
|
-
if (info.packageManager === "mise") {
|
|
240
|
-
const rootMiseTomlPath = path.join(info.cwd, "mise.toml");
|
|
241
|
-
let nodeVersion = "20";
|
|
242
|
-
if (fs.existsSync(rootMiseTomlPath)) {
|
|
243
|
-
const miseContent = fs.readFileSync(rootMiseTomlPath, "utf-8");
|
|
244
|
-
const match = /node\s*=\s*"([^"]+)"/.exec(miseContent);
|
|
245
|
-
if (match != null) {
|
|
246
|
-
nodeVersion = match[1];
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
fs.writeFileSync(path.join(distDir, "mise.toml"), `[tools]\nnode = "${nodeVersion}"\n`);
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
// dist/openssl.cnf
|
|
253
|
-
fs.writeFileSync(
|
|
254
|
-
path.join(distDir, "openssl.cnf"),
|
|
255
|
-
[
|
|
256
|
-
"nodejs_conf = openssl_init",
|
|
257
|
-
"",
|
|
258
|
-
"[openssl_init]",
|
|
259
|
-
"providers = provider_sect",
|
|
260
|
-
"ssl_conf = ssl_sect",
|
|
261
|
-
"",
|
|
262
|
-
"[provider_sect]",
|
|
263
|
-
"default = default_sect",
|
|
264
|
-
"legacy = legacy_sect",
|
|
265
|
-
"",
|
|
266
|
-
"[default_sect]",
|
|
267
|
-
"activate = 1",
|
|
268
|
-
"",
|
|
269
|
-
"[legacy_sect]",
|
|
270
|
-
"activate = 1",
|
|
271
|
-
"",
|
|
272
|
-
"[ssl_sect]",
|
|
273
|
-
"system_default = system_default_sect",
|
|
274
|
-
"",
|
|
275
|
-
"[system_default_sect]",
|
|
276
|
-
"Options = UnsafeLegacyRenegotiation",
|
|
277
|
-
].join("\n"),
|
|
278
|
-
);
|
|
279
|
-
|
|
280
|
-
// dist/pm2.config.cjs
|
|
281
|
-
if (info.pm2 != null) {
|
|
282
|
-
const pm2Name = info.pm2.name ?? pkgJson.name.replace(/@/g, "").replace(/[/\\]/g, "-");
|
|
283
|
-
const ignoreWatch = JSON.stringify([
|
|
284
|
-
"node_modules",
|
|
285
|
-
"www",
|
|
286
|
-
...(info.pm2.ignoreWatchPaths ?? []),
|
|
287
|
-
]);
|
|
288
|
-
const envObj: Record<string, string> = {
|
|
289
|
-
NODE_ENV: "production",
|
|
290
|
-
TZ: "Asia/Seoul",
|
|
291
|
-
...(info.env ?? {}),
|
|
292
|
-
};
|
|
293
|
-
const envStr = JSON.stringify(envObj, undefined, 4);
|
|
294
|
-
|
|
295
|
-
const useInterpreter = info.packageManager !== "volta";
|
|
296
|
-
|
|
297
|
-
const pm2Config = [
|
|
298
|
-
...(useInterpreter ? [`const cp = require("child_process");`, ``] : []),
|
|
299
|
-
`module.exports = {`,
|
|
300
|
-
` name: ${JSON.stringify(pm2Name)},`,
|
|
301
|
-
` script: "main.js",`,
|
|
302
|
-
` watch: true,`,
|
|
303
|
-
` watch_delay: 2000,`,
|
|
304
|
-
` ignore_watch: ${ignoreWatch},`,
|
|
305
|
-
...(useInterpreter ? [` interpreter: cp.execSync("mise which node").toString().trim(),`] : []),
|
|
306
|
-
` interpreter_args: "--openssl-config=openssl.cnf",`,
|
|
307
|
-
` env: ${envStr.replace(/\n/g, "\n ")},`,
|
|
308
|
-
` arrayProcess: "concat",`,
|
|
309
|
-
` useDelTargetNull: true,`,
|
|
310
|
-
`};`,
|
|
311
|
-
].join("\n");
|
|
312
|
-
|
|
313
|
-
fs.writeFileSync(path.join(distDir, "pm2.config.cjs"), pm2Config);
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
|
|
317
139
|
registerCleanupHandlers(cleanup, logger);
|
|
318
140
|
|
|
319
141
|
//#endregion
|
|
@@ -610,11 +432,15 @@ async function startWatch(info: ServerWatchInfo): Promise<void> {
|
|
|
610
432
|
const newExternal = cachedExternal;
|
|
611
433
|
|
|
612
434
|
const oldContext = esbuildContext;
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
435
|
+
esbuildContext = undefined; // 선제 초기화 — 생성 실패 시 disposed 참조 방지 (LOGIC-001)
|
|
436
|
+
try {
|
|
437
|
+
if (info.output.js) {
|
|
438
|
+
esbuildContext = await createEsbuildWatchContext(info, newEntryPoints, newExternal);
|
|
439
|
+
}
|
|
440
|
+
} finally {
|
|
441
|
+
if (oldContext != null) {
|
|
442
|
+
await oldContext.dispose();
|
|
443
|
+
}
|
|
618
444
|
}
|
|
619
445
|
|
|
620
446
|
const result = await rebuildAll();
|
|
@@ -39,6 +39,16 @@ describe("processPostCssInline", () => {
|
|
|
39
39
|
expect(result).toBe(input);
|
|
40
40
|
});
|
|
41
41
|
|
|
42
|
+
// Acceptance: double-quote 문자열의 인용부호가 보존된다
|
|
43
|
+
it("preserves double-quote style when processing inline CSS", async () => {
|
|
44
|
+
const input = `Component({ styles: [".host { color: blue; }"] })`;
|
|
45
|
+
const result = await processPostCssInline(input, "test.js", [testPlugin]);
|
|
46
|
+
|
|
47
|
+
expect(result).toContain("color: red");
|
|
48
|
+
// double-quote가 보존되어야 한다
|
|
49
|
+
expect(result).toMatch(/".host \{ color: red; \}"/);
|
|
50
|
+
});
|
|
51
|
+
|
|
42
52
|
// Unit: multiple styles in array
|
|
43
53
|
it("processes multiple styles in the array", async () => {
|
|
44
54
|
const input = `Component({ styles: ['.a { color: blue; }', '.b { color: green; }'] })`;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Capacitor Android 설정 분리 — LLM 검증
|
|
2
|
+
|
|
3
|
+
## 검증 항목
|
|
4
|
+
|
|
5
|
+
- [x] configureAndroid이 capacitor-android.ts에서 export된다: line 13 `export async function configureAndroid`
|
|
6
|
+
- [x] findJava21이 capacitor-android.ts에서 export된다: line 53 `export async function findJava21`
|
|
7
|
+
- [x] findAndroidSdk가 capacitor-android.ts에서 export된다: line 76 `export async function findAndroidSdk`
|
|
8
|
+
- [x] 내부 configure 함수들(_configureJavaHomePath 등)은 export되지 않는다: `^export` 검색 결과 3개만 확인
|
|
9
|
+
- [x] Capacitor 클래스의 _validateTools가 findAndroidSdk/findJava21을 import하여 사용한다: line 8 import, line 217/229 호출
|
|
10
|
+
- [x] Capacitor 클래스에서 9개 Android 설정 private 메서드가 삭제되었다: `_configureAndroid` 등 검색 결과 0건
|
|
11
|
+
- [x] capacitor.ts에서 env import가 제거되었다: `import.*env.*from` 검색 결과 0건
|