rsnext/packages/next/build/swc/options.js
Hannes Bornö b5590022b6
Font loaders next config shape (#41219)
Changes how font loaders are configured in next config, makes more sense since options can be optional. Also adds error for when font loaders are used from within node_modules.

## Bug

- [ ] Related issues linked using `fixes #number`
- [ ] Integration tests added
- [ ] Errors have a helpful link attached, see `contributing.md`

## Feature

- [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR.
- [ ] Related issues linked using `fixes #number`
- [ ] Integration tests added
- [ ] Documentation added
- [ ] Telemetry added. In case of a feature if it's used or not.
- [ ] Errors have a helpful link attached, see `contributing.md`

## Documentation / Examples

- [ ] Make sure the linting passes by running `pnpm lint`
- [ ] The "examples guidelines" are followed from [our contributing doc](https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md)
2022-10-07 09:15:57 +00:00

304 lines
7.9 KiB
JavaScript

const nextDistPath =
/(next[\\/]dist[\\/]shared[\\/]lib)|(next[\\/]dist[\\/]client)|(next[\\/]dist[\\/]pages)/
const regeneratorRuntimePath = require.resolve(
'next/dist/compiled/regenerator-runtime'
)
export function getParserOptions({ filename, jsConfig, ...rest }) {
const isTSFile = filename.endsWith('.ts')
const isTypeScript = isTSFile || filename.endsWith('.tsx')
const enableDecorators = Boolean(
jsConfig?.compilerOptions?.experimentalDecorators
)
return {
...rest,
syntax: isTypeScript ? 'typescript' : 'ecmascript',
dynamicImport: true,
decorators: enableDecorators,
// Exclude regular TypeScript files from React transformation to prevent e.g. generic parameters and angle-bracket type assertion from being interpreted as JSX tags.
[isTypeScript ? 'tsx' : 'jsx']: !isTSFile,
importAssertions: true,
}
}
function getBaseSWCOptions({
filename,
jest,
development,
hasReactRefresh,
globalWindow,
nextConfig,
resolvedBaseUrl,
jsConfig,
swcCacheDir,
isServerLayer,
relativeFilePathFromRoot,
hasServerComponents,
}) {
const parserConfig = getParserOptions({ filename, jsConfig })
const paths = jsConfig?.compilerOptions?.paths
const enableDecorators = Boolean(
jsConfig?.compilerOptions?.experimentalDecorators
)
const emitDecoratorMetadata = Boolean(
jsConfig?.compilerOptions?.emitDecoratorMetadata
)
const useDefineForClassFields = Boolean(
jsConfig?.compilerOptions?.useDefineForClassFields
)
const plugins = (nextConfig?.experimental?.swcPlugins ?? [])
.filter(Array.isArray)
.map(([name, options]) => [require.resolve(name), options])
return {
jsc: {
...(resolvedBaseUrl && paths
? {
baseUrl: resolvedBaseUrl,
paths,
}
: {}),
externalHelpers: !process.versions.pnp && !jest,
parser: parserConfig,
experimental: {
keepImportAssertions: true,
plugins,
cacheRoot: swcCacheDir,
},
transform: {
// Enables https://github.com/swc-project/swc/blob/0359deb4841be743d73db4536d4a22ac797d7f65/crates/swc_ecma_ext_transforms/src/jest.rs
...(jest
? {
hidden: {
jest: true,
},
}
: {}),
legacyDecorator: enableDecorators,
decoratorMetadata: emitDecoratorMetadata,
useDefineForClassFields: useDefineForClassFields,
react: {
importSource:
jsConfig?.compilerOptions?.jsxImportSource ??
(nextConfig?.compiler?.emotion ? '@emotion/react' : 'react'),
runtime: 'automatic',
pragma: 'React.createElement',
pragmaFrag: 'React.Fragment',
throwIfNamespace: true,
development: !!development,
useBuiltins: true,
refresh: !!hasReactRefresh,
},
optimizer: {
simplify: false,
globals: jest
? null
: {
typeofs: {
window: globalWindow ? 'object' : 'undefined',
},
envs: {
NODE_ENV: development ? '"development"' : '"production"',
},
// TODO: handle process.browser to match babel replacing as well
},
},
regenerator: {
importPath: regeneratorRuntimePath,
},
},
},
sourceMaps: jest ? 'inline' : undefined,
removeConsole: nextConfig?.compiler?.removeConsole,
// disable "reactRemoveProperties" when "jest" is true
// otherwise the setting from next.config.js will be used
reactRemoveProperties: jest
? false
: nextConfig?.compiler?.reactRemoveProperties,
modularizeImports: nextConfig?.experimental?.modularizeImports,
relay: nextConfig?.compiler?.relay,
// Disable css-in-js transform on server layer for server components
...(isServerLayer
? {}
: {
emotion: getEmotionOptions(nextConfig, development),
styledComponents: getStyledComponentsOptions(nextConfig, development),
styledJsx: true,
}),
serverComponents: hasServerComponents
? {
isServer: !!isServerLayer,
}
: false,
fontLoaders:
nextConfig?.experimental?.fontLoaders && relativeFilePathFromRoot
? {
fontLoaders: nextConfig.experimental.fontLoaders.map(
({ loader }) => loader
),
relativeFilePathFromRoot,
}
: null,
}
}
function getStyledComponentsOptions(nextConfig, development) {
let styledComponentsOptions = nextConfig?.compiler?.styledComponents
if (!styledComponentsOptions) {
return null
}
return {
...styledComponentsOptions,
displayName: styledComponentsOptions.displayName ?? Boolean(development),
}
}
function getEmotionOptions(nextConfig, development) {
if (!nextConfig?.compiler?.emotion) {
return null
}
let autoLabel = false
switch (nextConfig?.compiler?.emotion?.autoLabel) {
case 'never':
autoLabel = false
break
case 'always':
autoLabel = true
break
case 'dev-only':
default:
autoLabel = !!development
break
}
return {
enabled: true,
autoLabel,
labelFormat: nextConfig?.compiler?.emotion?.labelFormat,
sourcemap: development
? nextConfig?.compiler?.emotion?.sourceMap ?? true
: false,
}
}
export function getJestSWCOptions({
isServer,
filename,
esm,
nextConfig,
jsConfig,
pagesDir,
hasServerComponents,
// This is not passed yet as "paths" resolving needs a test first
// resolvedBaseUrl,
}) {
let baseOptions = getBaseSWCOptions({
filename,
jest: true,
development: false,
hasReactRefresh: false,
globalWindow: !isServer,
nextConfig,
jsConfig,
hasServerComponents,
// resolvedBaseUrl,
})
const isNextDist = nextDistPath.test(filename)
return {
...baseOptions,
env: {
targets: {
// Targets the current version of Node.js
node: process.versions.node,
},
},
module: {
type: esm && !isNextDist ? 'es6' : 'commonjs',
},
disableNextSsg: true,
disablePageConfig: true,
pagesDir,
}
}
export function getLoaderSWCOptions({
filename,
development,
isServer,
isServerLayer,
pagesDir,
isPageFile,
hasReactRefresh,
nextConfig,
jsConfig,
supportedBrowsers,
swcCacheDir,
relativeFilePathFromRoot,
hasServerComponents,
// This is not passed yet as "paths" resolving is handled by webpack currently.
// resolvedBaseUrl,
}) {
let baseOptions = getBaseSWCOptions({
filename,
development,
globalWindow: !isServer,
hasReactRefresh,
nextConfig,
jsConfig,
// resolvedBaseUrl,
swcCacheDir,
isServerLayer,
relativeFilePathFromRoot,
hasServerComponents,
})
const isNextDist = nextDistPath.test(filename)
if (isServer) {
return {
...baseOptions,
// Disables getStaticProps/getServerSideProps tree shaking on the server compilation for pages
disableNextSsg: true,
disablePageConfig: true,
isDevelopment: development,
isServer,
pagesDir,
isPageFile,
env: {
targets: {
// Targets the current version of Node.js
node: process.versions.node,
},
},
}
} else {
// Matches default @babel/preset-env behavior
baseOptions.jsc.target = 'es5'
return {
...baseOptions,
// Ensure Next.js internals are output as commonjs modules
...(isNextDist
? {
module: {
type: 'commonjs',
},
}
: {}),
disableNextSsg: !isPageFile,
isDevelopment: development,
isServer,
pagesDir,
isPageFile,
...(supportedBrowsers && supportedBrowsers.length > 0
? {
env: {
targets: supportedBrowsers,
},
}
: {}),
}
}
}