diff --git a/packages/graphql/templates/mutations.ts b/packages/graphql/templates/mutations.ts index 778887db..b7eebf9e 100644 --- a/packages/graphql/templates/mutations.ts +++ b/packages/graphql/templates/mutations.ts @@ -34,6 +34,8 @@ updateMovie(input: UpdateMovieInput) : MovieOutput */ export const updateMutationType = (typeName) => `update${typeName}`; +export const updateOperationName = (model: VulcanGraphqlModel) => + updateMutationType(model.graphql.typeName); export const updateMutationTemplate = ({ typeName }) => `${updateMutationType(typeName)}( input: ${updateInputType(typeName)}, diff --git a/packages/graphql/typings.ts b/packages/graphql/typings.ts index 36ec85a8..f136d891 100644 --- a/packages/graphql/typings.ts +++ b/packages/graphql/typings.ts @@ -128,13 +128,16 @@ export interface ApolloVariables { } // Mutation/Hooks typings -export interface CreateInput { +interface CommonInput { + contextName?: string; +} +export interface CreateInput extends CommonInput { data: TModel; } export interface CreateVariables { input: CreateInput; } -export interface UpdateInput { +export interface UpdateInput extends CommonInput { data: TModel; id?: string; } @@ -142,6 +145,13 @@ export interface UpdateVariables { input: UpdateInput; } +export interface DeleteInput extends CommonInput { + id: string; +} +export interface DeleteVariables { + input: DeleteInput; +} + // Filtering and selectors type VulcanSelectorSortOption = "asc" | "desc"; diff --git a/packages/react-hooks/delete.ts b/packages/react-hooks/delete.ts index b1797fa1..3c43a8e8 100644 --- a/packages/react-hooks/delete.ts +++ b/packages/react-hooks/delete.ts @@ -39,6 +39,7 @@ export const buildDeleteQuery = ({ typeName, fragmentName, fragment }) => `; import { multiQueryUpdater, ComputeNewDataFunc } from "./multiQueryUpdater"; +import { DeleteVariables } from "@vulcanjs/graphql"; /** * Compute new list for removed elements @@ -63,12 +64,6 @@ const multiQueryUpdaterAfterDelete = multiQueryUpdater( computeNewDataAfterDelete ); -interface DeleteInput { - id: string; -} -interface DeleteVariables { - input: DeleteInput; -} interface UseDeleteOptions extends VulcanMutationHookOptions, Partial {} diff --git a/packages/react-hooks/index.ts b/packages/react-hooks/index.ts index acb1e601..95015559 100644 --- a/packages/react-hooks/index.ts +++ b/packages/react-hooks/index.ts @@ -3,5 +3,5 @@ export type { UseSingleOptions } from "./single"; export { useMulti } from "./multi"; export { useCreate, buildCreateQuery } from "./create"; export { useDelete } from "./delete"; -export { useUpdate } from "./update"; +export { useUpdate, buildUpdateQuery } from "./update"; export { useUpsert } from "./upsert"; diff --git a/packages/react-hooks/multi.ts b/packages/react-hooks/multi.ts index 918aca23..4f284dd0 100644 --- a/packages/react-hooks/multi.ts +++ b/packages/react-hooks/multi.ts @@ -124,24 +124,23 @@ export const buildMultiQueryOptions = ( * Query updater after a fetch more * @param resolverName */ -export const fetchMoreUpdateQuery = (resolverName: string) => ( - previousResults, - { fetchMoreResult } -) => { - // no more post to fetch - if (!fetchMoreResult[resolverName]?.results?.length) { - return previousResults; - } - const newResults = { - ...previousResults, - [resolverName]: { ...previousResults[resolverName] }, +export const fetchMoreUpdateQuery = + (resolverName: string) => + (previousResults, { fetchMoreResult }) => { + // no more post to fetch + if (!fetchMoreResult[resolverName]?.results?.length) { + return previousResults; + } + const newResults = { + ...previousResults, + [resolverName]: { ...previousResults[resolverName] }, + }; + newResults[resolverName].results = [ + ...previousResults[resolverName].results, + ...fetchMoreResult[resolverName].results, + ]; + return newResults; }; - newResults[resolverName].results = [ - ...previousResults[resolverName].results, - ...fetchMoreResult[resolverName].results, - ]; - return newResults; -}; const buildMultiResult = ( options: UseMultiOptions, @@ -155,9 +154,8 @@ const buildMultiResult = ( const graphQLErrors = get(queryResult, "error.networkError.result.errors"); const { refetch, networkStatus, error, fetchMore, data } = queryResult; // Note: Scalar types like Dates are NOT converted. It should be done at the UI level. - const documents = data && data[resolverName] && data[resolverName].results; - const totalCount = - data && data[resolverName] && data[resolverName].totalCount; + const documents = data?.[resolverName]?.results; + const totalCount = data?.[resolverName]?.data[resolverName]?.totalCount; // see https://github.com/apollographql/apollo-client/blob/master/packages/apollo-client/src/core/networkStatus.ts const loadingInitial = networkStatus === 1; const loadingMore = networkStatus === 3 || networkStatus === 2; diff --git a/packages/react-hooks/single.ts b/packages/react-hooks/single.ts index 447bdc7a..2e7f0ad8 100644 --- a/packages/react-hooks/single.ts +++ b/packages/react-hooks/single.ts @@ -99,7 +99,7 @@ const buildSingleResult = ( const result = { ...queryResult, // Note: Scalar types like Dates are NOT converted. It should be done at the UI level. - document: data && data[resolverName] && data[resolverName].result, + document: data?.[resolverName]?.result, fragmentName, fragment, }; @@ -140,7 +140,7 @@ export const useSingle = ( extraQueries, } = options; - const { typeName, singleResolverName: resolverName } = model.graphql; + const { singleResolverName: resolverName } = model.graphql; const query = buildSingleQuery({ model, diff --git a/packages/react-hooks/update.ts b/packages/react-hooks/update.ts index d4c028be..7bb88416 100644 --- a/packages/react-hooks/update.ts +++ b/packages/react-hooks/update.ts @@ -29,13 +29,17 @@ import { useMutation, MutationResult, gql, FetchResult } from "@apollo/client"; -import { updateClientTemplate } from "@vulcanjs/graphql"; +import { + Fragment, + getModelFragment, + updateClientTemplate, +} from "@vulcanjs/graphql"; import { multiQueryUpdater, ComputeNewDataFunc } from "./multiQueryUpdater"; // import { computeQueryVariables } from "./variables"; import { computeNewDataAfterCreate } from "./create"; import { VulcanMutationHookOptions } from "./typings"; -import { UpdateVariables } from "@vulcanjs/graphql"; // TODO: import client code only +import { UpdateVariables, VulcanGraphqlModel } from "@vulcanjs/graphql"; // TODO: import client code only // We can reuse the same function to compute the new list after an element update const computeNewDataAfterUpdate: ComputeNewDataFunc = computeNewDataAfterCreate; @@ -44,11 +48,26 @@ const multiQueryUpdaterAfterUpdate = multiQueryUpdater( computeNewDataAfterUpdate ); -export const buildUpdateQuery = ({ typeName, fragmentName, fragment }) => - gql` - ${updateClientTemplate({ typeName, fragmentName })} - ${fragment} +export const buildUpdateQuery = ({ + model, + fragmentName, + fragment, +}: { + model: VulcanGraphqlModel; + fragmentName?: string; + fragment?: Fragment; +}) => { + const { typeName } = model.graphql; + const { finalFragment, finalFragmentName } = getModelFragment({ + model, + fragment, + fragmentName, + }); + return gql` + ${updateClientTemplate({ typeName, fragmentName: finalFragmentName })} + ${finalFragment} `; +}; // Options of the hook interface UseUpdateOptions @@ -82,7 +101,7 @@ export const useUpdate = ( const { typeName } = model.graphql; - const query = buildUpdateQuery({ typeName, fragmentName, fragment }); + const query = buildUpdateQuery({ model, fragmentName, fragment }); const resolverName = `update${typeName}`; diff --git a/packages/react-ui/components/form/Form/Form.tsx b/packages/react-ui/components/form/Form/Form.tsx index e8db7b56..ece69101 100644 --- a/packages/react-ui/components/form/Form/Form.tsx +++ b/packages/react-ui/components/form/Form/Form.tsx @@ -50,7 +50,12 @@ import { useWarnOnUnsaved } from "../useWarnOnUnsaved"; import { useVulcanComponents } from "../VulcanComponents/Consumer"; import type { FormType } from "../typings"; -import { CreateDocumentResult, FormProps, FormState } from "./typings"; +import { + CreateDocumentResult, + FormProps, + FormState, + UpdateDocumentResult, +} from "./typings"; import { MutationResult } from "@apollo/client"; // props that should trigger a form reset @@ -304,9 +309,10 @@ const getData = ( }; export const Form = (props: FormProps) => { - const { initCallback, createDocument, createDocumentMeta } = props; + const { initCallback, createDocument, updateDocument, deleteDocument } = + props; const initialState = getInitialStateFromProps(props); - const { schema, originalSchema, flatSchema } = initialState; + const { schema, originalSchema, flatSchema, initialDocument } = initialState; const isFirstRender = useRef(true); useEffect(() => { if (isFirstRender.current) { @@ -467,11 +473,12 @@ export const Form = (props: FormProps) => { } }*/ - const [currentDocument, setCurrentDocument] = useState<{ - title?: string; - _id?: string; - name?: string; - }>({}); + const [currentDocument, setCurrentDocument] = + useState<{ + title?: string; + _id?: string; + name?: string; + }>(initialDocument); /* @@ -536,7 +543,6 @@ export const Form = (props: FormProps) => { } }; - const [initialDocument, setInitialDocument] = useState({}); const [disabled, setDisabled] = useState(false); // TODO const [success, setSuccess] = useState(false); // TODO /** @@ -572,7 +578,7 @@ export const Form = (props: FormProps) => { setCurrentValues({}); setDeletedValues([]); setCurrentDocument(document || initialDocument); - setInitialDocument(document || initialDocument); + // setInitialDocument(document || initialDocument); setDisabled(false); }; @@ -583,7 +589,7 @@ export const Form = (props: FormProps) => { }; const editMutationSuccessCallback = function ( - result: CreateDocumentResult + result: UpdateDocumentResult ) { mutationSuccessCallback(result, "edit"); }; @@ -598,7 +604,9 @@ export const Form = (props: FormProps) => { setDisabled(false); setSuccess(true); // for new mutation, run refetch function if it exists - if (mutationType === "new" && props.refetch) props.refetch(); + // TODO: the create mutation should already return the freshest value, do we really need that? + // instead we might want to update currentResult with the result of the creation + if (mutationType === "new") refetchForm(); let { document } = result; // call the clear form method (i.e. trigger setState) only if the form has not been unmounted @@ -697,8 +705,6 @@ export const Form = (props: FormProps) => { contextName, }, }); - // in new versions of Apollo Client errors are no longer thrown/caught - // but can instead be provided as props by the useMutation hook if (result.errors?.length) { // TODO: previously got from meta, we could have more than 1 error mutationErrorCallback(document, result.errors[0]); @@ -712,19 +718,16 @@ export const Form = (props: FormProps) => { // update document form try { const documentId = currentDocument._id; - const result = await props.updateDocument({ + const result = await updateDocument({ input: { id: documentId, data, contextName, }, }); - // TODO: ?? what is Meta? - const meta = props.updateDocumentMeta; - // in new versions of Apollo Client errors are no longer thrown/caught - // but can instead be provided as props by the useMutation hook - if (meta?.error) { - mutationErrorCallback(document, meta.error); + // TODO: handle more than 1 error + if (result.errors?.length) { + mutationErrorCallback(document, result.errors[0]); } else { editMutationSuccessCallback(result); } @@ -739,7 +742,7 @@ export const Form = (props: FormProps) => { Delete document handler */ - const deleteDocument = () => { + const deleteDocumentWithConfirm = () => { const document = currentDocument; const documentId = props.document._id; const documentTitle = document.title || document.name || ""; @@ -750,13 +753,12 @@ export const Form = (props: FormProps) => { ); if (window.confirm(deleteDocumentConfirm)) { - props - .deleteDocument({ input: { id: documentId } }) + deleteDocument({ input: { id: documentId } }) .then((mutationResult) => { // the mutation result looks like {data:{collectionRemove: null}} if succeeded if (props.removeSuccessCallback) props.removeSuccessCallback({ documentId, documentTitle }); - if (props.refetch) props.refetch(); + refetchForm(); }) .catch((error) => { // eslint-disable-next-line no-console @@ -792,7 +794,7 @@ export const Form = (props: FormProps) => { formType, }, { - deleteDocument, + deleteDocument: deleteDocumentWithConfirm, } ); const isChanged = isNotSameDocument(initialDocument, currentDocument); diff --git a/packages/react-ui/components/form/Form/typings.ts b/packages/react-ui/components/form/Form/typings.ts index bf4737ae..d657668e 100644 --- a/packages/react-ui/components/form/Form/typings.ts +++ b/packages/react-ui/components/form/Form/typings.ts @@ -1,12 +1,17 @@ import { MutationResult } from "@apollo/client"; +import type { + CreateVariables, + UpdateVariables, + DeleteVariables, +} from "@vulcanjs/graphql"; import { VulcanModel } from "@vulcanjs/model"; import { User } from "@vulcanjs/permissions"; -import { VulcanSchema } from "@vulcanjs/schema/dist/typings"; +import { VulcanSchema } from "@vulcanjs/schema"; export interface FormState { schema: any; - initialDocument: any; - currentDocument: any; + initialDocument: Object; + currentDocument: Object; deletedValues: any; errors: any; currentValues: any; @@ -16,22 +21,42 @@ export interface FormState { originalSchema: any; } export interface FormProps { - refetch?: Function; + /** + * Function that retriggers data fetching in edit mode + * Usually provided by the useSingle but could be any function + */ + refetch?: () => void; + /** + * Document id in update mode + */ id?: string; - // TODO: merge - components?: {}; /* The model in which to edit or insert a document. */ model: VulcanModel; /** Passing directly a raw schema (TODO: model is still mandatory atm) */ schema?: VulcanSchema; + /** + * Passing directly a document (TODO: not yet tested in the new version) + */ document?: any; + /** + * Disable the form + */ disabled?: boolean; + /** + * currentUser to check authorizations to update/create some fields + */ currentUser?: User; addFields?: Array; removeFields?: Array; // deprecated //hideFields?: any; + /** + * Will prevent leaving the page/unmounting the form on unsaved changes + */ warnUnsavedChanges?: boolean; + /** + * Label so that graphql queries are contextualized + */ contextName?: string; itemProperties?: Object; showDelete?: boolean; @@ -40,6 +65,7 @@ export interface FormProps { revertLabel?: string; // revertCallback?: Function; + // TODO: probably should be removed successComponent?: any; /* Instead of passing collection you can pass the name of the collection.*/ // collectionName?: string; @@ -91,27 +117,27 @@ An example would be a createdAt date added automatically on creation even though * The result is usually extracted from a graphql mutation * But we have a simplified abstracted API, so we could also use the Form without graphql */ - createDocument: (createArgs: {}) => Promise< - CreateDocumentResult - >; - updateDocument: Function; - deleteDocument: Function; - // ?? - createDocumentMeta?: { error?: any }; - updateDocumentMeta?: { error?: any }; + createDocument: ( + createVars: CreateVariables + ) => Promise>; + updateDocument: ( + vars: UpdateVariables + ) => Promise>; + deleteDocument: (vars: DeleteVariables) => Promise; + // Other results from the Apollo query => should be ignored, in order to avoid dependency to graphql in the Form + // instead the container is responsible for passing errors and stuff + // createDocumentMeta?: { error?: any }; + // updateDocumentMeta?: { error?: any }; // EXPERIMENTAL: allowing to manually set the form children children?: React.ReactNode; } -// Should be input that can be passed to a createDocument mutation -export interface CreateDocumentInput { - input: { - data: any; - contextName?: string; - }; +export interface CreateDocumentResult { + document: TDocument; + errors: Array; } -export interface CreateDocumentResult { +export interface UpdateDocumentResult { document: TDocument; errors: Array; } diff --git a/packages/react-ui/components/form/FormContainer.tsx b/packages/react-ui/components/form/FormContainer.tsx index 473364f8..8b8de266 100644 --- a/packages/react-ui/components/form/FormContainer.tsx +++ b/packages/react-ui/components/form/FormContainer.tsx @@ -46,7 +46,12 @@ import { gql } from "@apollo/client"; import getFormFragments from "./modules/formFragments"; // import { VulcanModel } from "@vulcanjs/model"; -import { VulcanGraphqlModel } from "@vulcanjs/graphql"; +import { + VulcanGraphqlModel, + CreateVariables, + UpdateVariables, + DeleteVariables, +} from "@vulcanjs/graphql"; import { capitalize } from "@vulcanjs/utils"; import { useSingle, @@ -56,9 +61,10 @@ import { UseSingleOptions, } from "@vulcanjs/react-hooks"; import { useVulcanComponents } from "./VulcanComponents/Consumer"; -import { FetchResult, MutationResult } from "@apollo/client"; -import { FormType } from "./typings"; -import { CreateDocumentInput } from "./Form/typings"; +import { FetchResult } from "@apollo/client"; +// import { FormType } from "./typings"; +import { debugVulcan } from "@vulcanjs/utils"; +const debugForm = debugVulcan("form"); // Mutation that yield a success result type SuccessfulFetchResult = FetchResult & { @@ -216,8 +222,18 @@ export const FormContainer = (props: FormContainerProps) => { (queryFragment as any).loc.source.body, queryFragmentName );*/ - const { data, loading, refetch } = useSingle(queryOptions); - const document = data; // TODO: get the item from data + const { data, document, loading, refetch } = useSingle(queryOptions); + if (formType !== "new") { + debugForm( + "useSingle result", + "data", + data, + "document", + document, + "loading", + loading + ); + } // TODO: pass the creation functions down to the Form const [createDocument] = useCreate(mutationOptions); const [updateDocument] = useUpdate(mutationOptions); @@ -255,14 +271,26 @@ export const FormContainer = (props: FormContainerProps) => { if (mutationType === "new" && refetch) refetch(); */ - const createAndReturnDocument = async (input: CreateDocumentInput) => { - const result = await createDocument(input); + const createAndReturnDocument = async (variables: CreateVariables) => { + const result = await createDocument(variables); const { errors, document } = result; return { document, errors, }; }; + const updateAndReturnDocument = async (variables: UpdateVariables) => { + const result = await updateDocument(variables); + const { errors, document } = result; + return { + document, + errors, + }; + }; + + const deleteDocumentAndRefetch = async (variables: DeleteVariables) => { + await deleteDocument(variables); + }; if (isEdit && loading) { return ; @@ -272,8 +300,9 @@ export const FormContainer = (props: FormContainerProps) => { document={document} loading={loading} createDocument={createAndReturnDocument /*createDocument*/} - updateDocument={updateDocument} - deleteDocument={deleteDocument} + updateDocument={updateAndReturnDocument} + deleteDocument={deleteDocumentAndRefetch} + refetch={refetch} {...childProps} {...props} /> diff --git a/packages/react-ui/components/form/tests/Form.stories.tsx b/packages/react-ui/components/form/tests/Form.stories.tsx index de2bb186..e5fefb1b 100644 --- a/packages/react-ui/components/form/tests/Form.stories.tsx +++ b/packages/react-ui/components/form/tests/Form.stories.tsx @@ -192,6 +192,13 @@ ArrayOfStringsForm.args = { model: models.ArrayOfStrings }; export const AddressesForm = FormTemplate.bind({}); AddressesForm.args = { model: models.Addresses }; +// EDIT MODE +export const WithDocument = FormTemplate.bind({}); +WithDocument.args = { + model: models.OneField, + document: { text: "hello there" }, +}; + // @see https://github.com/storybookjs/storybook/pull/14550 export const WarnOnUnsavedChanges = () => (
diff --git a/packages/react-ui/components/form/tests/FormContainer.stories.tsx b/packages/react-ui/components/form/tests/FormContainer.stories.tsx index c698ec5a..0ed7c440 100644 --- a/packages/react-ui/components/form/tests/FormContainer.stories.tsx +++ b/packages/react-ui/components/form/tests/FormContainer.stories.tsx @@ -1,7 +1,6 @@ import React from "react"; import { Story, Meta } from "@storybook/react"; import { SmartForm, SmartFormProps } from "../FormContainer"; -import { createGraphqlModel, createOperationName } from "@vulcanjs/graphql"; // Mocking graphql import { MockedProvider } from "@apollo/client/testing"; @@ -12,45 +11,41 @@ import { } from "operation-name-mock-link"; import { VulcanComponentsProvider } from "../VulcanComponents/Provider"; import { ExpectedErrorBoundary } from "../../../testing/ExpectedErrorBoundary"; -import { singleOperationName } from "@vulcanjs/graphql"; -import { buildSingleQuery, buildCreateQuery } from "@vulcanjs/react-hooks"; +import {} from "@vulcanjs/graphql"; +import { + singleOperationName, + createGraphqlModel, + createOperationName, + updateOperationName, +} from "@vulcanjs/graphql"; +import { + buildSingleQuery, + buildCreateQuery, + buildUpdateQuery, +} from "@vulcanjs/react-hooks"; +import { OneFieldGraphql, OneFieldType } from "./fixtures/graphqlModels"; // dummy simplified model -interface OneFieldType { - text: string; -} -const OneField = createGraphqlModel({ - name: "OneField", - schema: { - text: { - type: String, - canRead: ["anyone"], - canUpdate: ["anyone"], - canCreate: ["anyone"], - }, - }, - graphql: { - typeName: "OneField", - multiTypeName: "OneFields", - }, -}); - const singleMock: OperationNameMockedResponse<{ - empty: { result: OneFieldType }; + oneField: { result: OneFieldType }; }> = { request: { - operationName: singleOperationName(OneField), + operationName: singleOperationName(OneFieldGraphql), query: buildSingleQuery({ - model: OneField, + model: OneFieldGraphql, }), }, - result: {}, + result: { + data: { + oneField: { result: { text: "hello", __typename: "OneField" } }, + }, + }, }; const createMock: OperationNameMockedResponse = { request: { - operationName: createOperationName(OneField), - query: buildCreateQuery({ model: OneField }), + operationName: createOperationName(OneFieldGraphql), + query: buildCreateQuery({ model: OneFieldGraphql }), }, result: { data: { @@ -61,6 +56,20 @@ const createMock: OperationNameMockedResponse = { }, }, }; +const updateMock: OperationNameMockedResponse = { + request: { + operationName: updateOperationName(OneFieldGraphql), + query: buildUpdateQuery({ model: OneFieldGraphql }), + }, + result: { + data: { + updateOneField: { + // always return the same object whatever the user created + data: { text: "successfully updated document" }, + }, + }, + }, +}; export default { component: SmartForm, @@ -78,7 +87,7 @@ export default { ), ], args: { - model: OneField, + model: OneFieldGraphql, }, parameters: { actions: {} }, } as Meta; @@ -98,13 +107,15 @@ CreateSmartForm.decorators = [ ), ]; -export const EditSmartForm = SmartFormTemplate.bind({}); -EditSmartForm.args = { +export const UpdateSmartForm = SmartFormTemplate.bind({}); +UpdateSmartForm.args = { documentId: "1", }; -EditSmartForm.decorators = [ +UpdateSmartForm.decorators = [ (Story) => ( - + ), diff --git a/packages/react-ui/components/form/tests/fixtures/graphqlModels.ts b/packages/react-ui/components/form/tests/fixtures/graphqlModels.ts new file mode 100644 index 00000000..026a811e --- /dev/null +++ b/packages/react-ui/components/form/tests/fixtures/graphqlModels.ts @@ -0,0 +1,22 @@ +import { createGraphqlModel } from "@vulcanjs/graphql"; + +// dummy simplified model +export interface OneFieldType { + text: string; + __typename?: "OneField"; // don't forget the typeName in mocks +} +export const OneFieldGraphql = createGraphqlModel({ + name: "OneField", + schema: { + text: { + type: String, + canRead: ["anyone"], + canUpdate: ["anyone"], + canCreate: ["anyone"], + }, + }, + graphql: { + typeName: "OneField", + multiTypeName: "OneFields", + }, +}); diff --git a/packages/react-ui/components/form/tests/fixtures/models.ts b/packages/react-ui/components/form/tests/fixtures/models.ts index 49295256..4185c776 100644 --- a/packages/react-ui/components/form/tests/fixtures/models.ts +++ b/packages/react-ui/components/form/tests/fixtures/models.ts @@ -145,3 +145,15 @@ export const Addresses = createModel({ //typeName: "Address", schema: addressSchema, }); + +export const OneField = createModel({ + name: "OneField", + schema: { + text: { + type: String, + canRead: ["anyone"], + canUpdate: ["anyone"], + canCreate: ["anyone"], + }, + }, +}); diff --git a/packages/utils/debug.ts b/packages/utils/debug.ts index f61fc183..02c028e4 100644 --- a/packages/utils/debug.ts +++ b/packages/utils/debug.ts @@ -4,3 +4,10 @@ export const deprecate = (currentVulcanVersion, message) => { console.warn(`DEPRECATED (${currentVulcanVersion}):`, message); } }; + +import debug from "debug"; +/** + * @example const debugForm = debugVulcan("form"); + * debugForm("Got some values in form") + */ +export const debugVulcan = (suffix: string) => debug(`vulcan:${suffix}`); diff --git a/packages/utils/package.json b/packages/utils/package.json index 2ecff8d4..35a59e6c 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -26,6 +26,7 @@ "gitHead": "f4594745e0a5c455a2f0c3e78fa3ff7a35ef4c95", "dependencies": { "@vulcanjs/model": "^0.1.13", + "debug": "^4.3.1", "lodash": "^4.17.21" } }