-
Notifications
You must be signed in to change notification settings - Fork 41
fix(file): json/yaml content on create #213
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
commit: |
| params = { | ||
| fsPath: newFsPath, | ||
| content: `# ${upperFirst(state.name)} file`, | ||
| content: getInitialContent(state.extension!, upperFirst(state.name)), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code uses a non-null assertion on state.extension when calling getInitialContent(), but the extension can be undefined when a default extension is not provided (e.g., in MediaCardForm), causing a type mismatch at runtime.
View Details
📝 Patch Details
diff --git a/src/app/src/components/shared/item/ItemCardForm.vue b/src/app/src/components/shared/item/ItemCardForm.vue
index 9a98d87..16af581 100644
--- a/src/app/src/components/shared/item/ItemCardForm.vue
+++ b/src/app/src/components/shared/item/ItemCardForm.vue
@@ -60,46 +60,61 @@ const fullName = computed(() => {
return isDirectory.value ? prefixedName : `${prefixedName}.${state.extension}`
})
-const schema = computed(() => z.object({
- name: z.string()
- .min(1, t('studio.validation.nameEmpty'))
- .refine((name: string) => !name.endsWith('.'), t('studio.validation.nameEndsWithDot'))
- .refine((name: string) => !name.startsWith('/'), t('studio.validation.nameStartsWithSlash')),
- extension: z.enum([...CONTENT_EXTENSIONS, ...MEDIA_EXTENSIONS] as [string, ...string[]]).nullish(),
- prefix: z.preprocess(
- val => val === '' ? null : val,
- z.string()
- .regex(/^\d+$/, t('studio.validation.prefixDigitsOnly'))
+const schema = computed(() => {
+ // For file creation/renaming, extension is required; for folders, it's not applicable
+ const extensionSchema = isDirectory.value
+ ? z.enum([...CONTENT_EXTENSIONS, ...MEDIA_EXTENSIONS] as [string, ...string[]]).nullish()
+ : z.union([
+ z.enum([...CONTENT_EXTENSIONS, ...MEDIA_EXTENSIONS] as [string, ...string[]]),
+ z.null(),
+ z.undefined(),
+ ])
.refine(
- (prefix: string | null | undefined) => {
- if (prefix === null || prefix === undefined) {
- return true
- }
+ (ext: string | null | undefined) => ext !== null && ext !== undefined,
+ { message: t('studio.validation.extensionRequired') },
+ )
- const num = Number(prefix)
+ return z.object({
+ name: z.string()
+ .min(1, t('studio.validation.nameEmpty'))
+ .refine((name: string) => !name.endsWith('.'), t('studio.validation.nameEndsWithDot'))
+ .refine((name: string) => !name.startsWith('/'), t('studio.validation.nameStartsWithSlash')),
+ extension: extensionSchema,
+ prefix: z.preprocess(
+ val => val === '' ? null : val,
+ z.string()
+ .regex(/^\d+$/, t('studio.validation.prefixDigitsOnly'))
+ .refine(
+ (prefix: string | null | undefined) => {
+ if (prefix === null || prefix === undefined) {
+ return true
+ }
+
+ const num = Number(prefix)
+
+ return Number.isInteger(num) && num >= 0
+ },
+ t('studio.validation.prefixNonNegativeInteger'),
+ )
+ .nullish(),
+ ),
+ }).refine(() => {
+ const siblings = props.parentItem.children?.filter(child => !child.hide) || []
+
+ const isDuplicate = siblings.some((sibling) => {
+ const siblingBaseName = sibling.fsPath.split('/').pop()
+ if (props.renamedItem && sibling.fsPath === props.renamedItem.fsPath) {
+ return false
+ }
+ return siblingBaseName === fullName.value
+ })
- return Number.isInteger(num) && num >= 0
- },
- t('studio.validation.prefixNonNegativeInteger'),
- )
- .nullish(),
- ),
-}).refine(() => {
- const siblings = props.parentItem.children?.filter(child => !child.hide) || []
-
- const isDuplicate = siblings.some((sibling) => {
- const siblingBaseName = sibling.fsPath.split('/').pop()
- if (props.renamedItem && sibling.fsPath === props.renamedItem.fsPath) {
- return false
- }
- return siblingBaseName === fullName.value
+ return !isDuplicate
+ }, {
+ message: t('studio.validation.nameExists'),
+ path: ['name'],
})
-
- return !isDuplicate
-}, {
- message: t('studio.validation.nameExists'),
- path: ['name'],
-}))
+})
type SchemaType = {
name: string
@@ -225,7 +240,7 @@ async function onSubmit() {
case StudioItemActionId.CreateDocument:
params = {
fsPath: newFsPath,
- content: getInitialContent(state.extension!, upperFirst(state.name)),
+ content: getInitialContent(state.extension as string, upperFirst(state.name)),
}
break
case StudioItemActionId.RenameItem:
Analysis
Extension validation missing in ItemCardForm allows undefined extension for file creation
What fails: ItemCardForm accepts undefined extension when creating media files, allowing invalid file creation with extensions like "myvideo.undefined" and passing undefined to getInitialContent() which expects a string parameter.
How to reproduce:
- In MediaCardForm (which uses ItemCardForm with
config: { allowed: MEDIA_EXTENSIONS, editable: false }- no default) - Attempt to create a new document (not rename, where renamedItem = null)
- The extension field is disabled (editable: false), so user cannot select an extension
- originalExtension computes to
props.config.defaultwhich is undefined - The Zod schema allows
z.enum(...).nullish()- validation passes with undefined - Submit button is enabled because validation passes
- On submit with CreateDocument action, calls
getInitialContent(state.extension!, ...)with undefined
Result:
- File created with name like "myvideo.undefined"
- getInitialContent receives undefined instead of string
- Type assertion masks the issue but doesn't prevent runtime problem
Expected:
- Extension should be required when creating files
- Form validation should prevent submission without a valid extension
- Follows the pattern in ContentCardForm which provides a default
Fix applied: Modified src/app/src/components/shared/item/ItemCardForm.vue to conditionally require extension:
- For file creation/renaming (!isDirectory): extension must be a valid string value
- For folder creation (isDirectory): extension can be null/undefined
- Validation now prevents form submission without selecting a valid extension
Resolves #207