@simplysm/sd-cli 14.0.38 → 14.0.39
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/angular-build-pipeline.d.ts +1 -1
- package/dist/angular/angular-build-pipeline.js +1 -1
- package/dist/angular/client-transform-stylesheet.d.ts +1 -1
- package/dist/angular/client-transform-stylesheet.js +3 -3
- package/dist/esbuild/esbuild-client-config.d.ts +0 -2
- package/dist/esbuild/esbuild-client-config.d.ts.map +1 -1
- package/dist/esbuild/esbuild-client-config.js +19 -9
- package/dist/esbuild/esbuild-client-config.js.map +1 -1
- package/dist/esbuild/esbuild-postcss-plugin.d.ts +8 -0
- package/dist/esbuild/esbuild-postcss-plugin.d.ts.map +1 -0
- package/dist/esbuild/esbuild-postcss-plugin.js +105 -0
- package/dist/esbuild/esbuild-postcss-plugin.js.map +1 -0
- package/dist/esbuild/esbuild-tsc-plugin.d.ts +23 -0
- package/dist/esbuild/esbuild-tsc-plugin.d.ts.map +1 -0
- package/dist/esbuild/esbuild-tsc-plugin.js +60 -0
- package/dist/esbuild/esbuild-tsc-plugin.js.map +1 -0
- package/dist/workers/client.worker.d.ts.map +1 -1
- package/dist/workers/client.worker.js +32 -2
- package/dist/workers/client.worker.js.map +1 -1
- package/dist/workers/server-build.worker.d.ts.map +1 -1
- package/dist/workers/server-build.worker.js +129 -90
- package/dist/workers/server-build.worker.js.map +1 -1
- package/dist/workers/server-esbuild-context.d.ts +27 -0
- package/dist/workers/server-esbuild-context.d.ts.map +1 -1
- package/dist/workers/server-esbuild-context.js +43 -3
- package/dist/workers/server-esbuild-context.js.map +1 -1
- package/package.json +6 -4
- package/src/angular/angular-build-pipeline.ts +2 -2
- package/src/angular/client-transform-stylesheet.ts +4 -4
- package/src/esbuild/esbuild-client-config.ts +21 -12
- package/src/esbuild/esbuild-postcss-plugin.ts +117 -0
- package/src/esbuild/esbuild-tsc-plugin.ts +83 -0
- package/src/workers/client.worker.ts +32 -2
- package/src/workers/server-build.worker.ts +136 -97
- package/src/workers/server-esbuild-context.ts +59 -3
- package/tests/angular/client-transform-stylesheet.spec.ts +1 -1
- package/tests/esbuild/esbuild-tsc-plugin.acc.spec.ts +349 -0
- package/tests/esbuild/esbuild-tsc-plugin.spec.ts +230 -0
- package/tests/utils/esbuild-client-config-postcss.verify.md +6 -0
- package/tests/utils/esbuild-client-config.acc.spec.ts +26 -14
- package/tests/utils/esbuild-client-config.spec.ts +73 -11
- package/tests/utils/esbuild-postcss-plugin.acc.spec.ts +299 -0
- package/tests/utils/esbuild-postcss-plugin.spec.ts +290 -0
- package/tests/utils/esbuild-scss-plugin.acc.spec.ts +1 -0
- package/tests/workers/server-build-lint.spec.ts +43 -0
- package/tests/workers/server-build-worker-refactoring.verify.md +14 -0
- package/tests/workers/server-build-worker.spec.ts +122 -9
- package/tests/workers/server-esbuild-context-tsc.verify.md +7 -0
- package/tests/workers/server-esbuild-context.acc.spec.ts +156 -2
- package/tests/workers/server-esbuild-context.spec.ts +320 -2
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
2
|
+
import type esbuild from "esbuild";
|
|
3
|
+
import type { TscPackageBuildResult } from "../../src/utils/tsc-build";
|
|
4
|
+
|
|
5
|
+
//#region Mocks
|
|
6
|
+
|
|
7
|
+
const mockRunTscPackageBuild = vi.fn<(...args: unknown[]) => TscPackageBuildResult>();
|
|
8
|
+
|
|
9
|
+
vi.mock("../../src/utils/tsc-build", () => ({
|
|
10
|
+
runTscPackageBuild: (...args: unknown[]) => mockRunTscPackageBuild(...args),
|
|
11
|
+
}));
|
|
12
|
+
|
|
13
|
+
const mockParseTsconfig = vi.fn();
|
|
14
|
+
|
|
15
|
+
vi.mock("../../src/utils/tsconfig", async (importOriginal) => {
|
|
16
|
+
const actual = await importOriginal<typeof import("../../src/utils/tsconfig")>();
|
|
17
|
+
return {
|
|
18
|
+
...actual,
|
|
19
|
+
parseTsconfig: (...args: unknown[]) => mockParseTsconfig(...args),
|
|
20
|
+
};
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
//#endregion
|
|
24
|
+
|
|
25
|
+
const { createTscPlugin } = await import("../../src/esbuild/esbuild-tsc-plugin");
|
|
26
|
+
|
|
27
|
+
/** esbuild 플러그인 lifecycle을 시뮬레이션하는 헬퍼 */
|
|
28
|
+
function setupPlugin(plugin: esbuild.Plugin) {
|
|
29
|
+
let onStartCb: (() => esbuild.OnStartResult | null | void | Promise<esbuild.OnStartResult | null | void>) | undefined;
|
|
30
|
+
let onEndCb: ((result: esbuild.BuildResult) => esbuild.OnEndResult | null | void | Promise<esbuild.OnEndResult | null | void>) | undefined;
|
|
31
|
+
|
|
32
|
+
const mockBuild = {
|
|
33
|
+
onStart(cb: typeof onStartCb) { onStartCb = cb; },
|
|
34
|
+
onEnd(cb: typeof onEndCb) { onEndCb = cb; },
|
|
35
|
+
} as unknown as esbuild.PluginBuild;
|
|
36
|
+
|
|
37
|
+
void plugin.setup(mockBuild);
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
async invokeOnStart() {
|
|
41
|
+
return (await onStartCb?.()) ?? null;
|
|
42
|
+
},
|
|
43
|
+
async invokeOnEnd(result?: Partial<esbuild.BuildResult>) {
|
|
44
|
+
return (await onEndCb?.({
|
|
45
|
+
errors: [],
|
|
46
|
+
warnings: [],
|
|
47
|
+
mangleCache: {},
|
|
48
|
+
outputFiles: [],
|
|
49
|
+
metafile: { inputs: {}, outputs: {} },
|
|
50
|
+
...result,
|
|
51
|
+
} as esbuild.BuildResult)) ?? null;
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const baseOptions = {
|
|
57
|
+
pkgDir: "/workspace/packages/my-server",
|
|
58
|
+
cwd: "/workspace",
|
|
59
|
+
output: { dts: true },
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const mockParsedConfig = {
|
|
63
|
+
options: { target: 99 },
|
|
64
|
+
fileNames: [],
|
|
65
|
+
errors: [],
|
|
66
|
+
} as any;
|
|
67
|
+
|
|
68
|
+
function createSuccessTscResult(): TscPackageBuildResult {
|
|
69
|
+
return {
|
|
70
|
+
success: true,
|
|
71
|
+
diagnostics: [],
|
|
72
|
+
errorCount: 0,
|
|
73
|
+
warningCount: 0,
|
|
74
|
+
program: { getSourceFiles: () => [] } as any,
|
|
75
|
+
affectedFiles: new Set(["/workspace/packages/my-server/src/main.ts"]),
|
|
76
|
+
builderProgram: {} as any,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
describe("createTscPlugin — Unit Tests", () => {
|
|
81
|
+
beforeEach(() => {
|
|
82
|
+
vi.clearAllMocks();
|
|
83
|
+
mockParseTsconfig.mockReturnValue(mockParsedConfig);
|
|
84
|
+
mockRunTscPackageBuild.mockReturnValue(createSuccessTscResult());
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
describe("plugin 구조", () => {
|
|
88
|
+
it("setup 함수가 onStart와 onEnd를 등록한다", () => {
|
|
89
|
+
const result = createTscPlugin(baseOptions);
|
|
90
|
+
const onStartSpy = vi.fn();
|
|
91
|
+
const onEndSpy = vi.fn();
|
|
92
|
+
|
|
93
|
+
const mockBuild = {
|
|
94
|
+
onStart: onStartSpy,
|
|
95
|
+
onEnd: onEndSpy,
|
|
96
|
+
} as unknown as esbuild.PluginBuild;
|
|
97
|
+
|
|
98
|
+
void result.plugin.setup(mockBuild);
|
|
99
|
+
|
|
100
|
+
expect(onStartSpy).toHaveBeenCalledOnce();
|
|
101
|
+
expect(onEndSpy).toHaveBeenCalledOnce();
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
describe("onStart — runTscPackageBuild 옵션 전달", () => {
|
|
106
|
+
it("pkgDir, cwd, output.dts를 올바르게 전달한다", async () => {
|
|
107
|
+
const result = createTscPlugin(baseOptions);
|
|
108
|
+
const lifecycle = setupPlugin(result.plugin);
|
|
109
|
+
|
|
110
|
+
await lifecycle.invokeOnStart();
|
|
111
|
+
await lifecycle.invokeOnEnd();
|
|
112
|
+
|
|
113
|
+
expect(mockRunTscPackageBuild).toHaveBeenCalledWith(
|
|
114
|
+
expect.objectContaining({
|
|
115
|
+
pkgDir: "/workspace/packages/my-server",
|
|
116
|
+
cwd: "/workspace",
|
|
117
|
+
output: { js: false, dts: true },
|
|
118
|
+
parsedConfig: mockParsedConfig,
|
|
119
|
+
}),
|
|
120
|
+
);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it("output.dts가 false일 때도 정확하게 전달한다", async () => {
|
|
124
|
+
const result = createTscPlugin({
|
|
125
|
+
...baseOptions,
|
|
126
|
+
output: { dts: false },
|
|
127
|
+
});
|
|
128
|
+
const lifecycle = setupPlugin(result.plugin);
|
|
129
|
+
|
|
130
|
+
await lifecycle.invokeOnStart();
|
|
131
|
+
await lifecycle.invokeOnEnd();
|
|
132
|
+
|
|
133
|
+
expect(mockRunTscPackageBuild).toHaveBeenCalledWith(
|
|
134
|
+
expect.objectContaining({
|
|
135
|
+
output: { js: false, dts: false },
|
|
136
|
+
}),
|
|
137
|
+
);
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
describe("onEnd — 상태 저장", () => {
|
|
142
|
+
it("tsc 결과의 diagnostics를 정확히 저장한다", async () => {
|
|
143
|
+
const diagnostics = [
|
|
144
|
+
{ category: 0, code: 6031, messageText: "Watching for changes" },
|
|
145
|
+
{ category: 1, code: 2322, messageText: "Type error" },
|
|
146
|
+
];
|
|
147
|
+
mockRunTscPackageBuild.mockReturnValue({
|
|
148
|
+
...createSuccessTscResult(),
|
|
149
|
+
diagnostics,
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
const result = createTscPlugin(baseOptions);
|
|
153
|
+
const lifecycle = setupPlugin(result.plugin);
|
|
154
|
+
|
|
155
|
+
await lifecycle.invokeOnStart();
|
|
156
|
+
await lifecycle.invokeOnEnd();
|
|
157
|
+
|
|
158
|
+
expect(result.getDiagnostics()).toEqual(diagnostics);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it("parseTsconfig 예외 시에도 에러를 저장한다", async () => {
|
|
162
|
+
mockParseTsconfig.mockImplementation(() => {
|
|
163
|
+
throw new Error("Invalid tsconfig.json");
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
const result = createTscPlugin(baseOptions);
|
|
167
|
+
const lifecycle = setupPlugin(result.plugin);
|
|
168
|
+
|
|
169
|
+
await lifecycle.invokeOnStart();
|
|
170
|
+
await lifecycle.invokeOnEnd();
|
|
171
|
+
|
|
172
|
+
expect(result.getErrors()).toEqual(["Invalid tsconfig.json"]);
|
|
173
|
+
expect(result.getDiagnostics()).toEqual([]);
|
|
174
|
+
expect(result.getProgram()).toBeUndefined();
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
describe("getter — 연속 빌드", () => {
|
|
179
|
+
it("두 번째 빌드 결과가 첫 번째 결과를 덮어쓴다", async () => {
|
|
180
|
+
const result = createTscPlugin(baseOptions);
|
|
181
|
+
const lifecycle = setupPlugin(result.plugin);
|
|
182
|
+
|
|
183
|
+
// 첫 번째 빌드 — 에러
|
|
184
|
+
const errorResult: TscPackageBuildResult = {
|
|
185
|
+
success: false,
|
|
186
|
+
errors: ["first error"],
|
|
187
|
+
diagnostics: [{ category: 1, code: 1, messageText: "err" }],
|
|
188
|
+
errorCount: 1,
|
|
189
|
+
warningCount: 0,
|
|
190
|
+
};
|
|
191
|
+
mockRunTscPackageBuild.mockReturnValue(errorResult);
|
|
192
|
+
await lifecycle.invokeOnStart();
|
|
193
|
+
await lifecycle.invokeOnEnd();
|
|
194
|
+
expect(result.getErrors()).toEqual(["first error"]);
|
|
195
|
+
|
|
196
|
+
// 두 번째 빌드 — 성공
|
|
197
|
+
mockRunTscPackageBuild.mockReturnValue(createSuccessTscResult());
|
|
198
|
+
await lifecycle.invokeOnStart();
|
|
199
|
+
await lifecycle.invokeOnEnd();
|
|
200
|
+
expect(result.getErrors()).toBeUndefined();
|
|
201
|
+
expect(result.getProgram()).toBeDefined();
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
describe("resetBuilderProgram", () => {
|
|
206
|
+
it("빌드 전 호출해도 에러가 발생하지 않는다", () => {
|
|
207
|
+
const result = createTscPlugin(baseOptions);
|
|
208
|
+
expect(() => result.resetBuilderProgram()).not.toThrow();
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it("여러 번 호출해도 안전하다", async () => {
|
|
212
|
+
const result = createTscPlugin(baseOptions);
|
|
213
|
+
const lifecycle = setupPlugin(result.plugin);
|
|
214
|
+
|
|
215
|
+
await lifecycle.invokeOnStart();
|
|
216
|
+
await lifecycle.invokeOnEnd();
|
|
217
|
+
|
|
218
|
+
result.resetBuilderProgram();
|
|
219
|
+
result.resetBuilderProgram();
|
|
220
|
+
|
|
221
|
+
// 리셋 후 빌드 가능
|
|
222
|
+
await lifecycle.invokeOnStart();
|
|
223
|
+
await lifecycle.invokeOnEnd();
|
|
224
|
+
|
|
225
|
+
expect(mockRunTscPackageBuild).toHaveBeenLastCalledWith(
|
|
226
|
+
expect.objectContaining({ oldBuilderProgram: undefined }),
|
|
227
|
+
);
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
});
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# Feature 1.2 client 빌드 PostCSS 설정 통합 — LLM 검증
|
|
2
|
+
|
|
3
|
+
## 검증 항목
|
|
4
|
+
|
|
5
|
+
- [x] postcssConfigPath가 CreateClientEsbuildOptions 인터페이스에서 제거됨: `esbuild-client-config.ts`에서 `postcssConfigPath` 검색 결과 없음 (No matches found). `client.worker.ts`에서도 2곳 모두 제거 확인.
|
|
6
|
+
- [x] createScssPlugin에 로딩된 PostCSS 인스턴스가 전달됨: `esbuild-client-config.ts:118`에서 `postcssPlugins: loadedPostcssPlugins`로 `createScssPlugin`에 전달 확인.
|
|
@@ -37,6 +37,19 @@ vi.mock("browserslist-to-esbuild", () => ({
|
|
|
37
37
|
default: vi.fn(() => ["chrome61"]),
|
|
38
38
|
}));
|
|
39
39
|
|
|
40
|
+
vi.mock("module", async (importOriginal) => {
|
|
41
|
+
const actual = await importOriginal<typeof import("module")>();
|
|
42
|
+
return {
|
|
43
|
+
...actual,
|
|
44
|
+
createRequire: vi.fn(() => (name: string) => {
|
|
45
|
+
if (name === "nonexistent-plugin") {
|
|
46
|
+
throw new Error(`Cannot find module '${name}'`);
|
|
47
|
+
}
|
|
48
|
+
return (..._args: any[]) => ({ postcssPlugin: name });
|
|
49
|
+
}),
|
|
50
|
+
};
|
|
51
|
+
});
|
|
52
|
+
|
|
40
53
|
// --- Imports (after mocks) ---
|
|
41
54
|
|
|
42
55
|
const { createClientEsbuildContext } = await import(
|
|
@@ -171,7 +184,7 @@ describe("createClientEsbuildContext — Acceptance", () => {
|
|
|
171
184
|
});
|
|
172
185
|
|
|
173
186
|
// Scenario: 커스텀 env 주입
|
|
174
|
-
it("env 설정 시 import.meta.env
|
|
187
|
+
it("env 설정 시 import.meta.env 객체로 define에 주입", async () => {
|
|
175
188
|
await createClientEsbuildContext({
|
|
176
189
|
pkgDir: "/workspace/packages/my-app",
|
|
177
190
|
cwd: "/workspace",
|
|
@@ -180,27 +193,26 @@ describe("createClientEsbuildContext — Acceptance", () => {
|
|
|
180
193
|
});
|
|
181
194
|
|
|
182
195
|
const esbuildOptions = vi.mocked(esbuild.context).mock.calls[0][0];
|
|
183
|
-
expect(esbuildOptions.define!["import.meta.env
|
|
184
|
-
|
|
196
|
+
expect(esbuildOptions.define!["import.meta.env"]).toBe(
|
|
197
|
+
JSON.stringify({ API_URL: "https://api.example.com", DEBUG: "true" }),
|
|
185
198
|
);
|
|
186
|
-
expect(esbuildOptions.define!["import.meta.env.DEBUG"]).toBe('"true"');
|
|
187
199
|
});
|
|
188
200
|
|
|
189
|
-
// Scenario: PostCSS 설정
|
|
190
|
-
it("postcssPlugins 전달 시
|
|
201
|
+
// Scenario: PostCSS 설정 — postcssConfiguration 비활성화 + sd-postcss 등록
|
|
202
|
+
it("postcssPlugins 전달 시 postcssConfiguration은 undefined이고 sd-postcss 플러그인이 등록된다", async () => {
|
|
191
203
|
await createClientEsbuildContext({
|
|
192
204
|
pkgDir: "/workspace/packages/my-app",
|
|
193
205
|
cwd: "/workspace",
|
|
194
206
|
mode: "build",
|
|
195
207
|
postcssPlugins: [["autoprefixer", {}]],
|
|
196
|
-
postcssConfigPath: "/workspace/packages/my-app",
|
|
197
208
|
});
|
|
198
209
|
|
|
199
210
|
const [, styleOpts] = vi.mocked(createCompilerPlugin).mock.calls[0];
|
|
200
|
-
expect(styleOpts.postcssConfiguration).
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
211
|
+
expect(styleOpts.postcssConfiguration).toBeUndefined();
|
|
212
|
+
|
|
213
|
+
const esbuildOptions = vi.mocked(esbuild.context).mock.calls[0][0];
|
|
214
|
+
const pluginNames = esbuildOptions.plugins!.map((p: any) => p.name);
|
|
215
|
+
expect(pluginNames).toContain("sd-postcss");
|
|
204
216
|
});
|
|
205
217
|
|
|
206
218
|
// Scenario: 프로덕션 일회성 빌드
|
|
@@ -358,8 +370,8 @@ describe("createClientEsbuildContext — Acceptance", () => {
|
|
|
358
370
|
]);
|
|
359
371
|
});
|
|
360
372
|
|
|
361
|
-
// Scenario: dev 모드 출력 네이밍 (해시
|
|
362
|
-
it("dev 모드: entryNames,
|
|
373
|
+
// Scenario: dev 모드 출력 네이밍 (entry/asset은 해시 없음, chunk은 해시 포함)
|
|
374
|
+
it("dev 모드: entryNames, assetNames는 [name], chunkNames는 [name]-[hash]", async () => {
|
|
363
375
|
await createClientEsbuildContext({
|
|
364
376
|
pkgDir: "/workspace/packages/my-app",
|
|
365
377
|
cwd: "/workspace",
|
|
@@ -368,7 +380,7 @@ describe("createClientEsbuildContext — Acceptance", () => {
|
|
|
368
380
|
|
|
369
381
|
const esbuildOptions = vi.mocked(esbuild.context).mock.calls[0][0];
|
|
370
382
|
expect(esbuildOptions.entryNames).toBe("[name]");
|
|
371
|
-
expect(esbuildOptions.chunkNames).toBe("[name]");
|
|
383
|
+
expect(esbuildOptions.chunkNames).toBe("[name]-[hash]");
|
|
372
384
|
expect(esbuildOptions.assetNames).toBe("[name]");
|
|
373
385
|
});
|
|
374
386
|
|
|
@@ -39,6 +39,19 @@ vi.mock("browserslist-to-esbuild", () => ({
|
|
|
39
39
|
default: vi.fn(() => ["chrome61"]),
|
|
40
40
|
}));
|
|
41
41
|
|
|
42
|
+
vi.mock("module", async (importOriginal) => {
|
|
43
|
+
const actual = await importOriginal<typeof import("module")>();
|
|
44
|
+
return {
|
|
45
|
+
...actual,
|
|
46
|
+
createRequire: vi.fn(() => (name: string) => {
|
|
47
|
+
if (name === "nonexistent-plugin") {
|
|
48
|
+
throw new Error(`Cannot find module '${name}'`);
|
|
49
|
+
}
|
|
50
|
+
return (..._args: any[]) => ({ postcssPlugin: name });
|
|
51
|
+
}),
|
|
52
|
+
};
|
|
53
|
+
});
|
|
54
|
+
|
|
42
55
|
// --- Imports (after mocks) ---
|
|
43
56
|
|
|
44
57
|
const { createClientEsbuildContext } = await import(
|
|
@@ -108,22 +121,19 @@ describe("createClientEsbuildContext — define 생성", () => {
|
|
|
108
121
|
expect(opts.define!["ngHmrMode"]).toBe("false");
|
|
109
122
|
});
|
|
110
123
|
|
|
111
|
-
it("env가 없으면 import.meta.env
|
|
124
|
+
it("env가 없으면 import.meta.env define 없음", async () => {
|
|
112
125
|
await createClientEsbuildContext(baseDev);
|
|
113
126
|
const opts = vi.mocked(esbuild.context).mock.calls[0][0];
|
|
114
|
-
|
|
115
|
-
k.startsWith("import.meta.env."),
|
|
116
|
-
);
|
|
117
|
-
expect(envKeys).toHaveLength(0);
|
|
127
|
+
expect(opts.define!["import.meta.env"]).toBeUndefined();
|
|
118
128
|
});
|
|
119
129
|
|
|
120
|
-
it("env
|
|
130
|
+
it("env 설정 시 import.meta.env 객체로 define에 주입", async () => {
|
|
121
131
|
await createClientEsbuildContext({
|
|
122
132
|
...baseBuild,
|
|
123
133
|
env: { MSG: 'hello "world"' },
|
|
124
134
|
});
|
|
125
135
|
const opts = vi.mocked(esbuild.context).mock.calls[0][0];
|
|
126
|
-
expect(opts.define!["import.meta.env
|
|
136
|
+
expect(opts.define!["import.meta.env"]).toBe(JSON.stringify({ MSG: 'hello "world"' }));
|
|
127
137
|
});
|
|
128
138
|
});
|
|
129
139
|
|
|
@@ -160,13 +170,65 @@ describe("createClientEsbuildContext — PostCSS 설정", () => {
|
|
|
160
170
|
expect(styleOpts.postcssConfiguration).toBeUndefined();
|
|
161
171
|
});
|
|
162
172
|
|
|
163
|
-
it("
|
|
173
|
+
it("postcssPlugins 전달해도 postcssConfiguration은 항상 undefined", async () => {
|
|
164
174
|
await createClientEsbuildContext({
|
|
165
175
|
...baseBuild,
|
|
166
176
|
postcssPlugins: [["autoprefixer"]],
|
|
167
177
|
});
|
|
168
178
|
const [, styleOpts] = vi.mocked(createCompilerPlugin).mock.calls[0];
|
|
169
|
-
expect(styleOpts.postcssConfiguration
|
|
179
|
+
expect(styleOpts.postcssConfiguration).toBeUndefined();
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
describe("createClientEsbuildContext — PostCSS 플러그인 통합", () => {
|
|
184
|
+
beforeEach(() => vi.clearAllMocks());
|
|
185
|
+
|
|
186
|
+
it("postcssPlugins 전달 시 sd-postcss 플러그인이 plugins에 등록된다", async () => {
|
|
187
|
+
await createClientEsbuildContext({
|
|
188
|
+
...baseBuild,
|
|
189
|
+
postcssPlugins: [["autoprefixer"]],
|
|
190
|
+
});
|
|
191
|
+
const opts = vi.mocked(esbuild.context).mock.calls[0][0];
|
|
192
|
+
const pluginNames = opts.plugins!.map((p: any) => p.name);
|
|
193
|
+
expect(pluginNames).toContain("sd-postcss");
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it("postcssPlugins 미전달 시 sd-postcss 미등록", async () => {
|
|
197
|
+
await createClientEsbuildContext(baseBuild);
|
|
198
|
+
const opts = vi.mocked(esbuild.context).mock.calls[0][0];
|
|
199
|
+
const pluginNames = opts.plugins!.map((p: any) => p.name);
|
|
200
|
+
expect(pluginNames).not.toContain("sd-postcss");
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it("sd-postcss가 customPlugins 뒤, sd-legacy-strip-dynamic-import 앞에 배치된다", async () => {
|
|
204
|
+
const customPlugin = { name: "custom", setup: vi.fn() };
|
|
205
|
+
await createClientEsbuildContext({
|
|
206
|
+
...baseBuild,
|
|
207
|
+
postcssPlugins: [["autoprefixer"]],
|
|
208
|
+
plugins: [customPlugin as any],
|
|
209
|
+
legacyModule: true,
|
|
210
|
+
onEnd: vi.fn(),
|
|
211
|
+
});
|
|
212
|
+
const opts = vi.mocked(esbuild.context).mock.calls[0][0];
|
|
213
|
+
const pluginNames = opts.plugins!.map((p: any) => p.name);
|
|
214
|
+
|
|
215
|
+
const customIdx = pluginNames.indexOf("custom");
|
|
216
|
+
const postcssIdx = pluginNames.indexOf("sd-postcss");
|
|
217
|
+
const stripIdx = pluginNames.indexOf("sd-legacy-strip-dynamic-import");
|
|
218
|
+
const onEndIdx = pluginNames.indexOf("sd-on-end");
|
|
219
|
+
|
|
220
|
+
expect(postcssIdx).toBeGreaterThan(customIdx);
|
|
221
|
+
expect(postcssIdx).toBeLessThan(stripIdx);
|
|
222
|
+
expect(postcssIdx).toBeLessThan(onEndIdx);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
it("존재하지 않는 플러그인 이름으로 에러가 throw된다", async () => {
|
|
226
|
+
await expect(
|
|
227
|
+
createClientEsbuildContext({
|
|
228
|
+
...baseBuild,
|
|
229
|
+
postcssPlugins: [["nonexistent-plugin"]],
|
|
230
|
+
}),
|
|
231
|
+
).rejects.toThrow("nonexistent-plugin");
|
|
170
232
|
});
|
|
171
233
|
});
|
|
172
234
|
|
|
@@ -471,8 +533,8 @@ describe("createClientEsbuildContext — legacyModule 설정", () => {
|
|
|
471
533
|
env: { API_URL: "https://api.example.com" },
|
|
472
534
|
});
|
|
473
535
|
const opts = vi.mocked(esbuild.context).mock.calls[0][0];
|
|
474
|
-
expect(opts.define!["import.meta.env
|
|
475
|
-
JSON.stringify("https://api.example.com"),
|
|
536
|
+
expect(opts.define!["import.meta.env"]).toBe(
|
|
537
|
+
JSON.stringify({ API_URL: "https://api.example.com" }),
|
|
476
538
|
);
|
|
477
539
|
expect(opts.supported).toEqual({ "import-meta": false });
|
|
478
540
|
});
|