sanity-codegen icon indicating copy to clipboard operation
sanity-codegen copied to clipboard

Error: Cannot find module 'part:@sanity/base/client' when using Orderable Document List

Open lotarbo opened this issue 2 years ago • 4 comments

same error here https://github.com/sanity-io/orderable-document-list/issues/14

lotarbo avatar Apr 21 '22 11:04 lotarbo

Same type of error but with Error: Cannot find module 'part:@sanity/base/user.

Using a custom React component in sanity that imports import { FormField } from "@sanity/base/components"; and some other existing sanity internal library components.

sanity build compiles and prod site runs perfectly, just sanity-codegen throwing this error.

fevernova90 avatar Jun 29 '22 07:06 fevernova90

This is a complex issue that has to do with babel resolution of sanity parts as its core.

For some custom components, this may be sufficient: https://github.com/ricokahler/sanity-codegen/issues/59#issuecomment-762472234

But for Orderable Document List it isn't, as the type is defined via a function, eg orderRankField({ type: 'project' }),

Not sure what the solution actually is, but it may not really be possible with how sanity-codegen presently works.

mfanuzzi avatar Sep 13 '22 01:09 mfanuzzi

Here is a solution for this plugin specifically, though it may be helpful for other plugins as well.

There are two steps: first, extend the codegen babel config to "no-op" the target plugin. Second, mock the essential bits that you're expecting the function/plugin to return.

sanity-codegen.config.ts (taken from here):

import { defaultBabelOptions } from 'sanity-codegen/cli';
import { SanityCodegenConfig } from 'sanity-codegen';

const config: SanityCodegenConfig = {
  schemaPath: './schemas/index',
  outputPath: '../web/src/config/sanity-schema.ts',

  babelOptions: {
    ...defaultBabelOptions,
    plugins: [
      ...defaultBabelOptions.plugins.filter(
        ([key]) => key !== 'module-resolver',
      ),
      [
        'module-resolver',
        {
          root: ['.'],
          alias: {
            // The below are all the defaults
            'part:@sanity/base/schema-creator':
              'sanity-codegen/schema-creator-shim',
            'all:part:@sanity/base/schema-type':
              'sanity-codegen/schema-type-shim',
            'part:@sanity/base/schema-type': 'sanity-codegen/schema-type-shim',
            '^part:.*': 'sanity-codegen/no-op',
            '^config:.*': 'sanity-codegen/no-op',
            '^all:part:.*': 'sanity-codegen/no-op',

            // Here we specify which plugins need to be mocked
            // (see mocks.ts for more info)
            // 👇👇👇
            '@sanity/orderable-document-list/lib/helpers/constants':
              'sanity-codegen/no-op',
            '@sanity/orderable-document-list': 'sanity-codegen/no-op',
            // 👆👆👆
          },
        },
      ],
    ],
  },
};

export default config;

I created a mocks.ts to store my mock of orderRankField() (and others in the future) like so:

import { orderRankField as _orderRankField } from '@sanity/orderable-document-list';
const constants = require('@sanity/orderable-document-list/lib/helpers/constants');

/** a mock of orderRankField for typegen purposes */
export const orderRankField = (props: {
  type: string;
  title?: string;
  icon?: any;
  id?: string;
  filter?: string;
  params?: {
    lang: 'en_US';
  };
}) => {
  const name =
    typeof constants.ORDER_FIELD_NAME === 'string'
      ? constants.ORDER_FIELD_NAME
      : 'orderRank';

  const orf = {
    title: 'Order Rank',
    readOnly: true,
    hidden: true,
    name,
    type: 'string',
    ..._orderRankField(props),
  };

  return orf;
};

...so that I could then reference it in my document files, for example a "project" schema in project.ts:

import { orderRankField } from '../util'; // my mocked one, NOT the version from the plugin

export const projectDoc = {
  name: 'project',
  type: 'document',
  title: 'Project',
  fields: [
    name,
    orderRankField({ type: 'project', title: 'Projects' }),
  ],
} as const;

mfanuzzi avatar Sep 13 '22 18:09 mfanuzzi

A solution that worked for me is to use babel-plugin-search-and-replace to replace the custom input imports with a dummy component. Inspired by this solution: https://github.com/ricokahler/sanity-codegen/issues/59#issuecomment-1086887871

import { SanityCodegenConfig } from "sanity-codegen"
import { defaultBabelOptions } from "sanity-codegen/cli"

const sanityCodegenConfig: SanityCodegenConfig = {
  schemaPath: "./schemas/schema",
  outputPath: "./schema.ts",

  babelOptions: {
    ...defaultBabelOptions,
    plugins: [
      ...defaultBabelOptions.plugins,
      [
        "search-and-replace",
        {
          rules: [
            {
              search: /src\/components\/.*/, // part of the path to your components
              replace: "sanity-codegen-components.tsx", // a file at the same level of your search
            },
          ],
        },
      ],
    ],
  },
}

export default sanityCodegenConfig

We could possibly refactor this to make use of sanity-codegen/no-op instead of creating a dummy component.

alexrohleder avatar Sep 14 '22 13:09 alexrohleder