Skip to content

Commit 051e480

Browse files
feat: added blockVitest environment and flags intake (#2162)
## PR Checklist - [x] Addresses an existing open issue: fixes #2159, #2160 - [x] That issue was marked as [`status: accepting prs`](https://github.com/JoshuaKGoldberg/create-typescript-app/issues?q=is%3Aopen+is%3Aissue+label%3A%22status%3A+accepting+prs%22) - [x] Steps in [CONTRIBUTING.md](https://github.com/JoshuaKGoldberg/create-typescript-app/blob/main/.github/CONTRIBUTING.md) were taken ## Overview 🎁
1 parent f6accd2 commit 051e480

File tree

3 files changed

+108
-44
lines changed

3 files changed

+108
-44
lines changed

src/base.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ export const base = createBase({
136136
devDependencies: z.record(z.string(), z.string()).optional(),
137137
peerDependencies: z.record(z.string(), z.string()).optional(),
138138
peerDependenciesMeta: z.record(z.unknown()).optional(),
139-
scripts: z.record(z.string(), z.string()).optional(),
139+
scripts: z.record(z.string(), z.string().optional()).optional(),
140140
})
141141
.optional()
142142
.describe("additional properties to include in `package.json`"),

src/blocks/blockVitest.test.ts

Lines changed: 62 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1041,76 +1041,83 @@ describe("blockVitest", () => {
10411041
});
10421042

10431043
describe("intake", () => {
1044-
it("returns undefined when vitest.config.ts does not exist", () => {
1044+
it("returns nothing when vitest.config.ts does not exist", () => {
10451045
const actual = testIntake(blockVitest, {
10461046
files: {
10471047
src: {},
10481048
},
1049+
options: optionsBase,
10491050
});
10501051

1051-
expect(actual).toEqual(undefined);
1052+
expect(actual).toEqual({});
10521053
});
10531054

1054-
it("returns undefined when vitest.config.ts does not contain the expected defineConfig", () => {
1055+
it("returns nothing when vitest.config.ts does not contain the expected defineConfig", () => {
10551056
const actual = testIntake(blockVitest, {
10561057
files: {
10571058
"vitest.config.ts": [`invalid`],
10581059
},
1060+
options: optionsBase,
10591061
});
10601062

1061-
expect(actual).toEqual(undefined);
1063+
expect(actual).toEqual({});
10621064
});
10631065

1064-
it("returns undefined when vitest.config.ts passes a non-object to defineConfig", () => {
1066+
it("returns nothing when vitest.config.ts passes a non-object to defineConfig", () => {
10651067
const actual = testIntake(blockVitest, {
10661068
files: {
10671069
"vitest.config.ts": [`defineConfig("invalid")`],
10681070
},
1071+
options: optionsBase,
10691072
});
10701073

1071-
expect(actual).toEqual(undefined);
1074+
expect(actual).toEqual({});
10721075
});
10731076

1074-
it("returns undefined when vitest.config.ts does not pass a test to defineConfig", () => {
1077+
it("returns nothing when vitest.config.ts does not pass a test to defineConfig", () => {
10751078
const actual = testIntake(blockVitest, {
10761079
files: {
10771080
"vitest.config.ts": [`defineConfig({ other: true })`],
10781081
},
1082+
options: optionsBase,
10791083
});
10801084

1081-
expect(actual).toEqual(undefined);
1085+
expect(actual).toEqual({});
10821086
});
10831087

1084-
it("returns undefined when vitest.config.ts passes unknown test data to defineConfig", () => {
1088+
it("returns nothing when vitest.config.ts passes unknown test data to defineConfig", () => {
10851089
const actual = testIntake(blockVitest, {
10861090
files: {
10871091
"vitest.config.ts": [`defineConfig({ test: true })`],
10881092
},
1093+
options: optionsBase,
10891094
});
10901095

1091-
expect(actual).toEqual(undefined);
1096+
expect(actual).toEqual({});
10921097
});
10931098

1094-
it("returns undefined when vitest.config.ts passes invalid test syntax to defineConfig", () => {
1099+
it("returns nothing when vitest.config.ts passes invalid test syntax to defineConfig", () => {
10951100
const actual = testIntake(blockVitest, {
10961101
files: {
10971102
"vitest.config.ts": [`defineConfig({ test: { ! } })`],
10981103
},
1104+
options: optionsBase,
10991105
});
11001106

1101-
expect(actual).toEqual(undefined);
1107+
expect(actual).toEqual({});
11021108
});
11031109

1104-
it("returns undefined when vitest.config.ts passes invalid test data to defineConfig", () => {
1110+
it("returns nothing when vitest.config.ts passes invalid test data to defineConfig", () => {
11051111
const actual = testIntake(blockVitest, {
11061112
files: {
11071113
"vitest.config.ts": [
11081114
`defineConfig({ test: { coverage: 'invalid' } })`,
11091115
],
11101116
},
1117+
options: optionsBase,
11111118
});
11121119

1113-
expect(actual).toEqual(undefined);
1120+
expect(actual).toEqual({});
11141121
});
11151122

11161123
it("returns coverage and exclude when they exist in vitest.config.ts", () => {
@@ -1135,6 +1142,7 @@ export default defineConfig({
11351142
`,
11361143
],
11371144
},
1145+
options: optionsBase,
11381146
});
11391147

11401148
expect(actual).toEqual({
@@ -1145,5 +1153,45 @@ export default defineConfig({
11451153
exclude: ["lib", "node_modules"],
11461154
});
11471155
});
1156+
1157+
it("returns environment when it exists in vitest.config.ts", () => {
1158+
const actual = testIntake(blockVitest, {
1159+
files: {
1160+
"vitest.config.ts": [
1161+
`import { defineConfig } from "vitest/config";
1162+
1163+
export default defineConfig({
1164+
test: {
1165+
environment: "happy-dom",
1166+
},
1167+
});
1168+
`,
1169+
],
1170+
},
1171+
options: optionsBase,
1172+
});
1173+
1174+
expect(actual).toEqual({
1175+
environment: "happy-dom",
1176+
});
1177+
});
1178+
1179+
it("returns flags when it exists in package.json", () => {
1180+
const actual = testIntake(blockVitest, {
1181+
files: {},
1182+
options: {
1183+
...optionsBase,
1184+
packageData: {
1185+
scripts: {
1186+
test: "vitest --typecheck",
1187+
},
1188+
},
1189+
},
1190+
});
1191+
1192+
expect(actual).toEqual({
1193+
flags: ["--typecheck"],
1194+
});
1195+
});
11481196
});
11491197
});

src/blocks/blockVitest.ts

Lines changed: 45 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { IntakeDirectory } from "bingo-fs";
12
import JSON5 from "json5";
23
import { z } from "zod";
34

@@ -33,12 +34,46 @@ const zCoverage = z.object({
3334
include: z.array(z.string()).optional(),
3435
});
3536

37+
const zEnvironment = z.string();
38+
3639
const zExclude = z.array(z.string());
3740

38-
const zTest = z.object({
39-
coverage: zCoverage,
40-
exclude: zExclude,
41-
});
41+
const zTest = z
42+
.object({
43+
coverage: zCoverage,
44+
environment: zEnvironment,
45+
exclude: zExclude,
46+
})
47+
.partial();
48+
49+
function intakeFromConfig(files: IntakeDirectory) {
50+
const file = intakeFile(files, ["vitest.config.ts"]);
51+
if (!file) {
52+
return undefined;
53+
}
54+
55+
const normalized = file[0].replaceAll(/[\n\r]/g, "");
56+
const matched = /defineConfig\(\{(.+)\}\)\s*(?:;\s*)?$/u.exec(normalized);
57+
if (!matched) {
58+
return undefined;
59+
}
60+
61+
const rawData = tryParseJSON5(`{${matched[1]}}`);
62+
if (typeof rawData !== "object" || typeof rawData.test !== "object") {
63+
return undefined;
64+
}
65+
66+
const parsedData = zTest.safeParse(rawData.test).data;
67+
if (!parsedData) {
68+
return undefined;
69+
}
70+
71+
return {
72+
coverage: parsedData.coverage,
73+
environment: parsedData.environment,
74+
exclude: parsedData.exclude,
75+
};
76+
}
4277

4378
export const blockVitest = base.createBlock({
4479
about: {
@@ -47,35 +82,16 @@ export const blockVitest = base.createBlock({
4782
addons: {
4883
actionSteps: z.array(zActionStep).default([]),
4984
coverage: zCoverage.default({}),
50-
environment: z.string().optional(),
85+
environment: zEnvironment.optional(),
5186
exclude: zExclude.default([]),
5287
flags: z.array(z.string()).default([]),
5388
},
54-
intake({ files }) {
55-
const file = intakeFile(files, ["vitest.config.ts"]);
56-
if (!file) {
57-
return undefined;
58-
}
59-
60-
const normalized = file[0].replaceAll(/[\n\r]/g, "");
61-
const matched = /defineConfig\(\{(.+)\}\)\s*(?:;\s*)?$/u.exec(normalized);
62-
if (!matched) {
63-
return undefined;
64-
}
65-
66-
const rawData = tryParseJSON5(`{${matched[1]}}`);
67-
if (typeof rawData !== "object" || typeof rawData.test !== "object") {
68-
return undefined;
69-
}
70-
71-
const parsedData = zTest.safeParse(rawData.test).data;
72-
if (!parsedData) {
73-
return undefined;
74-
}
75-
89+
intake({ files, options }) {
7690
return {
77-
coverage: parsedData.coverage,
78-
exclude: parsedData.exclude,
91+
...intakeFromConfig(files),
92+
flags: options.packageData?.scripts?.test
93+
?.match(/^vitest (.+)/)?.[1]
94+
.split(" "),
7995
};
8096
},
8197
produce({ addons }) {

0 commit comments

Comments
 (0)