Skip to content

Commit

Permalink
Interpolate module.exports as default import (#36082)
Browse files Browse the repository at this point in the history
fixes #34412

## Bug

- [x] Related issues linked using `fixes #number`
- [ ] Integration tests added
- [ ] Errors have 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 helpful link attached, see `contributing.md`

## Documentation / Examples

- [ ] Make sure the linting passes by running `yarn lint`
  • Loading branch information
Brooooooklyn committed Apr 12, 2022
1 parent a9d6d9f commit da6f271
Show file tree
Hide file tree
Showing 8 changed files with 77 additions and 12 deletions.
Expand Up @@ -10,6 +10,8 @@ import { promisify } from 'util'
import { parse } from '../../swc'
import { buildExports } from './utils'

const IS_NEXT_CLIENT_BUILT_IN = /[\\/]next[\\/](link|image)\.js$/

function addExportNames(names: string[], node: any) {
if (!node) return
switch (node.type) {
Expand Down Expand Up @@ -57,7 +59,7 @@ async function collectExports(
const names: string[] = []

// Next.js built-in client components
if (/[\\/]next[\\/](link|image)\.js$/.test(resourcePath)) {
if (IS_NEXT_CLIENT_BUILT_IN.test(resourcePath)) {
names.push('default')
}

Expand Down Expand Up @@ -158,12 +160,14 @@ export default async function transformSource(
const moduleRefDef =
"const MODULE_REFERENCE = Symbol.for('react.module.reference');\n"

const isNextClientBuiltIn = IS_NEXT_CLIENT_BUILT_IN.test(resourcePath)

const clientRefsExports = names.reduce((res: any, name) => {
const moduleRef =
'{ $$typeof: MODULE_REFERENCE, filepath: ' +
JSON.stringify(resourcePath) +
', name: ' +
JSON.stringify(name) +
JSON.stringify(name === 'default' && isNextClientBuiltIn ? '' : name) +
' };\n'
res[name] = moduleRef
return res
Expand Down
8 changes: 6 additions & 2 deletions packages/next/lib/eslint/runLintCheck.ts
Expand Up @@ -34,8 +34,12 @@ function isValidSeverity(severity: string): severity is Severity {
}

const requiredPackages = [
{ file: 'eslint', pkg: 'eslint' },
{ file: 'eslint-config-next', pkg: 'eslint-config-next' },
{ file: 'eslint', pkg: 'eslint', exportsRestrict: false },
{
file: 'eslint-config-next',
pkg: 'eslint-config-next',
exportsRestrict: false,
},
]

async function cliPrompt() {
Expand Down
23 changes: 22 additions & 1 deletion packages/next/lib/has-necessary-dependencies.ts
@@ -1,6 +1,10 @@
import { existsSync } from 'fs'
import { join, relative } from 'path'

export interface MissingDependency {
file: string
pkg: string
exportsRestrict: boolean
}

export type NecessaryDependencies = {
Expand All @@ -15,7 +19,24 @@ export async function hasNecessaryDependencies(
let resolutions = new Map<string, string>()
const missingPackages = requiredPackages.filter((p) => {
try {
resolutions.set(p.pkg, require.resolve(p.file, { paths: [baseDir] }))
if (p.exportsRestrict) {
const pkgPath = require.resolve(`${p.pkg}/package.json`, {
paths: [baseDir],
})
const fileNameToVerify = relative(p.pkg, p.file)
if (fileNameToVerify) {
const fileToVerify = join(pkgPath, '..', fileNameToVerify)
if (existsSync(fileToVerify)) {
resolutions.set(p.pkg, join(pkgPath, '..'))
} else {
return true
}
} else {
resolutions.set(p.pkg, pkgPath)
}
} else {
resolutions.set(p.pkg, require.resolve(p.file, { paths: [baseDir] }))
}
return false
} catch (_) {
return true
Expand Down
8 changes: 7 additions & 1 deletion packages/next/lib/verify-partytown-setup.ts
Expand Up @@ -61,7 +61,13 @@ export async function verifyPartytownSetup(
try {
const partytownDeps: NecessaryDependencies = await hasNecessaryDependencies(
dir,
[{ file: '@builder.io/partytown', pkg: '@builder.io/partytown' }]
[
{
file: '@builder.io/partytown',
pkg: '@builder.io/partytown',
exportsRestrict: false,
},
]
)

if (partytownDeps.missing?.length > 0) {
Expand Down
14 changes: 11 additions & 3 deletions packages/next/lib/verifyTypeScriptSetup.ts
Expand Up @@ -17,9 +17,17 @@ import { missingDepsError } from './typescript/missingDependencyError'
import { NextConfigComplete } from '../server/config-shared'

const requiredPackages = [
{ file: 'typescript', pkg: 'typescript' },
{ file: '@types/react/index.d.ts', pkg: '@types/react' },
{ file: '@types/node/index.d.ts', pkg: '@types/node' },
{ file: 'typescript', pkg: 'typescript', exportsRestrict: false },
{
file: '@types/react/index.d.ts',
pkg: '@types/react',
exportsRestrict: true,
},
{
file: '@types/node/index.d.ts',
pkg: '@types/node',
exportsRestrict: false,
},
]

export async function verifyTypeScriptSetup(
Expand Down
15 changes: 14 additions & 1 deletion packages/next/taskfile-swc.js
Expand Up @@ -14,7 +14,11 @@ module.exports = function (task) {
function* (
file,
serverOrClient,
{ stripExtension, keepImportAssertions = false } = {}
{
stripExtension,
keepImportAssertions = false,
interopClientDefaultExport = false,
} = {}
) {
// Don't compile .d.ts
if (file.base.endsWith('.d.ts')) return
Expand Down Expand Up @@ -111,6 +115,15 @@ module.exports = function (task) {
}

if (output.map) {
if (interopClientDefaultExport) {
output.code += `
if (typeof exports.default === 'function' || (typeof exports.default === 'object' && exports.default !== null)) {
Object.assign(exports.default, exports);
module.exports = exports.default;
}
`
}

const map = `${file.base}.map`

output.code += Buffer.from(`\n//# sourceMappingURL=${map}`)
Expand Down
2 changes: 1 addition & 1 deletion packages/next/taskfile.js
Expand Up @@ -1826,7 +1826,7 @@ export async function nextbuild(task, opts) {
export async function client(task, opts) {
await task
.source(opts.src || 'client/**/*.+(js|ts|tsx)')
.swc('client', { dev: opts.dev })
.swc('client', { dev: opts.dev, interopClientDefaultExport: true })
.target('dist/client')
notify('Compiled client files')
}
Expand Down
11 changes: 10 additions & 1 deletion test/e2e/type-module-interop/index.test.ts
Expand Up @@ -11,8 +11,17 @@ describe('Type module interop', () => {
next = await createNext({
files: {
'pages/index.js': `
import Link from 'next/link'
export default function Page() {
return <p>hello world</p>
return (
<>
<p>hello world</p>
<Link href="/modules">
<a id="link-to-module">link to module</a>
</Link>
</>
)
}
`,
'pages/modules.jsx': `
Expand Down

0 comments on commit da6f271

Please sign in to comment.