ui-thing 0.0.4 → 0.0.6
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/.github/workflows/main.yml +19 -0
- package/.github/workflows/test.yml +19 -0
- package/CHANGELOG.md +47 -124
- package/dist/index.d.ts +0 -0
- package/dist/index.js +51 -51
- package/dist/index.js.map +1 -1
- package/package.json +17 -8
- package/src/comp.ts +22 -22
- package/src/types.ts +20 -0
- package/tests/utils/addPrettierConfig.test.ts +78 -0
- package/tests/utils/fileExists.test.ts +38 -0
- package/vite.config.ts +9 -0
package/package.json
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ui-thing",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.6",
|
|
4
4
|
"description": "CLI used to add Nuxt 3 components to a project",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
7
7
|
"main": "./dist/index.js",
|
|
8
|
+
"repository": {
|
|
9
|
+
"url": "https://github.com/BayBreezy/ui-thing-cli"
|
|
10
|
+
},
|
|
8
11
|
"publishConfig": {
|
|
9
12
|
"access": "public"
|
|
10
13
|
},
|
|
@@ -16,7 +19,9 @@
|
|
|
16
19
|
"dev": "tsup --watch",
|
|
17
20
|
"start": "node dist/index.js",
|
|
18
21
|
"format": "npx prettier --write .",
|
|
19
|
-
"release": "npm run build && npx changelogen@latest --release && npm publish && git push --follow-tags"
|
|
22
|
+
"release": "npm run build && npx changelogen@latest --release && npm publish && git push --follow-tags",
|
|
23
|
+
"test": "vitest",
|
|
24
|
+
"coverage": "vitest run --coverage"
|
|
20
25
|
},
|
|
21
26
|
"keywords": [
|
|
22
27
|
"cli",
|
|
@@ -36,6 +41,7 @@
|
|
|
36
41
|
"license": "MIT",
|
|
37
42
|
"dependencies": {
|
|
38
43
|
"boxen": "^7.1.1",
|
|
44
|
+
"build": "^0.1.4",
|
|
39
45
|
"c12": "^1.5.1",
|
|
40
46
|
"commander": "^11.1.0",
|
|
41
47
|
"defu": "^6.1.3",
|
|
@@ -49,14 +55,17 @@
|
|
|
49
55
|
"prompts": "^2.4.2"
|
|
50
56
|
},
|
|
51
57
|
"devDependencies": {
|
|
58
|
+
"@gmrchk/cli-testing-library": "^0.1.2",
|
|
52
59
|
"@ianvs/prettier-plugin-sort-imports": "^4.1.1",
|
|
53
|
-
"@types/figlet": "^1.5.
|
|
54
|
-
"@types/fs-extra": "^11.0.
|
|
55
|
-
"@types/lodash": "^4.14.
|
|
56
|
-
"@types/node": "^20.
|
|
57
|
-
"@types/prompts": "^2.4.
|
|
60
|
+
"@types/figlet": "^1.5.8",
|
|
61
|
+
"@types/fs-extra": "^11.0.4",
|
|
62
|
+
"@types/lodash": "^4.14.201",
|
|
63
|
+
"@types/node": "^20.9.0",
|
|
64
|
+
"@types/prompts": "^2.4.8",
|
|
65
|
+
"@vitest/coverage-v8": "^0.34.6",
|
|
58
66
|
"magicast": "^0.3.0",
|
|
59
67
|
"tsup": "^7.2.0",
|
|
60
|
-
"typescript": "^5.2.2"
|
|
68
|
+
"typescript": "^5.2.2",
|
|
69
|
+
"vitest": "^0.34.6"
|
|
61
70
|
}
|
|
62
71
|
}
|
package/src/comp.ts
CHANGED
|
@@ -435,7 +435,7 @@ export default [
|
|
|
435
435
|
fileName: "Checkbox/Checkbox.vue",
|
|
436
436
|
dirPath: "components/UI",
|
|
437
437
|
fileContent:
|
|
438
|
-
'<template>\r\n <CheckboxRoot v-bind="forwarded" :class="styles({ class: props.class })">\r\n <slot>\r\n <Transition enter-active-class="transition" enter-from-class="opacity-0 scale-0">\r\n <UICheckboxIndicator :icon="icon" />\r\n </Transition>\r\n </slot>\r\n </CheckboxRoot>\r\n</template>\r\n\r\n<script lang="ts" setup>\r\n import { CheckboxRoot, useForwardPropsEmits } from "radix-vue";\r\n import type { CheckboxRootEmits, CheckboxRootProps } from "radix-vue";\r\n\r\n const props = defineProps<\r\n CheckboxRootProps & {\r\n class?: any;\r\n id?: string;\r\n icon?: string;\r\n }\r\n >();\r\n\r\n const emit = defineEmits<CheckboxRootEmits>();\r\n const forwarded = useForwardPropsEmits(props, emit);\r\n\r\n const styles = tv({\r\n base: "peer h-
|
|
438
|
+
'<template>\r\n <CheckboxRoot v-bind="forwarded" :class="styles({ class: props.class })">\r\n <slot>\r\n <Transition enter-active-class="transition" enter-from-class="opacity-0 scale-0">\r\n <UICheckboxIndicator :icon="icon" />\r\n </Transition>\r\n </slot>\r\n </CheckboxRoot>\r\n</template>\r\n\r\n<script lang="ts" setup>\r\n import { CheckboxRoot, useForwardPropsEmits } from "radix-vue";\r\n import type { CheckboxRootEmits, CheckboxRootProps } from "radix-vue";\r\n\r\n const props = defineProps<\r\n CheckboxRootProps & {\r\n class?: any;\r\n id?: string;\r\n icon?: string;\r\n }\r\n >();\r\n\r\n const emit = defineEmits<CheckboxRootEmits>();\r\n const forwarded = useForwardPropsEmits(props, emit);\r\n\r\n const styles = tv({\r\n base: "peer h-[18px] w-[18px] shrink-0 rounded-sm border border-primary ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground md:h-4 md:w-4",\r\n });\r\n</script>\r\n',
|
|
439
439
|
},
|
|
440
440
|
{
|
|
441
441
|
fileName: "Checkbox/Indicator.vue",
|
|
@@ -988,7 +988,7 @@ export default [
|
|
|
988
988
|
fileName: "Input.vue",
|
|
989
989
|
dirPath: "components/UI",
|
|
990
990
|
fileContent:
|
|
991
|
-
'<template>\r\n <input :class="styles({ class: props.class })" v-bind="forwarded" v-model="localModel" />\r\n</template>\r\n\r\n<script lang="ts" setup>\r\n import { useForwardPropsEmits } from "radix-vue";\r\n\r\n const props = withDefaults(\r\n defineProps<{\r\n class?: any;\r\n id?: string;\r\n name?: string;\r\n placeholder?: string;\r\n disabled?: boolean;\r\n required?: boolean;\r\n type?: string;\r\n modelValue?: any;\r\n }>(),\r\n { type: "text" }\r\n );\r\n const emits = defineEmits<{\r\n "update:modelValue": [value: any];\r\n }>();\r\n const forwarded = useForwardPropsEmits(useOmit(props, ["class"]), emits);\r\n\r\n const localModel = useVModel(props, "modelValue", emits);\r\n\r\n const styles = tv({\r\n base: "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground file:hover:cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 sm:text-sm",\r\n });\r\n</script>\r\n',
|
|
991
|
+
'<template>\r\n <input :class="styles({ class: props.class })" v-bind="forwarded" v-model="localModel" />\r\n</template>\r\n\r\n<script lang="ts" setup>\r\n import { useForwardPropsEmits } from "radix-vue";\r\n\r\n const props = withDefaults(\r\n defineProps<{\r\n class?: any;\r\n id?: string;\r\n name?: string;\r\n placeholder?: string;\r\n disabled?: boolean;\r\n required?: boolean;\r\n type?: string;\r\n modelValue?: any;\r\n }>(),\r\n { type: "text" }\r\n );\r\n const emits = defineEmits<{\r\n "update:modelValue": [value: any];\r\n }>();\r\n const forwarded = useForwardPropsEmits(useOmit(props, ["class"]), emits);\r\n\r\n const localModel = useVModel(props, "modelValue", emits);\r\n\r\n const styles = tv({\r\n base: "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-[16px] ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground file:hover:cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 sm:text-sm",\r\n });\r\n</script>\r\n',
|
|
992
992
|
},
|
|
993
993
|
],
|
|
994
994
|
utils: [],
|
|
@@ -1022,7 +1022,7 @@ export default [
|
|
|
1022
1022
|
fileName: "Label.vue",
|
|
1023
1023
|
dirPath: "components/UI",
|
|
1024
1024
|
fileContent:
|
|
1025
|
-
'<template>\r\n <Label :class="styles({ class: props.class })" v-bind="forwarded">\r\n <slot />\r\n </Label>\r\n</template>\r\n\r\n<script lang="ts" setup>\r\n import { Label, useForwardProps } from "radix-vue";\r\n import type { LabelProps } from "radix-vue";\r\n\r\n const props = defineProps<\r\n LabelProps & {\r\n class?: any;\r\n }\r\n >();\r\n const forwarded = useForwardProps(useOmit(props, ["class"]));\r\n\r\n const styles = tv({\r\n base: "inline-block text-
|
|
1025
|
+
'<template>\r\n <Label :class="styles({ class: props.class })" v-bind="forwarded">\r\n <slot />\r\n </Label>\r\n</template>\r\n\r\n<script lang="ts" setup>\r\n import { Label, useForwardProps } from "radix-vue";\r\n import type { LabelProps } from "radix-vue";\r\n\r\n const props = defineProps<\r\n LabelProps & {\r\n class?: any;\r\n }\r\n >();\r\n const forwarded = useForwardProps(useOmit(props, ["class"]));\r\n\r\n const styles = tv({\r\n base: "inline-block text-base font-medium leading-none hover:cursor-pointer peer-disabled:cursor-not-allowed peer-disabled:opacity-70 sm:text-sm",\r\n });\r\n</script>\r\n',
|
|
1026
1026
|
},
|
|
1027
1027
|
],
|
|
1028
1028
|
utils: [],
|
|
@@ -1240,7 +1240,7 @@ export default [
|
|
|
1240
1240
|
fileName: "OTP.vue",
|
|
1241
1241
|
dirPath: "components/UI",
|
|
1242
1242
|
fileContent:
|
|
1243
|
-
'<template>\n <VOtpInput\n ref="otp"\n v-model:value="localModel"\n :input-classes="styles({ separator: Boolean(separator), class: inputClasses })"\n :separator="separator"\n :num-inputs="numInputs"\n :input-type="inputType"\n :inputmode="inputmode"\n :should-auto-focus="shouldAutoFocus"\n :placeholder="placeholder"\n :is-disabled="disabled"\n @on-change="emits(\'change\', $event)"\n @on-complete="emits(\'complete\', $event)"\n />\n</template>\n\n<script lang="ts" setup>\n import VOtpInput from "vue3-otp-input";\n\n const otp = ref<InstanceType<typeof VOtpInput> | null>(null);\n\n const props = withDefaults(\n defineProps<{\n modelValue?: string;\n numInputs?: number;\n separator?: string;\n inputClasses?: any;\n conditionalClass?: any[];\n inputType?: "number" | "tel" | "letter-numeric" | "password";\n inputmode?: "none" | "text" | "tel" | "url" | "email" | "numeric" | "decimal" | "search";\n shouldAutoFocus?: boolean;\n placeholder?: string[];\n disabled?: boolean;\n }>(),\n {\n numInputs: 4,\n inputType: "letter-numeric",\n inputmode: "text",\n separator: "",\n }\n );\n const emits = defineEmits<{\n "update:modelValue": [any];\n change: [any];\n complete: [any];\n ready: [any];\n }>();\n const localModel = useVModel(props, "modelValue", emits);\n\n const styles = tv({\n base: "mr-3 h-10 w-10 rounded-md border border-input bg-background p-1 text-center font-medium [-moz-appearance:_textfield] selection:bg-primary
|
|
1243
|
+
'<template>\n <VOtpInput\n ref="otp"\n v-model:value="localModel"\n :input-classes="styles({ separator: Boolean(separator), class: inputClasses })"\n :separator="separator"\n :num-inputs="numInputs"\n :input-type="inputType"\n :inputmode="inputmode"\n :should-auto-focus="shouldAutoFocus"\n :placeholder="placeholder"\n :is-disabled="disabled"\n @on-change="emits(\'change\', $event)"\n @on-complete="emits(\'complete\', $event)"\n />\n</template>\n\n<script lang="ts" setup>\n import VOtpInput from "vue3-otp-input";\n\n const otp = ref<InstanceType<typeof VOtpInput> | null>(null);\n\n const props = withDefaults(\n defineProps<{\n modelValue?: string;\n numInputs?: number;\n separator?: string;\n inputClasses?: any;\n conditionalClass?: any[];\n inputType?: "number" | "tel" | "letter-numeric" | "password";\n inputmode?: "none" | "text" | "tel" | "url" | "email" | "numeric" | "decimal" | "search";\n shouldAutoFocus?: boolean;\n placeholder?: string[];\n disabled?: boolean;\n }>(),\n {\n numInputs: 4,\n inputType: "letter-numeric",\n inputmode: "text",\n separator: "",\n }\n );\n const emits = defineEmits<{\n "update:modelValue": [any];\n change: [any];\n complete: [any];\n ready: [any];\n }>();\n const localModel = useVModel(props, "modelValue", emits);\n\n const styles = tv({\n base: "mr-3 h-10 w-10 rounded-md border border-input bg-background p-1 text-center text-base font-medium [-moz-appearance:_textfield] selection:bg-primary selection:text-primary-foreground placeholder:text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 sm:text-sm [&::-webkit-inner-spin-button]:m-0 [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none",\n variants: {\n separator: {\n true: "mx-2",\n },\n },\n });\n\n onMounted(() => {\n emits("ready", otp.value);\n });\n</script>\n',
|
|
1244
1244
|
},
|
|
1245
1245
|
],
|
|
1246
1246
|
utils: [],
|
|
@@ -1999,24 +1999,6 @@ export default [
|
|
|
1999
1999
|
utils: [],
|
|
2000
2000
|
composables: [],
|
|
2001
2001
|
},
|
|
2002
|
-
{
|
|
2003
|
-
name: "Vue Sonner",
|
|
2004
|
-
value: "vue-sonner",
|
|
2005
|
-
deps: ["vue-sonner"],
|
|
2006
|
-
devDeps: [],
|
|
2007
|
-
nuxtModules: [],
|
|
2008
|
-
instructions: ["Remember to add the <UIVueSonner /> tag to your app.vue/layout file."],
|
|
2009
|
-
files: [
|
|
2010
|
-
{
|
|
2011
|
-
fileName: "VueSonner.client.vue",
|
|
2012
|
-
dirPath: "components/UI",
|
|
2013
|
-
fileContent:
|
|
2014
|
-
'<template>\r\n <Toaster\r\n position="top-right"\r\n :visible-toasts="5"\r\n rich-colors\r\n :duration="7000"\r\n close-button\r\n :theme="$colorMode.value == \'dark\' ? \'dark\' : \'light\'"\r\n />\r\n</template>\r\n\r\n<script lang="ts" setup>\r\n import { Toaster } from "vue-sonner";\r\n</script>\r\n<style scoped>\r\n :deep([data-sonner-toaster][data-theme="dark"]),\r\n :deep([data-sonner-toaster][data-theme="light"]) {\r\n --normal-bg: theme("colors.popover.DEFAULT");\r\n --normal-border: theme("colors.border");\r\n --normal-text: theme("colors.popover.foreground");\r\n --border-radius: theme("borderRadius.md");\r\n }\r\n :deep([data-sonner-toaster]) {\r\n @apply font-sans;\r\n }\r\n :deep([data-sonner-toast][data-styled="true"]) {\r\n @apply items-start;\r\n }\r\n :deep([data-sonner-toast] [data-icon]) {\r\n @apply mt-0.5;\r\n }\r\n :deep([data-sonner-toast] [data-title]) {\r\n @apply text-sm font-semibold;\r\n }\r\n :deep([data-sonner-toast] [data-description]) {\r\n @apply text-sm;\r\n }\r\n :deep([data-sonner-toast] [data-close-button]) {\r\n @apply border border-border bg-background text-foreground hover:border-inherit hover:bg-inherit hover:text-accent-foreground;\r\n }\r\n :deep([data-sonner-toast] [data-button]) {\r\n @apply bg-primary text-primary-foreground transition hover:opacity-90 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background;\r\n }\r\n :deep(.sonner-loading-bar) {\r\n @apply bg-muted-foreground;\r\n }\r\n</style>\r\n',
|
|
2015
|
-
},
|
|
2016
|
-
],
|
|
2017
|
-
utils: [],
|
|
2018
|
-
composables: [],
|
|
2019
|
-
},
|
|
2020
2002
|
{
|
|
2021
2003
|
name: "VeeCheckbox",
|
|
2022
2004
|
value: "vue-checkbox",
|
|
@@ -2112,6 +2094,24 @@ export default [
|
|
|
2112
2094
|
utils: [],
|
|
2113
2095
|
composables: [],
|
|
2114
2096
|
},
|
|
2097
|
+
{
|
|
2098
|
+
name: "Vue Sonner",
|
|
2099
|
+
value: "vue-sonner",
|
|
2100
|
+
deps: ["vue-sonner"],
|
|
2101
|
+
devDeps: [],
|
|
2102
|
+
nuxtModules: [],
|
|
2103
|
+
instructions: ["Remember to add the <UIVueSonner /> tag to your app.vue/layout file."],
|
|
2104
|
+
files: [
|
|
2105
|
+
{
|
|
2106
|
+
fileName: "VueSonner.client.vue",
|
|
2107
|
+
dirPath: "components/UI",
|
|
2108
|
+
fileContent:
|
|
2109
|
+
'<template>\r\n <Toaster\r\n position="top-right"\r\n :visible-toasts="5"\r\n rich-colors\r\n :duration="7000"\r\n close-button\r\n :theme="$colorMode.value == \'dark\' ? \'dark\' : \'light\'"\r\n />\r\n</template>\r\n\r\n<script lang="ts" setup>\r\n import { Toaster } from "vue-sonner";\r\n</script>\r\n<style scoped>\r\n :deep([data-sonner-toaster][data-theme="dark"]),\r\n :deep([data-sonner-toaster][data-theme="light"]) {\r\n --normal-bg: theme("colors.popover.DEFAULT");\r\n --normal-border: theme("colors.border");\r\n --normal-text: theme("colors.popover.foreground");\r\n --border-radius: theme("borderRadius.md");\r\n }\r\n :deep([data-sonner-toaster]) {\r\n @apply font-sans;\r\n }\r\n :deep([data-sonner-toast][data-styled="true"]) {\r\n @apply items-start;\r\n }\r\n :deep([data-sonner-toast] [data-icon]) {\r\n @apply mt-0.5;\r\n }\r\n :deep([data-sonner-toast] [data-title]) {\r\n @apply text-sm font-semibold;\r\n }\r\n :deep([data-sonner-toast] [data-description]) {\r\n @apply text-sm;\r\n }\r\n :deep([data-sonner-toast] [data-close-button]) {\r\n @apply border border-border bg-background text-foreground hover:border-inherit hover:bg-inherit hover:text-accent-foreground;\r\n }\r\n :deep([data-sonner-toast] [data-button]) {\r\n @apply bg-primary text-primary-foreground transition hover:opacity-90 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background;\r\n }\r\n :deep(.sonner-loading-bar) {\r\n @apply bg-muted-foreground;\r\n }\r\n</style>\r\n',
|
|
2110
|
+
},
|
|
2111
|
+
],
|
|
2112
|
+
utils: [],
|
|
2113
|
+
composables: [],
|
|
2114
|
+
},
|
|
2115
2115
|
{
|
|
2116
2116
|
name: "VeeTextarea",
|
|
2117
2117
|
value: "vue-textarea",
|
package/src/types.ts
CHANGED
|
@@ -11,3 +11,23 @@ export type UIConfig = {
|
|
|
11
11
|
};
|
|
12
12
|
|
|
13
13
|
export type InitOptions = { force?: boolean };
|
|
14
|
+
|
|
15
|
+
export type Component = {
|
|
16
|
+
name: string;
|
|
17
|
+
value: string;
|
|
18
|
+
deps: string[];
|
|
19
|
+
devDeps: string[];
|
|
20
|
+
nuxtModules: string[];
|
|
21
|
+
instructions?: string[];
|
|
22
|
+
files: Composable[];
|
|
23
|
+
utils: Composable[];
|
|
24
|
+
composables: Composable[];
|
|
25
|
+
components?: string[];
|
|
26
|
+
askValidator?: boolean;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export type Composable = {
|
|
30
|
+
fileName: string;
|
|
31
|
+
dirPath: string;
|
|
32
|
+
fileContent: string;
|
|
33
|
+
};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import fse from "fs-extra";
|
|
2
|
+
import * as prompts from "prompts";
|
|
3
|
+
import * as execa from "execa";
|
|
4
|
+
import { afterEach, beforeAll, describe, expect, it, vi } from "vitest";
|
|
5
|
+
|
|
6
|
+
import * as testingFn from "../../src/utils/addPrettierConfig";
|
|
7
|
+
|
|
8
|
+
const currentDir = process.cwd();
|
|
9
|
+
const question = "A prettier config file already exists. Overwrite?";
|
|
10
|
+
const promptOptions = {
|
|
11
|
+
name: "overwrite",
|
|
12
|
+
type: "confirm",
|
|
13
|
+
message: question,
|
|
14
|
+
initial: true,
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
describe("utils/addPrettierConfig", () => {
|
|
18
|
+
afterEach(() => {
|
|
19
|
+
vi.restoreAllMocks();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it(
|
|
23
|
+
"should ask the user if they want to overwrite the existing prettier config file if one exists",
|
|
24
|
+
async () => {
|
|
25
|
+
vi.spyOn(fse, "existsSync").mockImplementation(() => true);
|
|
26
|
+
vi.mock('prompts', async () => {
|
|
27
|
+
const prompts = await import('prompts');
|
|
28
|
+
return {
|
|
29
|
+
...prompts,
|
|
30
|
+
default: async () => {
|
|
31
|
+
return { overwrite: false };
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const result = await testingFn.addPrettierConfig();
|
|
37
|
+
expect(result).toBe(false);
|
|
38
|
+
expect(fse.existsSync).toHaveBeenCalledTimes(1);
|
|
39
|
+
}
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
it('should create config file if one does not exist', async () => {
|
|
43
|
+
vi.spyOn(fse, "existsSync").mockImplementation(() => false);
|
|
44
|
+
vi.spyOn(fse, "writeFile").mockResolvedValue();
|
|
45
|
+
|
|
46
|
+
const result = await testingFn.addPrettierConfig(currentDir, false);
|
|
47
|
+
expect(result).toBe(true);
|
|
48
|
+
expect(fse.existsSync).toHaveBeenCalledTimes(1);
|
|
49
|
+
expect(fse.writeFile).toHaveBeenCalledTimes(1);
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
it('should format files with prettier if format is true', async () => {
|
|
53
|
+
vi.spyOn(execa, '$')
|
|
54
|
+
vi.spyOn(testingFn, 'addPrettierConfig')
|
|
55
|
+
vi.spyOn(fse, "existsSync").mockImplementation(() => false);
|
|
56
|
+
vi.spyOn(fse, "writeFile").mockResolvedValue();
|
|
57
|
+
vi.mock('execa', async () => {
|
|
58
|
+
const execa = await import('execa');
|
|
59
|
+
return {
|
|
60
|
+
...execa,
|
|
61
|
+
'$': async () => {
|
|
62
|
+
return true
|
|
63
|
+
},
|
|
64
|
+
default: async () => {
|
|
65
|
+
return true
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const result = await testingFn.addPrettierConfig(currentDir, true);
|
|
71
|
+
expect(result).toBe(true);
|
|
72
|
+
expect(fse.existsSync).toHaveBeenCalledTimes(1);
|
|
73
|
+
expect(fse.writeFile).toHaveBeenCalledTimes(1);
|
|
74
|
+
expect(execa.$).toHaveBeenCalledTimes(1);
|
|
75
|
+
expect(testingFn.addPrettierConfig).toHaveBeenCalledTimes(1);
|
|
76
|
+
|
|
77
|
+
})
|
|
78
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
4
|
+
|
|
5
|
+
import * as testingFn from "../../src/utils/fileExists";
|
|
6
|
+
|
|
7
|
+
const currentDir = process.cwd();
|
|
8
|
+
const badPath = path.join(currentDir, "test");
|
|
9
|
+
const goodPath = path.join(currentDir, "tests/utils/fileExists.test.ts");
|
|
10
|
+
|
|
11
|
+
describe("utils/fileExists", () => {
|
|
12
|
+
afterEach(() => {
|
|
13
|
+
vi.restoreAllMocks();
|
|
14
|
+
});
|
|
15
|
+
it("should return true if file exists", async () => {
|
|
16
|
+
const spy = vi.spyOn(fs.promises, "access");
|
|
17
|
+
const fnSpy = vi.spyOn(testingFn, "fileExists");
|
|
18
|
+
|
|
19
|
+
const exists = await testingFn.fileExists(goodPath);
|
|
20
|
+
expect(fnSpy).toHaveBeenCalledWith(goodPath);
|
|
21
|
+
expect(spy).toHaveBeenCalledWith(goodPath, fs.constants.F_OK || fs.constants.W_OK);
|
|
22
|
+
expect(fnSpy).toBeCalledTimes(1);
|
|
23
|
+
expect(spy).toBeCalledTimes(1);
|
|
24
|
+
expect(exists).toBe(true);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("should return false if file does not exist", async () => {
|
|
28
|
+
const spy = vi.spyOn(fs.promises, "access");
|
|
29
|
+
const fnSpy = vi.spyOn(testingFn, "fileExists");
|
|
30
|
+
|
|
31
|
+
const exists = await testingFn.fileExists(badPath);
|
|
32
|
+
expect(fnSpy).toHaveBeenCalledWith(badPath);
|
|
33
|
+
expect(spy).toHaveBeenCalledWith(badPath, fs.constants.F_OK || fs.constants.W_OK);
|
|
34
|
+
expect(fnSpy).toBeCalledTimes(1);
|
|
35
|
+
expect(spy).toBeCalledTimes(1);
|
|
36
|
+
expect(exists).toBe(false);
|
|
37
|
+
});
|
|
38
|
+
});
|