Skip to content

Commit

Permalink
chore(sanity): load monorepo aliases from '@repo/dev-aliases'
Browse files Browse the repository at this point in the history
  • Loading branch information
bjoerge committed Sep 25, 2024
1 parent 2180d0f commit 16dfd3d
Show file tree
Hide file tree
Showing 9 changed files with 118 additions and 123 deletions.
1 change: 1 addition & 0 deletions packages/sanity/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,7 @@
"@jest/globals": "^29.7.0",
"@playwright/experimental-ct-react": "1.44.1",
"@playwright/test": "1.44.1",
"@repo/dev-aliases": "workspace:*",
"@repo/package.config": "workspace:*",
"@sanity/codegen": "3.58.0",
"@sanity/generate-help-url": "^3.0.0",
Expand Down
41 changes: 19 additions & 22 deletions packages/sanity/src/_internal/cli/server/__tests__/aliases.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ import {escapeRegExp} from 'lodash'
import resolve from 'resolve.exports'
import {type Alias} from 'vite'

import {browserCompatibleSanityPackageSpecifiers, getAliases} from '../aliases'
import {
browserCompatibleSanityPackageSpecifiers,
getSanityPkgExportAliases,
} from '../getBrowserAliases'
import {getMonorepoAliases} from '../sanityMonorepo'

const sanityPkgPath = path.resolve(__dirname, '../../../../../package.json')
// eslint-disable-next-line import/no-dynamic-require
Expand Down Expand Up @@ -57,7 +61,7 @@ describe('getAliases', () => {
// > esbuild in this environment because esbuild relies on this invariant. This
// > is not a problem with esbuild. You need to fix your environment instead.
it('returns the correct aliases for normal builds', () => {
const aliases = getAliases({sanityPkgPath})
const aliases = getSanityPkgExportAliases(sanityPkgPath)

// Prepare expected aliases
const dirname = path.dirname(sanityPkgPath)
Expand All @@ -80,31 +84,24 @@ describe('getAliases', () => {
expect(aliases).toEqual(expectedAliases)
})

it('returns the correct aliases for the monorepo', () => {
const monorepoPath = path.resolve(__dirname, '../../../../../monorepo')
it('returns the correct aliases for the monorepo', async () => {
const monorepoPath = '/path/to/monorepo'
const devAliases = {
'sanity/_singletons': 'packages/sanity/src/_singletons.ts',
'sanity/desk': 'packages/sanity/src/desk.ts',
'sanity/presentation': 'packages/sanity/src/presentation.ts',
'sanity/_singletons': 'sanity/src/_singletons.ts',
'sanity/desk': 'sanity/src/desk.ts',
'sanity/presentation': 'sanity/src/presentation.ts',
}
jest.doMock(path.resolve(monorepoPath, 'dev/aliases.cjs'), () => devAliases, {virtual: true})

const aliases = getAliases({
monorepo: {path: monorepoPath},
})

const expectedAliases = Object.fromEntries(
Object.entries(devAliases).map(([key, modulePath]) => {
return [key, path.resolve(monorepoPath, modulePath)]
}),
)
const expectedAliases = {
'sanity/_singletons': '/path/to/monorepo/packages/sanity/src/_singletons.ts',
'sanity/desk': '/path/to/monorepo/packages/sanity/src/desk.ts',
'sanity/presentation': '/path/to/monorepo/packages/sanity/src/presentation.ts',
}

expect(aliases).toMatchObject(expectedAliases)
})
jest.doMock('@repo/dev-aliases', () => devAliases, {virtual: true})

it('returns an empty object if no conditions are met', () => {
const aliases = getAliases({})
const aliases = await getMonorepoAliases(monorepoPath)

expect(aliases).toEqual({})
expect(aliases).toMatchObject(expectedAliases)
})
})
105 changes: 12 additions & 93 deletions packages/sanity/src/_internal/cli/server/aliases.ts
Original file line number Diff line number Diff line change
@@ -1,51 +1,18 @@
import path from 'node:path'
import {type AliasOptions} from 'vite'

import {escapeRegExp} from 'lodash'
import resolve from 'resolve.exports'
import {type Alias, type AliasOptions} from 'vite'

import {type SanityMonorepo} from './sanityMonorepo'
import {getSanityPkgExportAliases} from './getBrowserAliases'
import {getMonorepoAliases} from './sanityMonorepo'

/**
* @internal
*/
export interface GetAliasesOptions {
/** An optional monorepo configuration object. */
monorepo?: SanityMonorepo
/** An optional monorepo path. */
monorepoPath?: string
/** The path to the sanity package.json file. */
sanityPkgPath?: string
sanityPkgPath: string
}

/**
* The following are the specifiers that are expected/allowed to be used within
* a built Sanity studio in the browser. These are used in combination with
* `resolve.exports` to determine the final entry point locations for each allowed specifier.
*
* There is also a corresponding test for this file that expects these to be
* included in the `sanity` package.json. That test is meant to keep this list
* in sync in the event we add another package subpath.
*
* @internal
*/
export const browserCompatibleSanityPackageSpecifiers = [
'sanity',
'sanity/_createContext',
'sanity/_singletons',
'sanity/desk',
'sanity/presentation',
'sanity/router',
'sanity/structure',
'sanity/package.json',
]

/**
* These conditions should align with the conditions present in the
* `package.json` of the `'sanity'` module. they are given to `resolve.exports`
* in order to determine the correct entrypoint for the browser-compatible
* package specifiers listed above.
*/
const conditions = ['import', 'browser', 'default']

/**
* Returns an object of aliases for Vite to use.
*
Expand All @@ -55,61 +22,13 @@ const conditions = ['import', 'browser', 'default']
*
* If the project is within the monorepo, it uses the source files directly for a better
* development experience. Otherwise, it uses the `sanityPkgPath` and `conditions` to locate
* the entry points for each subpath the Sanity module exports.
* the entry points for each subpath of the Sanity module exports.
*
* @internal
*/
export function getAliases({monorepo, sanityPkgPath}: GetAliasesOptions): AliasOptions {
// If the current Studio is located within the Sanity monorepo
if (monorepo?.path) {
// Load monorepo aliases. This ensures that the Vite server uses the source files
// instead of the compiled output, allowing for a better development experience.
const aliasesPath = path.resolve(monorepo.path, 'dev/aliases.cjs')

// Import the development aliases configuration
// eslint-disable-next-line import/no-dynamic-require
const devAliases: Record<string, string> = require(aliasesPath)

// Resolve each alias path relative to the monorepo path
const monorepoAliases = Object.fromEntries(
Object.entries(devAliases).map(([key, modulePath]) => {
return [key, path.resolve(monorepo.path, modulePath)]
}),
)

// Return the aliases configuration for monorepo
return monorepoAliases
}

// If not in the monorepo, use the `sanityPkgPath`
// to locate the entry points for each subpath the Sanity module exports
if (sanityPkgPath) {
// Load the package.json of the Sanity package
// eslint-disable-next-line import/no-dynamic-require
const pkg = require(sanityPkgPath)
const dirname = path.dirname(sanityPkgPath)

// Resolve the entry points for each allowed specifier
const unifiedSanityAliases = browserCompatibleSanityPackageSpecifiers.reduce<Alias[]>(
(acc, next) => {
// Resolve the export path for the specifier using resolve.exports
const dest = resolve.exports(pkg, next, {browser: true, conditions})?.[0]
if (!dest) return acc

// Map the specifier to its resolved path
acc.push({
find: new RegExp(`^${escapeRegExp(next)}$`),
replacement: path.resolve(dirname, dest),
})
return acc
},
[],
)

// Return the aliases configuration for external projects
return unifiedSanityAliases
}

// Return an empty aliases configuration if no conditions are met
return {}
export async function getAliases({
monorepoPath,
sanityPkgPath,
}: GetAliasesOptions): Promise<AliasOptions> {
return monorepoPath ? getMonorepoAliases(monorepoPath) : getSanityPkgExportAliases(sanityPkgPath)
}
63 changes: 63 additions & 0 deletions packages/sanity/src/_internal/cli/server/getBrowserAliases.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import path from 'node:path'

import {escapeRegExp} from 'lodash'
import resolve from 'resolve.exports'
import {type Alias} from 'vite'

/**
* The following are the specifiers that are expected/allowed to be used within
* a built Sanity studio in the browser. These are used in combination with
* `resolve.exports` to determine the final entry point locations for each allowed specifier.
*
* There is also a corresponding test for this file that expects these to be
* included in the `sanity` package.json. That test is meant to keep this list
* in sync in the event we add another package subpath.
*
* @internal
*/
export const browserCompatibleSanityPackageSpecifiers = [
'sanity',
'sanity/_createContext',
'sanity/_singletons',
'sanity/desk',
'sanity/presentation',
'sanity/router',
'sanity/structure',
'sanity/package.json',
]

/**
* These conditions should align with the conditions present in the
* `package.json` of the `'sanity'` module. they are given to `resolve.exports`
* in order to determine the correct entrypoint for the browser-compatible
* package specifiers listed above.
*/
const conditions = ['import', 'browser', 'default']

// locate the entry points for each subpath the Sanity module exports
export function getSanityPkgExportAliases(sanityPkgPath: string) {
// Load the package.json of the Sanity package
// eslint-disable-next-line import/no-dynamic-require
const pkg = require(sanityPkgPath)
const dirname = path.dirname(sanityPkgPath)

// Resolve the entry points for each allowed specifier
const unifiedSanityAliases = browserCompatibleSanityPackageSpecifiers.reduce<Alias[]>(
(acc, next) => {
// Resolve the export path for the specifier using resolve.exports
const dest = resolve.exports(pkg, next, {browser: true, conditions})?.[0]
if (!dest) return acc

// Map the specifier to its resolved path
acc.push({
find: new RegExp(`^${escapeRegExp(next)}$`),
replacement: path.resolve(dirname, dest),
})
return acc
},
[],
)

// Return the aliases configuration for external projects
return unifiedSanityAliases
}
8 changes: 5 additions & 3 deletions packages/sanity/src/_internal/cli/server/getViteConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ import debug from 'debug'
import readPkgUp from 'read-pkg-up'
import {type ConfigEnv, type InlineConfig, mergeConfig} from 'vite'

import {getAliases} from './aliases'
import {createExternalFromImportMap} from './createExternalFromImportMap'
import {getSanityPkgExportAliases} from './getBrowserAliases'
import {getStudioEnvironmentVariables} from './getStudioEnvironmentVariables'
import {normalizeBasePath} from './helpers'
import {loadSanityMonorepo} from './sanityMonorepo'
import {getMonorepoAliases, loadSanityMonorepo} from './sanityMonorepo'
import {sanityBuildEntries} from './vite/plugin-sanity-build-entries'
import {sanityDotWorkaroundPlugin} from './vite/plugin-sanity-dot-workaround'
import {sanityFaviconsPlugin} from './vite/plugin-sanity-favicons'
Expand Down Expand Up @@ -113,7 +113,9 @@ export async function getViteConfig(options: ViteOptions): Promise<InlineConfig>
envPrefix: 'SANITY_STUDIO_',
logLevel: mode === 'production' ? 'silent' : 'info',
resolve: {
alias: getAliases({monorepo, sanityPkgPath}),
alias: monorepo?.path
? await getMonorepoAliases(monorepo.path)
: getSanityPkgExportAliases(sanityPkgPath),
dedupe: ['styled-components'],
},
define: {
Expand Down
7 changes: 4 additions & 3 deletions packages/sanity/src/_internal/cli/server/renderDocument.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,9 @@ import {parse as parseHtml} from 'node-html-parser'
import {createElement} from 'react'
import {renderToStaticMarkup} from 'react-dom/server'

import {getAliases} from './aliases'
import {TIMESTAMPED_IMPORTMAP_INJECTOR_SCRIPT} from './constants'
import {debug as serverDebug} from './debug'
import {type SanityMonorepo} from './sanityMonorepo'
import {getMonorepoAliases, type SanityMonorepo} from './sanityMonorepo'

const debug = serverDebug.extend('renderDocument')

Expand Down Expand Up @@ -173,7 +172,9 @@ function renderDocumentFromWorkerData() {
// Require hook #1
// Alias monorepo modules
debug('Registering potential aliases')
require('module-alias').addAliases(getAliases({monorepo}))
if (monorepo) {
require('module-alias').addAliases(getMonorepoAliases(monorepo.path))
}

// Require hook #2
// Use `esbuild` to allow JSX/TypeScript and modern JS features
Expand Down
9 changes: 9 additions & 0 deletions packages/sanity/src/_internal/cli/server/sanityMonorepo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,15 @@ export interface SanityMonorepo {
path: string
}

export async function getMonorepoAliases(monorepoPath: string) {
const {default: aliases} = await import('@repo/dev-aliases')
return Object.fromEntries(
Object.entries(aliases).map(([pkgName, pkgPath]) => {
return [pkgName, path.resolve(monorepoPath, path.join('packages', pkgPath))]
}),
)
}

/**
* Load information about the `sanity-io/sanity` monorepo (if applicable)
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {afterAll, beforeAll, describe, expect, it, jest} from '@jest/globals'
import {type SanityDocument, type SanityProject} from '@sanity/client'
import {evaluate, parse} from 'groq-js'

import {getAliases} from '../../server/aliases'
import {getMonorepoAliases} from '../../server/sanityMonorepo'
import {createReceiver, type WorkerChannelReceiver} from '../../util/workerChannels'
import {type ValidateDocumentsWorkerData, type ValidationWorkerChannel} from '../validateDocuments'

Expand Down Expand Up @@ -204,7 +204,7 @@ describe('validateDocuments', () => {
const moduleAlias = require('module-alias')
const { register } = require('esbuild-register/dist/node')
moduleAlias.addAliases(${JSON.stringify(getAliases({monorepo: {path: path.resolve(__dirname, '../../../../../../..')}}))})
moduleAlias.addAliases(${JSON.stringify(await getMonorepoAliases(path.resolve(__dirname, '../../../../../../..')))})
const { unregister } = register({
target: 'node18',
Expand Down
3 changes: 3 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 16dfd3d

Please sign in to comment.