Skip to content

Commit

Permalink
Merge branch 'canary' into swc-v-1
Browse files Browse the repository at this point in the history
  • Loading branch information
kodiakhq[bot] committed Feb 15, 2022
2 parents 524ff06 + 62b1704 commit f214d49
Show file tree
Hide file tree
Showing 8 changed files with 97 additions and 16 deletions.
2 changes: 1 addition & 1 deletion docs/api-reference/next/image.md
Expand Up @@ -323,7 +323,7 @@ module.exports = {
The following Image Optimization cloud providers are included:

- Default: Works automatically with `next dev`, `next start`, or a custom server
- [Vercel](https://vercel.com): Works automatically when you deploy on Vercel, no configuration necessary. [Learn more](https://vercel.com/docs/next.js/image-optimization)
- [Vercel](https://vercel.com): Works automatically when you deploy on Vercel, no configuration necessary. [Learn more](https://vercel.com/docs/concepts/image-optimization)
- [Imgix](https://www.imgix.com): `loader: 'imgix'`
- [Cloudinary](https://cloudinary.com): `loader: 'cloudinary'`
- [Akamai](https://www.akamai.com): `loader: 'akamai'`
Expand Down
4 changes: 3 additions & 1 deletion errors/failed-loading-swc.md
Expand Up @@ -8,7 +8,9 @@ SWC requires a binary be downloaded that is compatible specific to your system.

#### Possible Ways to Fix It

You might need to allow optional packages to be installed by your package manager (remove `--no-optional` flag) for the package to download correctly.
When on an M1 Mac and switching from a Node.js version without M1 support e.g. v14 to a version with e.g. v16, you may need a different swc dependency which can require re-installing `node_modules` (`npm i --force` or `yarn install --force`).

Alternatively, you might need to allow optional packages to be installed by your package manager (remove `--no-optional` flag) for the package to download correctly.

If SWC continues to fail to load you can opt-out by disabling `swcMinify` in your `next.config.js` or by adding a `.babelrc` to your project with the following content:

Expand Down
2 changes: 1 addition & 1 deletion packages/create-next-app/templates/typescript/gitignore
Expand Up @@ -23,7 +23,7 @@
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log**
.pnpm-debug.log*

# local env files
.env.local
Expand Down
7 changes: 7 additions & 0 deletions packages/next/build/index.ts
Expand Up @@ -532,6 +532,13 @@ export default async function build(
await recursiveDelete(distDir, /^cache/)
}

// Ensure commonjs handling is used for files in the distDir (generally .next)
// Files outside of the distDir can be "type": "module"
await promises.writeFile(
path.join(distDir, 'package.json'),
'{"type": "commonjs"}'
)

// We need to write the manifest with rewrites before build
// so serverless can import the manifest
await nextBuildSpan
Expand Down
10 changes: 10 additions & 0 deletions packages/next/server/dev/hot-reloader.ts
Expand Up @@ -41,6 +41,7 @@ import { DecodeError } from '../../shared/lib/utils'
import { Span, trace } from '../../trace'
import { getProperError } from '../../lib/is-error'
import ws from 'next/dist/compiled/ws'
import { promises as fs } from 'fs'

const wsServer = new ws.Server({ noServer: true })

Expand Down Expand Up @@ -432,6 +433,15 @@ export default class HotReloader {
startSpan.stop() // Stop immediately to create an artificial parent span

await this.clean(startSpan)
// Ensure distDir exists before writing package.json
await fs.mkdir(this.config.distDir, { recursive: true })

// Ensure commonjs handling is used for files in the distDir (generally .next)
// Files outside of the distDir can be "type": "module"
await fs.writeFile(
join(this.config.distDir, 'package.json'),
'{"type": "commonjs"}'
)

const configs = await this.getWebpackConfig(startSpan)

Expand Down
Expand Up @@ -3,9 +3,9 @@ import { RefreshRuntimeGlobals } from '../runtime'
declare const self: Window & RefreshRuntimeGlobals

type Dictionary = { [key: string]: unknown }
declare const module: {
declare const __webpack_module__: {
id: string
__proto__: { exports: unknown }
exports: unknown
hot: {
accept: () => void
dispose: (onDispose: (data: Dictionary) => void) => void
Expand All @@ -27,27 +27,30 @@ export default function () {
// AMP / No-JS mode does not inject these helpers:
'$RefreshHelpers$' in self
) {
var currentExports = module.__proto__.exports
var prevExports = module.hot.data?.prevExports ?? null
// @ts-ignore __webpack_module__ is global
var currentExports = __webpack_module__.exports
// @ts-ignore __webpack_module__ is global
var prevExports = __webpack_module__.hot.data?.prevExports ?? null

// This cannot happen in MainTemplate because the exports mismatch between
// templating and execution.
self.$RefreshHelpers$.registerExportsForReactRefresh(
currentExports,
module.id
__webpack_module__.id
)

// A module can be accepted automatically based on its exports, e.g. when
// it is a Refresh Boundary.
if (self.$RefreshHelpers$.isReactRefreshBoundary(currentExports)) {
// Save the previous exports on update so we can compare the boundary
// signatures.
module.hot.dispose(function (data) {
__webpack_module__.hot.dispose(function (data) {
data.prevExports = currentExports
})
// Unconditionally accept an update to this module, we'll check if it's
// still a Refresh Boundary later.
module.hot.accept()
// @ts-ignore importMeta is replaced in the loader
global.importMeta.webpackHot.accept()

// This field is set when the previous version of this module was a
// Refresh Boundary, letting us know we need to check for invalidation or
Expand All @@ -66,7 +69,7 @@ export default function () {
currentExports
)
) {
module.hot.invalidate()
__webpack_module__.hot.invalidate()
} else {
self.$RefreshHelpers$.scheduleUpdate()
}
Expand All @@ -78,7 +81,7 @@ export default function () {
// because we already accepted this update (accidental side effect).
var isNoLongerABoundary = prevExports !== null
if (isNoLongerABoundary) {
module.hot.invalidate()
__webpack_module__.hot.invalidate()
}
}
}
Expand Down
25 changes: 21 additions & 4 deletions packages/react-refresh-utils/loader.ts
Expand Up @@ -2,16 +2,33 @@ import type { LoaderDefinition } from 'webpack'
import RefreshModuleRuntime from './internal/ReactRefreshModule.runtime'

let refreshModuleRuntime = RefreshModuleRuntime.toString()
refreshModuleRuntime = refreshModuleRuntime.slice(
refreshModuleRuntime.indexOf('{') + 1,
refreshModuleRuntime.lastIndexOf('}')
refreshModuleRuntime = refreshModuleRuntime
.slice(
refreshModuleRuntime.indexOf('{') + 1,
refreshModuleRuntime.lastIndexOf('}')
)
// Given that the import above executes the module we need to make sure it does not crash on `import.meta` not being allowed.
.replace('global.importMeta', 'import.meta')

let commonJsrefreshModuleRuntime = refreshModuleRuntime.replace(
'import.meta.webpackHot',
'module.hot'
)

const ReactRefreshLoader: LoaderDefinition = function ReactRefreshLoader(
source,
inputSourceMap
) {
this.callback(null, `${source}\n\n;${refreshModuleRuntime}`, inputSourceMap)
this.callback(
null,
`${source}\n\n;${
// Account for commonjs not supporting `import.meta
this.resourcePath.endsWith('.cjs')
? commonJsrefreshModuleRuntime
: refreshModuleRuntime
}`,
inputSourceMap
)
}

export default ReactRefreshLoader
42 changes: 42 additions & 0 deletions test/e2e/type-module-interop/index.test.ts
@@ -0,0 +1,42 @@
import { createNext } from 'e2e-utils'
import { NextInstance } from 'test/lib/next-modes/base'
import { hasRedbox, renderViaHTTP } from 'next-test-utils'
import webdriver from 'next-webdriver'

describe('Type module interop', () => {
let next: NextInstance

beforeAll(async () => {
next = await createNext({
files: {
'pages/index.js': `
export default function Page() {
return <p>hello world</p>
}
`,
},
dependencies: {},
})
const contents = await next.readFile('package.json')
const pkg = JSON.parse(contents)
await next.patchFile(
'package.json',
JSON.stringify({
...pkg,
type: 'module',
})
)
})
afterAll(() => next.destroy())

it('should render server-side', async () => {
const html = await renderViaHTTP(next.url, '/')
expect(html).toContain('hello world')
})

it('should render client-side', async () => {
const browser = await webdriver(next.url, '/')
expect(await hasRedbox(browser)).toBe(false)
await browser.close()
})
})

0 comments on commit f214d49

Please sign in to comment.