mirror of
https://github.com/shadcn-ui/ui.git
synced 2026-06-23 12:45:47 +00:00
Compare commits
1 Commits
fix/cli-va
...
shadcn/cli
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f5b964460f |
@@ -85,6 +85,11 @@
|
|||||||
"target": {
|
"target": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "The target path of the file. This is the path to the file in the project."
|
"description": "The target path of the file. This is the path to the file in the project."
|
||||||
|
},
|
||||||
|
"action": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["append", "prepend"],
|
||||||
|
"description": "The action to perform on the target file. Can be append or prepend."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"if": {
|
"if": {
|
||||||
|
|||||||
@@ -65,6 +65,10 @@ export const build = new Command()
|
|||||||
|
|
||||||
// Loop through each file in the files array.
|
// Loop through each file in the files array.
|
||||||
for (const file of registryItem.files) {
|
for (const file of registryItem.files) {
|
||||||
|
if (file["content"]) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
file["content"] = await fs.readFile(
|
file["content"] = await fs.readFile(
|
||||||
path.resolve(resolvePaths.cwd, file.path),
|
path.resolve(resolvePaths.cwd, file.path),
|
||||||
"utf-8"
|
"utf-8"
|
||||||
|
|||||||
@@ -19,6 +19,10 @@ export const registryItemTypeSchema = z.enum([
|
|||||||
"registry:internal",
|
"registry:internal",
|
||||||
])
|
])
|
||||||
|
|
||||||
|
export const registryItemFileActionSchema = z
|
||||||
|
.enum(["append", "prepend"])
|
||||||
|
.optional()
|
||||||
|
|
||||||
export const registryItemFileSchema = z.discriminatedUnion("type", [
|
export const registryItemFileSchema = z.discriminatedUnion("type", [
|
||||||
// Target is required for registry:file and registry:page
|
// Target is required for registry:file and registry:page
|
||||||
z.object({
|
z.object({
|
||||||
@@ -26,12 +30,14 @@ export const registryItemFileSchema = z.discriminatedUnion("type", [
|
|||||||
content: z.string().optional(),
|
content: z.string().optional(),
|
||||||
type: z.enum(["registry:file", "registry:page"]),
|
type: z.enum(["registry:file", "registry:page"]),
|
||||||
target: z.string(),
|
target: z.string(),
|
||||||
|
action: registryItemFileActionSchema,
|
||||||
}),
|
}),
|
||||||
z.object({
|
z.object({
|
||||||
path: z.string(),
|
path: z.string(),
|
||||||
content: z.string().optional(),
|
content: z.string().optional(),
|
||||||
type: registryItemTypeSchema.exclude(["registry:file", "registry:page"]),
|
type: registryItemTypeSchema.exclude(["registry:file", "registry:page"]),
|
||||||
target: z.string().optional(),
|
target: z.string().optional(),
|
||||||
|
action: registryItemFileActionSchema,
|
||||||
}),
|
}),
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
import { existsSync, promises as fs } from "fs"
|
import { existsSync, promises as fs } from "fs"
|
||||||
import path, { basename } from "path"
|
import path, { basename } from "path"
|
||||||
import { getRegistryBaseColor } from "@/src/registry/api"
|
import { getRegistryBaseColor } from "@/src/registry/api"
|
||||||
import { RegistryItem, registryItemFileSchema } from "@/src/registry/schema"
|
import {
|
||||||
|
RegistryItem,
|
||||||
|
registryItemFileActionSchema,
|
||||||
|
registryItemFileSchema,
|
||||||
|
} from "@/src/registry/schema"
|
||||||
import { Config } from "@/src/utils/get-config"
|
import { Config } from "@/src/utils/get-config"
|
||||||
import { ProjectInfo, getProjectInfo } from "@/src/utils/get-project-info"
|
import { ProjectInfo, getProjectInfo } from "@/src/utils/get-project-info"
|
||||||
import { highlighter } from "@/src/utils/highlighter"
|
import { highlighter } from "@/src/utils/highlighter"
|
||||||
@@ -16,6 +20,31 @@ import { transformTwPrefixes } from "@/src/utils/transformers/transform-tw-prefi
|
|||||||
import prompts from "prompts"
|
import prompts from "prompts"
|
||||||
import { z } from "zod"
|
import { z } from "zod"
|
||||||
|
|
||||||
|
async function applyFileAction(
|
||||||
|
filePath: string,
|
||||||
|
content: string,
|
||||||
|
action: z.infer<typeof registryItemFileActionSchema>
|
||||||
|
) {
|
||||||
|
if (!action) {
|
||||||
|
return content
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only try to read existing content if the file exists
|
||||||
|
if (existsSync(filePath)) {
|
||||||
|
const existingContent = await fs.readFile(filePath, "utf-8")
|
||||||
|
|
||||||
|
if (action === "append") {
|
||||||
|
return `${existingContent}\n${content}`
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action === "prepend") {
|
||||||
|
return `${content}\n${existingContent}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return content
|
||||||
|
}
|
||||||
|
|
||||||
export async function updateFiles(
|
export async function updateFiles(
|
||||||
files: RegistryItem["files"],
|
files: RegistryItem["files"],
|
||||||
config: Config,
|
config: Config,
|
||||||
@@ -109,10 +138,29 @@ export async function updateFiles(
|
|||||||
getNormalizedFileContent(existingFileContent),
|
getNormalizedFileContent(existingFileContent),
|
||||||
getNormalizedFileContent(content),
|
getNormalizedFileContent(content),
|
||||||
])
|
])
|
||||||
|
|
||||||
|
// Check if content is already the same
|
||||||
if (normalizedExisting === normalizedNew) {
|
if (normalizedExisting === normalizedNew) {
|
||||||
filesSkipped.push(path.relative(config.resolvedPaths.cwd, filePath))
|
filesSkipped.push(path.relative(config.resolvedPaths.cwd, filePath))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Also check if action is already applied
|
||||||
|
if (file.action) {
|
||||||
|
if (file.action === "append") {
|
||||||
|
// Check if the content is already appended
|
||||||
|
if (normalizedExisting.endsWith(normalizedNew)) {
|
||||||
|
filesSkipped.push(path.relative(config.resolvedPaths.cwd, filePath))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
} else if (file.action === "prepend") {
|
||||||
|
// Check if the content is already prepended
|
||||||
|
if (normalizedExisting.startsWith(normalizedNew)) {
|
||||||
|
filesSkipped.push(path.relative(config.resolvedPaths.cwd, filePath))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (existingFile && !options.overwrite) {
|
if (existingFile && !options.overwrite) {
|
||||||
@@ -147,10 +195,24 @@ export async function updateFiles(
|
|||||||
await fs.mkdir(targetDir, { recursive: true })
|
await fs.mkdir(targetDir, { recursive: true })
|
||||||
}
|
}
|
||||||
|
|
||||||
await fs.writeFile(filePath, content, "utf-8")
|
let finalContent = content
|
||||||
existingFile
|
if (existingFile && file.action) {
|
||||||
? filesUpdated.push(path.relative(config.resolvedPaths.cwd, filePath))
|
const existingFileContent = await fs.readFile(filePath, "utf-8")
|
||||||
: filesCreated.push(path.relative(config.resolvedPaths.cwd, filePath))
|
if (file.action === "append") {
|
||||||
|
finalContent = `${existingFileContent}\n${content}`
|
||||||
|
} else if (file.action === "prepend") {
|
||||||
|
finalContent = `${content}\n${existingFileContent}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await fs.writeFile(filePath, finalContent, "utf-8")
|
||||||
|
|
||||||
|
const relativePath = path.relative(config.resolvedPaths.cwd, filePath)
|
||||||
|
if (existingFile) {
|
||||||
|
filesUpdated.push(relativePath)
|
||||||
|
} else {
|
||||||
|
filesCreated.push(relativePath)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasUpdatedFiles = filesCreated.length || filesUpdated.length
|
const hasUpdatedFiles = filesCreated.length || filesUpdated.length
|
||||||
@@ -192,7 +254,7 @@ export async function updateFiles(
|
|||||||
if (filesSkipped.length) {
|
if (filesSkipped.length) {
|
||||||
spinner(
|
spinner(
|
||||||
`Skipped ${filesSkipped.length} ${
|
`Skipped ${filesSkipped.length} ${
|
||||||
filesUpdated.length === 1 ? "file" : "files"
|
filesSkipped.length === 1 ? "file" : "files"
|
||||||
}: (files might be identical, use --overwrite to overwrite)`,
|
}: (files might be identical, use --overwrite to overwrite)`,
|
||||||
{
|
{
|
||||||
silent: options.silent,
|
silent: options.silent,
|
||||||
@@ -332,7 +394,10 @@ export function resolveNestedFilePath(
|
|||||||
return fileSegments.slice(commonDirIndex + 1).join("/")
|
return fileSegments.slice(commonDirIndex + 1).join("/")
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getNormalizedFileContent(content: string) {
|
export async function getNormalizedFileContent(content: string | undefined) {
|
||||||
|
if (!content) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
return content.replace(/\r\n/g, "\n").trim()
|
return content.replace(/\r\n/g, "\n").trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,17 @@
|
|||||||
|
import { existsSync, promises as fs } from "fs"
|
||||||
import path from "path"
|
import path from "path"
|
||||||
import { afterAll, afterEach, describe, expect, test, vi } from "vitest"
|
import {
|
||||||
|
afterAll,
|
||||||
|
afterEach,
|
||||||
|
beforeEach,
|
||||||
|
describe,
|
||||||
|
expect,
|
||||||
|
test,
|
||||||
|
vi,
|
||||||
|
} from "vitest"
|
||||||
|
|
||||||
import { getConfig } from "../../../src/utils/get-config"
|
import { getConfig } from "../../../src/utils/get-config"
|
||||||
|
import * as transformers from "../../../src/utils/transformers"
|
||||||
import {
|
import {
|
||||||
findCommonRoot,
|
findCommonRoot,
|
||||||
resolveFilePath,
|
resolveFilePath,
|
||||||
@@ -9,12 +19,17 @@ import {
|
|||||||
updateFiles,
|
updateFiles,
|
||||||
} from "../../../src/utils/updaters/update-files"
|
} from "../../../src/utils/updaters/update-files"
|
||||||
|
|
||||||
|
vi.mock("../../../src/utils/transformers", () => ({
|
||||||
|
transform: vi.fn().mockImplementation((opts) => Promise.resolve(opts.raw)),
|
||||||
|
}))
|
||||||
|
|
||||||
vi.mock("fs/promises", async () => {
|
vi.mock("fs/promises", async () => {
|
||||||
const actual = (await vi.importActual(
|
const actual = (await vi.importActual(
|
||||||
"fs/promises"
|
"fs/promises"
|
||||||
)) as typeof import("fs/promises")
|
)) as typeof import("fs/promises")
|
||||||
return {
|
return {
|
||||||
...actual,
|
...actual,
|
||||||
|
readFile: vi.fn(),
|
||||||
writeFile: vi.fn(),
|
writeFile: vi.fn(),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -23,8 +38,10 @@ vi.mock("fs", async () => {
|
|||||||
const actual = (await vi.importActual("fs")) as typeof import("fs")
|
const actual = (await vi.importActual("fs")) as typeof import("fs")
|
||||||
return {
|
return {
|
||||||
...actual,
|
...actual,
|
||||||
|
existsSync: vi.fn(),
|
||||||
promises: {
|
promises: {
|
||||||
...actual.promises,
|
...actual.promises,
|
||||||
|
readFile: vi.fn(),
|
||||||
writeFile: vi.fn(),
|
writeFile: vi.fn(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -731,6 +748,30 @@ describe("updateFiles", () => {
|
|||||||
const config = await getConfig(
|
const config = await getConfig(
|
||||||
path.resolve(__dirname, "../../fixtures/vite-with-tailwind")
|
path.resolve(__dirname, "../../fixtures/vite-with-tailwind")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Set up mocks for transform
|
||||||
|
vi.mocked(transformers.transform).mockImplementation((opts) => {
|
||||||
|
if (opts.raw.includes("Button")) {
|
||||||
|
return Promise.resolve(opts.raw)
|
||||||
|
}
|
||||||
|
return Promise.resolve(opts.raw)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Mock existsSync to check for existing files
|
||||||
|
vi.mocked(existsSync).mockImplementation((path) => {
|
||||||
|
return path.toString().includes("button.tsx")
|
||||||
|
})
|
||||||
|
|
||||||
|
// Mock readFile to return content for comparison
|
||||||
|
vi.mocked(fs.readFile).mockImplementation((path) => {
|
||||||
|
if (path.toString().includes("button.tsx")) {
|
||||||
|
return Promise.resolve(`export function Button() {
|
||||||
|
return <button>Click me</button>
|
||||||
|
}`)
|
||||||
|
}
|
||||||
|
return Promise.resolve("")
|
||||||
|
})
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
await updateFiles(
|
await updateFiles(
|
||||||
[
|
[
|
||||||
@@ -772,6 +813,27 @@ return <div>Hello World</div>
|
|||||||
const config = await getConfig(
|
const config = await getConfig(
|
||||||
path.resolve(__dirname, "../../fixtures/vite-with-tailwind")
|
path.resolve(__dirname, "../../fixtures/vite-with-tailwind")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Set up mocks for transform
|
||||||
|
vi.mocked(transformers.transform).mockImplementation((opts) => {
|
||||||
|
return Promise.resolve(opts.raw)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Mock existsSync to check for existing files
|
||||||
|
vi.mocked(existsSync).mockImplementation((path) => {
|
||||||
|
return path.toString().includes("button.tsx")
|
||||||
|
})
|
||||||
|
|
||||||
|
// Mock readFile to return content for comparison
|
||||||
|
vi.mocked(fs.readFile).mockImplementation((path) => {
|
||||||
|
if (path.toString().includes("button.tsx")) {
|
||||||
|
return Promise.resolve(`export function Button() {
|
||||||
|
return <button>I'm different</button>
|
||||||
|
}`)
|
||||||
|
}
|
||||||
|
return Promise.resolve("")
|
||||||
|
})
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
await updateFiles(
|
await updateFiles(
|
||||||
[
|
[
|
||||||
@@ -809,3 +871,248 @@ return <div>Hello World</div>
|
|||||||
`)
|
`)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe("file actions", () => {
|
||||||
|
test("should append content to existing file", async () => {
|
||||||
|
// Set up mocks
|
||||||
|
vi.mocked(transformers.transform).mockResolvedValue("new-content")
|
||||||
|
vi.mocked(existsSync)
|
||||||
|
.mockReturnValueOnce(true) // First check for file existence
|
||||||
|
.mockReturnValueOnce(true) // Directory exists check
|
||||||
|
|
||||||
|
const existingContent = "existing-content"
|
||||||
|
vi.mocked(fs.readFile)
|
||||||
|
.mockResolvedValueOnce(existingContent) // For content comparison check
|
||||||
|
.mockResolvedValueOnce(existingContent) // For append operation
|
||||||
|
|
||||||
|
const config = await getConfig(
|
||||||
|
path.resolve(__dirname, "../../fixtures/vite-with-tailwind")
|
||||||
|
)
|
||||||
|
|
||||||
|
const result = await updateFiles(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
path: "components/test.tsx",
|
||||||
|
type: "registry:component",
|
||||||
|
content: "original-content", // This will be transformed to "new-content"
|
||||||
|
action: "append",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
config,
|
||||||
|
{
|
||||||
|
overwrite: true,
|
||||||
|
silent: true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Check the file was updated not created
|
||||||
|
expect(result.filesUpdated).toHaveLength(1)
|
||||||
|
expect(result.filesCreated).toHaveLength(0)
|
||||||
|
expect(result.filesSkipped).toHaveLength(0)
|
||||||
|
|
||||||
|
// Check writeFile was called correctly for append
|
||||||
|
expect(fs.writeFile).toHaveBeenCalledWith(
|
||||||
|
expect.any(String),
|
||||||
|
`${existingContent}\nnew-content`,
|
||||||
|
"utf-8"
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test("should prepend content to existing file", async () => {
|
||||||
|
// Set up mocks
|
||||||
|
vi.mocked(transformers.transform).mockResolvedValue("new-content")
|
||||||
|
vi.mocked(existsSync)
|
||||||
|
.mockReturnValueOnce(true) // First check for file existence
|
||||||
|
.mockReturnValueOnce(true) // Directory exists check
|
||||||
|
|
||||||
|
const existingContent = "existing-content"
|
||||||
|
vi.mocked(fs.readFile)
|
||||||
|
.mockResolvedValueOnce(existingContent) // For content comparison check
|
||||||
|
.mockResolvedValueOnce(existingContent) // For prepend operation
|
||||||
|
|
||||||
|
const config = await getConfig(
|
||||||
|
path.resolve(__dirname, "../../fixtures/vite-with-tailwind")
|
||||||
|
)
|
||||||
|
|
||||||
|
const result = await updateFiles(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
path: "components/test.tsx",
|
||||||
|
type: "registry:component",
|
||||||
|
content: "original-content", // This will be transformed to "new-content"
|
||||||
|
action: "prepend",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
config,
|
||||||
|
{
|
||||||
|
overwrite: true,
|
||||||
|
silent: true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Check the file was updated not created
|
||||||
|
expect(result.filesUpdated).toHaveLength(1)
|
||||||
|
expect(result.filesCreated).toHaveLength(0)
|
||||||
|
expect(result.filesSkipped).toHaveLength(0)
|
||||||
|
|
||||||
|
// Check writeFile was called correctly for prepend
|
||||||
|
expect(fs.writeFile).toHaveBeenCalledWith(
|
||||||
|
expect.any(String),
|
||||||
|
`new-content\n${existingContent}`,
|
||||||
|
"utf-8"
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test("should update file when content is different", async () => {
|
||||||
|
// Set up mocks
|
||||||
|
vi.mocked(transformers.transform).mockResolvedValue("new-content")
|
||||||
|
vi.mocked(existsSync)
|
||||||
|
.mockReturnValueOnce(true) // First check for file existence
|
||||||
|
.mockReturnValueOnce(true) // Directory exists check
|
||||||
|
|
||||||
|
const existingContent = "existing-content"
|
||||||
|
vi.mocked(fs.readFile).mockResolvedValueOnce(existingContent) // For content comparison
|
||||||
|
|
||||||
|
const config = await getConfig(
|
||||||
|
path.resolve(__dirname, "../../fixtures/vite-with-tailwind")
|
||||||
|
)
|
||||||
|
|
||||||
|
const result = await updateFiles(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
path: "components/test.tsx",
|
||||||
|
type: "registry:component",
|
||||||
|
content: "original-content", // This will be transformed to "new-content"
|
||||||
|
},
|
||||||
|
],
|
||||||
|
config,
|
||||||
|
{
|
||||||
|
overwrite: true,
|
||||||
|
silent: true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Check the file was updated not created
|
||||||
|
expect(result.filesUpdated).toHaveLength(1)
|
||||||
|
expect(result.filesCreated).toHaveLength(0)
|
||||||
|
expect(result.filesSkipped).toHaveLength(0)
|
||||||
|
|
||||||
|
// Check writeFile was called with new content
|
||||||
|
expect(fs.writeFile).toHaveBeenCalledWith(
|
||||||
|
expect.any(String),
|
||||||
|
"new-content",
|
||||||
|
"utf-8"
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test("should skip file when content is the same", async () => {
|
||||||
|
// Set up mocks
|
||||||
|
const content = "same-content"
|
||||||
|
vi.mocked(transformers.transform).mockResolvedValue(content)
|
||||||
|
vi.mocked(existsSync).mockReturnValue(true)
|
||||||
|
vi.mocked(fs.readFile).mockResolvedValue(content)
|
||||||
|
|
||||||
|
const config = await getConfig(
|
||||||
|
path.resolve(__dirname, "../../fixtures/vite-with-tailwind")
|
||||||
|
)
|
||||||
|
|
||||||
|
const result = await updateFiles(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
path: "components/test.tsx",
|
||||||
|
type: "registry:component",
|
||||||
|
content,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
config,
|
||||||
|
{
|
||||||
|
overwrite: true,
|
||||||
|
silent: true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Check the file was skipped
|
||||||
|
expect(result.filesSkipped).toHaveLength(1)
|
||||||
|
expect(result.filesUpdated).toHaveLength(0)
|
||||||
|
expect(result.filesCreated).toHaveLength(0)
|
||||||
|
|
||||||
|
// Verify writeFile was not called
|
||||||
|
expect(fs.writeFile).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
test("should skip file when content is already appended", async () => {
|
||||||
|
// Set up mocks
|
||||||
|
const newContent = "new-content"
|
||||||
|
const existingContent = "existing-content\nnew-content" // Already has the content appended
|
||||||
|
|
||||||
|
vi.mocked(transformers.transform).mockResolvedValue(newContent)
|
||||||
|
vi.mocked(existsSync).mockReturnValue(true)
|
||||||
|
vi.mocked(fs.readFile).mockResolvedValue(existingContent)
|
||||||
|
|
||||||
|
const config = await getConfig(
|
||||||
|
path.resolve(__dirname, "../../fixtures/vite-with-tailwind")
|
||||||
|
)
|
||||||
|
|
||||||
|
const result = await updateFiles(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
path: "components/test.tsx",
|
||||||
|
type: "registry:component",
|
||||||
|
content: newContent,
|
||||||
|
action: "append",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
config,
|
||||||
|
{
|
||||||
|
overwrite: true,
|
||||||
|
silent: true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// The file should be skipped because the content is already appended
|
||||||
|
expect(result.filesSkipped).toHaveLength(1)
|
||||||
|
expect(result.filesUpdated).toHaveLength(0)
|
||||||
|
expect(result.filesCreated).toHaveLength(0)
|
||||||
|
|
||||||
|
// Verify writeFile was not called
|
||||||
|
expect(fs.writeFile).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
test("should skip file when content is already prepended", async () => {
|
||||||
|
// Set up mocks
|
||||||
|
const newContent = "new-content"
|
||||||
|
const existingContent = "new-content\nexisting-content" // Already has the content prepended
|
||||||
|
|
||||||
|
vi.mocked(transformers.transform).mockResolvedValue(newContent)
|
||||||
|
vi.mocked(existsSync).mockReturnValue(true)
|
||||||
|
vi.mocked(fs.readFile).mockResolvedValue(existingContent)
|
||||||
|
|
||||||
|
const config = await getConfig(
|
||||||
|
path.resolve(__dirname, "../../fixtures/vite-with-tailwind")
|
||||||
|
)
|
||||||
|
|
||||||
|
const result = await updateFiles(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
path: "components/test.tsx",
|
||||||
|
type: "registry:component",
|
||||||
|
content: newContent,
|
||||||
|
action: "prepend",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
config,
|
||||||
|
{
|
||||||
|
overwrite: true,
|
||||||
|
silent: true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// The file should be skipped because the content is already prepended
|
||||||
|
expect(result.filesSkipped).toHaveLength(1)
|
||||||
|
expect(result.filesUpdated).toHaveLength(0)
|
||||||
|
expect(result.filesCreated).toHaveLength(0)
|
||||||
|
|
||||||
|
// Verify writeFile was not called
|
||||||
|
expect(fs.writeFile).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user