2019-12-03 22:08:49 +01:00
/* eslint-env jest */
2021-08-24 14:52:45 +02:00
import os from 'os'
import path from 'path'
2021-10-07 01:46:46 +02:00
import { Span } from 'next/dist/trace'
import loader from 'next/dist/build/babel/loader'
2019-12-05 18:04:00 +01:00
const dir = path . resolve ( os . tmpdir ( ) )
2019-12-03 22:08:49 +01:00
2021-08-24 14:52:45 +02:00
const babel = async ( code : string , queryOpts = { } as any ) = > {
2021-10-07 01:46:46 +02:00
const { isServer = false , resourcePath = ` index.js ` } = queryOpts
2021-05-07 19:21:54 +02:00
2019-12-03 22:08:49 +01:00
let isAsync = false
2021-08-24 14:52:45 +02:00
return new Promise < string > ( ( resolve , reject ) = > {
2019-12-03 22:08:49 +01:00
function callback ( err , content ) {
if ( err ) {
reject ( err )
} else {
2021-10-07 01:46:46 +02:00
resolve ( content . replace ( /\n/g , '' ) )
2019-12-03 22:08:49 +01:00
}
}
const res = loader . bind ( {
resourcePath ,
async ( ) {
isAsync = true
return callback
} ,
callback ,
2020-06-30 15:05:29 +02:00
emitWarning() { } ,
2019-12-03 22:08:49 +01:00
query : {
// loader opts
2019-12-05 18:04:00 +01:00
cwd : dir ,
2019-12-03 22:08:49 +01:00
isServer ,
2019-12-05 18:04:00 +01:00
distDir : path.resolve ( dir , '.next' ) ,
2021-05-07 19:21:54 +02:00
pagesDir :
'pagesDir' in queryOpts
? queryOpts . pagesDir
: path . resolve ( dir , 'pages' ) ,
2019-12-03 22:08:49 +01:00
cache : false ,
2021-10-07 01:46:46 +02:00
hasReactRefresh : false ,
2019-12-03 22:08:49 +01:00
} ,
2021-10-07 01:46:46 +02:00
currentTraceSpan : new Span ( { name : 'test' } ) ,
2019-12-03 22:08:49 +01:00
} ) ( code , null )
if ( ! isAsync ) {
resolve ( res )
}
} )
}
describe ( 'next-babel-loader' , ( ) = > {
describe ( 'replace constants' , ( ) = > {
it ( 'should replace typeof window expression nested' , async ( ) = > {
const code = await babel ( 'function a(){console.log(typeof window)}' )
expect ( code ) . toMatchInlineSnapshot (
2021-10-07 01:46:46 +02:00
` "function a() { console.log( \\ "object \\ ");}" `
2019-12-03 22:08:49 +01:00
)
} )
it ( 'should replace typeof window expression top level (client)' , async ( ) = > {
const code = await babel ( 'typeof window;' )
expect ( code ) . toMatchInlineSnapshot ( ` " \\ "object \\ ";" ` )
} )
it ( 'should replace typeof window expression top level (server)' , async ( ) = > {
const code = await babel ( 'typeof window;' , { isServer : true } )
expect ( code ) . toMatchInlineSnapshot ( ` " \\ "undefined \\ ";" ` )
} )
2020-05-18 19:16:07 +02:00
it ( 'should replace typeof window in === expression nested' , async ( ) = > {
2019-12-03 22:08:49 +01:00
const code = await babel (
` function a(){console.log(typeof window === 'undefined')} `
)
2021-10-07 01:46:46 +02:00
expect ( code ) . toMatchInlineSnapshot (
` "function a() { console.log(false);}" `
)
2019-12-03 22:08:49 +01:00
} )
it ( 'should replace typeof window expression top level' , async ( ) = > {
const code = await babel ( ` typeof window === 'undefined'; ` )
expect ( code ) . toMatchInlineSnapshot ( ` "false;" ` )
} )
2020-05-18 19:16:07 +02:00
it ( 'should replace typeof window in === expression top level' , async ( ) = > {
2019-12-03 22:08:49 +01:00
const code = await babel ( ` typeof window === 'object'; ` )
expect ( code ) . toMatchInlineSnapshot ( ` "true;" ` )
} )
2020-05-18 19:16:07 +02:00
it ( 'should replace typeof window in !== expression top level' , async ( ) = > {
2019-12-03 22:08:49 +01:00
const code = await babel ( ` typeof window !== 'undefined'; ` )
expect ( code ) . toMatchInlineSnapshot ( ` "true;" ` )
} )
2020-05-18 19:16:07 +02:00
it ( 'should replace typeof window expression !== object top level' , async ( ) = > {
2019-12-03 22:08:49 +01:00
const code = await babel ( ` typeof window !== 'object'; ` )
expect ( code ) . toMatchInlineSnapshot ( ` "false;" ` )
} )
2020-05-18 19:16:07 +02:00
it ( 'should replace typeof window expression top level serverside' , async ( ) = > {
2019-12-03 22:08:49 +01:00
const code = await babel ( ` typeof window !== 'undefined'; ` , {
isServer : true ,
} )
expect ( code ) . toMatchInlineSnapshot ( ` "false;" ` )
} )
2020-05-18 19:16:07 +02:00
it ( 'should replace typeof window expression !== object top level serverside' , async ( ) = > {
2019-12-03 22:08:49 +01:00
const code = await babel ( ` typeof window !== 'object'; ` , {
isServer : true ,
} )
expect ( code ) . toMatchInlineSnapshot ( ` "true;" ` )
} )
it ( 'should replace process.browser (1)' , async ( ) = > {
const code = await babel ( ` process.browser ` , {
isServer : false ,
} )
expect ( code ) . toMatchInlineSnapshot ( ` "true;" ` )
} )
it ( 'should replace process.browser (2)' , async ( ) = > {
const code = await babel ( ` process.browser ` , {
isServer : true ,
} )
expect ( code ) . toMatchInlineSnapshot ( ` "false;" ` )
} )
it ( 'should replace process.browser (3)' , async ( ) = > {
const code = await babel ( ` process.browser == false ` , {
isServer : true ,
} )
expect ( code ) . toMatchInlineSnapshot ( ` "true;" ` )
} )
it ( 'should replace process.browser (4)' , async ( ) = > {
const code = await babel ( ` if (process.browser === false) {} ` , {
isServer : true ,
} )
2021-10-07 01:46:46 +02:00
expect ( code ) . toMatchInlineSnapshot ( ` "if (true) {}" ` )
2019-12-03 22:08:49 +01:00
} )
it ( 'should replace process.browser (5)' , async ( ) = > {
const code = await babel ( ` if (process.browser) {} ` , {
isServer : true ,
} )
2021-10-07 01:46:46 +02:00
expect ( code ) . toMatchInlineSnapshot ( ` "if (false) {}" ` )
2019-12-03 22:08:49 +01:00
} )
it ( 'should replace NODE_ENV on client (prod)' , async ( ) = > {
const code = await babel ( ` process.env.NODE_ENV ` , {
isServer : false ,
} )
expect ( code ) . toMatchInlineSnapshot ( ` " \\ "production \\ ";" ` )
} )
it ( 'should replace NODE_ENV on server' , async ( ) = > {
const code = await babel ( ` process.env.NODE_ENV ` , {
isServer : true ,
} )
expect ( code ) . toMatchInlineSnapshot ( ` " \\ "production \\ ";" ` )
} )
2020-05-18 19:16:07 +02:00
it ( 'should replace NODE_ENV in === statement (prod)' , async ( ) = > {
2019-12-03 22:08:49 +01:00
const code = await babel ( ` if (process.env.NODE_ENV === 'production') {} ` )
2021-10-07 01:46:46 +02:00
expect ( code ) . toMatchInlineSnapshot ( ` "if (true) {}" ` )
2019-12-03 22:08:49 +01:00
} )
2020-05-18 19:16:07 +02:00
it ( 'should replace NODE_ENV in !== statement (prod)' , async ( ) = > {
2019-12-03 22:08:49 +01:00
const code = await babel ( ` if (process.env.NODE_ENV !== 'production') {} ` )
2021-10-07 01:46:46 +02:00
expect ( code ) . toMatchInlineSnapshot ( ` "if (false) {}" ` )
2019-12-03 22:08:49 +01:00
} )
2019-12-05 18:04:00 +01:00
2021-05-07 19:21:54 +02:00
it ( 'should handle no pagesDir' , async ( ) = > {
const code = await babel (
`
import dynamic from 'next/dynamic'
const Comp = dynamic ( ( ) = > import ( 'comp' ) )
export default function Page ( props ) {
return < Comp / >
}
` ,
{
pagesDir : undefined ,
}
)
expect (
2021-10-07 01:46:46 +02:00
code . replace ( /modules: \[".*?"/ , 'modules:["/path/to/page"' )
2021-05-07 19:21:54 +02:00
) . toMatchInlineSnapshot (
2021-10-07 01:46:46 +02:00
` "var _jsxFileName = \\ "index.js \\ ";import React from \\ "react \\ ";var __jsx = React.createElement;import dynamic from 'next/dynamic';const Comp = dynamic(() => import('comp'), { loadableGenerated: { webpack: () => [require.resolveWeak('comp')], modules:[ \\ "/path/to/page \\ " + 'comp'] }});export default function Page(props) { return __jsx(Comp, { __self: this, __source: { fileName: _jsxFileName, lineNumber: 7, columnNumber: 18 } });}" `
2021-05-07 19:21:54 +02:00
)
} )
2019-12-05 18:04:00 +01:00
it ( 'should not drop unused exports by default' , async ( ) = > {
const code = await babel (
// effectful
` import"core-js"; ` +
// basic
` import{foo,bar}from"a";import baz from"b"; ` +
// complex
2019-12-06 17:46:00 +01:00
` import * as React from "react"; ` +
2019-12-05 18:04:00 +01:00
` import baz2,{yeet}from"c"; ` +
` import baz3,{cats}from"d"; ` +
` import{c,d}from"e"; ` +
` import{e as ee,f as ff}from"f"; `
)
expect ( code ) . toMatchInlineSnapshot (
2021-10-07 01:46:46 +02:00
` "import \\ "core-js \\ ";import { foo, bar } from \\ "a \\ ";import baz from \\ "b \\ ";import * as React from \\ "react \\ ";import baz2, { yeet } from \\ "c \\ ";import baz3, { cats } from \\ "d \\ ";import { c, d } from \\ "e \\ ";import { e as ee, f as ff } from \\ "f \\ ";" `
2019-12-05 18:04:00 +01:00
)
} )
const pageFile = path . resolve ( dir , 'pages' , 'index.js' )
2019-12-08 19:38:22 +01:00
const tsPageFile = pageFile . replace ( /\.js$/ , '.ts' )
2019-12-05 18:04:00 +01:00
it ( 'should not drop unused exports by default in a page' , async ( ) = > {
const code = await babel (
// effectful
` import"core-js"; ` +
// basic
` import{foo,bar}from"a";import baz from"b"; ` +
// complex
2019-12-06 17:46:00 +01:00
` import*as React from"react"; ` +
2019-12-05 18:04:00 +01:00
` import baz2,{yeet}from"c"; ` +
` import baz3,{cats}from"d"; ` +
` import{c,d}from"e"; ` +
` import{e as ee,f as ff}from"f"; ` ,
{ resourcePath : pageFile }
)
expect ( code ) . toMatchInlineSnapshot (
2021-10-07 01:46:46 +02:00
` "import \\ "core-js \\ ";import { foo, bar } from \\ "a \\ ";import baz from \\ "b \\ ";import * as React from \\ "react \\ ";import baz2, { yeet } from \\ "c \\ ";import baz3, { cats } from \\ "d \\ ";import { c, d } from \\ "e \\ ";import { e as ee, f as ff } from \\ "f \\ ";" `
2019-12-05 18:04:00 +01:00
)
} )
it ( 'should drop unused exports in a modern-apis page' , async ( ) = > {
const code = await babel (
// effectful
` import"core-js"; ` +
// basic
` import{foo,bar}from"a";import baz from"b"; ` +
// complex
2019-12-06 17:46:00 +01:00
` import*as React from"react"; ` +
2019-12-05 18:04:00 +01:00
` import baz2,{yeet}from"c"; ` +
` import baz3,{cats}from"d"; ` +
` import{c,d}from"e"; ` +
` import{e as ee,f as ff}from"f"; ` +
` ` +
2020-02-27 18:57:39 +01:00
` export function getStaticProps() {foo;bar;baz;cats;baz2;ff; return { props: {} } } ` ,
2019-12-05 18:04:00 +01:00
{ resourcePath : pageFile }
)
2019-12-07 04:57:14 +01:00
expect ( code ) . toMatchInlineSnapshot (
2021-10-07 01:46:46 +02:00
` "import \\ "core-js \\ ";import * as React from \\ "react \\ ";import { yeet } from \\ "c \\ ";import baz3 from \\ "d \\ ";import { c, d } from \\ "e \\ ";import { e as ee } from \\ "f \\ ";" `
2019-12-07 04:57:14 +01:00
)
2019-12-05 18:04:00 +01:00
} )
it ( 'should keep used exports in a modern-apis page (server)' , async ( ) = > {
const code = await babel (
// effectful
` import"core-js"; ` +
// basic
` import{foo,bar}from"a";import baz from"b";import ooo from"ooo"; ` +
// complex
2019-12-06 17:46:00 +01:00
` import*as React from"react"; ` +
2019-12-05 18:04:00 +01:00
` import baz2,{yeet}from"c"; ` +
` import baz3,{cats}from"d"; ` +
` import{c,d}from"e"; ` +
` import{e as ee,f as ff}from"f"; ` +
` ` +
2020-02-27 18:57:39 +01:00
` export function getStaticProps() {foo();baz2();ff();ooo(); return { props: {} }} ` +
2019-12-05 18:04:00 +01:00
` export default function () { return bar(); } ` ,
{ resourcePath : pageFile , isServer : true }
)
expect ( code ) . toMatchInlineSnapshot (
2021-10-07 01:46:46 +02:00
` "import \\ "core-js \\ ";import { foo, bar } from \\ "a \\ ";import baz from \\ "b \\ ";import ooo from \\ "ooo \\ ";import * as React from \\ "react \\ ";import baz2, { yeet } from \\ "c \\ ";import baz3, { cats } from \\ "d \\ ";import { c, d } from \\ "e \\ ";import { e as ee, f as ff } from \\ "f \\ ";export function getStaticProps() { foo(); baz2(); ff(); ooo(); return { props: {} };}export default function () { return bar();}" `
2019-12-05 18:04:00 +01:00
)
} )
it ( 'should keep used exports in a modern-apis page (client)' , async ( ) = > {
const code = await babel (
// effectful
` import"core-js"; ` +
// basic
` import{foo,bar}from"a";import baz from"b";import ooo from"ooo"; ` +
// complex
2019-12-06 17:46:00 +01:00
` import*as React from"react"; ` +
2019-12-05 18:04:00 +01:00
` import baz2,{yeet}from"c"; ` +
` import baz3,{cats}from"d"; ` +
` import{c,d}from"e"; ` +
` import{e as ee,f as ff}from"f"; ` +
` ` +
2020-02-27 18:57:39 +01:00
` export function getStaticProps() {foo();baz2();ff();ooo();cats; return { props: {} }} ` +
2019-12-05 18:04:00 +01:00
` export default function () { return cats + bar(); } ` ,
{ resourcePath : pageFile , isServer : false }
)
expect ( code ) . toMatchInlineSnapshot (
2021-10-07 01:46:46 +02:00
` "import \\ "core-js \\ ";import { bar } from \\ "a \\ ";import baz from \\ "b \\ ";import * as React from \\ "react \\ ";import { yeet } from \\ "c \\ ";import baz3, { cats } from \\ "d \\ ";import { c, d } from \\ "e \\ ";import { e as ee } from \\ "f \\ ";export var __N_SSG = true;export default function () { return cats + bar();}" `
2019-12-05 18:04:00 +01:00
)
} )
2019-12-06 17:46:00 +01:00
it ( 'should keep used exports and react in a modern-apis page with JSX (client)' , async ( ) = > {
const code = await babel (
// effectful
` import"core-js"; ` +
// basic
` import{foo,bar}from"a";import baz from"b";import ooo from"ooo"; ` +
// complex
` import*as React from"react"; ` +
` import baz2,{yeet}from"c"; ` +
` import baz3,{cats}from"d"; ` +
` import{c,d}from"e"; ` +
` import{e as ee,f as ff}from"f"; ` +
` ` +
2020-02-27 18:57:39 +01:00
` export function getStaticProps() {foo();baz2();ff();ooo(); return { props: {} }} ` +
2019-12-06 17:46:00 +01:00
` export default function () { return <div>{cats + bar()}</div> } ` ,
{ resourcePath : pageFile , isServer : false }
)
2021-10-07 01:46:46 +02:00
expect ( code ) . toContain (
` var __jsx = React.createElement;import "core-js";import { bar } from "a";import baz from "b";import * as React from "react";import { yeet } from "c";import baz3, { cats } from "d";import { c, d } from "e";import { e as ee } from "f";export var __N_SSG = true;export default function () { return __jsx("div", { __self: this, __source: { fileName: _jsxFileName, lineNumber: 1, columnNumber: 326 } }, cats + bar());} `
2020-05-15 22:51:29 +02:00
)
} )
2019-12-08 19:38:22 +01:00
it ( 'should support optional chaining for JS file' , async ( ) = > {
const code = await babel (
` let hello; ` +
` export default () => hello?.world ? 'something' : 'nothing' ` ,
{
resourcePath : pageFile ,
}
)
expect ( code ) . toMatchInlineSnapshot (
2021-10-07 01:46:46 +02:00
` "let hello;export default (() => hello !== null && hello !== void 0 && hello.world ? 'something' : 'nothing');" `
2019-12-08 19:38:22 +01:00
)
} )
it ( 'should support optional chaining for TS file' , async ( ) = > {
const code = await babel (
` let hello; ` +
` export default () => hello?.world ? 'something' : 'nothing' ` ,
{
resourcePath : tsPageFile ,
}
)
expect ( code ) . toMatchInlineSnapshot (
2021-10-07 01:46:46 +02:00
` "let hello;export default (() => hello !== null && hello !== void 0 && hello.world ? 'something' : 'nothing');" `
2019-12-08 19:38:22 +01:00
)
} )
it ( 'should support nullish coalescing for JS file' , async ( ) = > {
const code = await babel (
` const res = {
status : 0 ,
nullVal : null ,
statusText : '' ,
}
const status = res . status ? ? 999
const nullVal = res . nullVal ? ? 'another'
const statusText = res . nullVal ? ? 'not found'
export default ( ) = > 'hello'
` ,
{
resourcePath : pageFile ,
}
)
expect ( code ) . toMatchInlineSnapshot (
2021-10-07 01:46:46 +02:00
` "var _res $ status, _res $ nullVal, _res $ nullVal2;const res = { status: 0, nullVal: null, statusText: ''};const status = (_res $ status = res.status) !== null && _res $ status !== void 0 ? _res $ status : 999;const nullVal = (_res $ nullVal = res.nullVal) !== null && _res $ nullVal !== void 0 ? _res $ nullVal : 'another';const statusText = (_res $ nullVal2 = res.nullVal) !== null && _res $ nullVal2 !== void 0 ? _res $ nullVal2 : 'not found';export default (() => 'hello');" `
2019-12-08 19:38:22 +01:00
)
} )
it ( 'should support nullish coalescing for TS file' , async ( ) = > {
const code = await babel (
` const res = {
status : 0 ,
nullVal : null ,
statusText : '' ,
}
const status = res . status ? ? 999
const nullVal = res . nullVal ? ? 'another'
const statusText = res . nullVal ? ? 'not found'
export default ( ) = > 'hello'
` ,
{
resourcePath : tsPageFile ,
}
)
expect ( code ) . toMatchInlineSnapshot (
2021-10-07 01:46:46 +02:00
` "var _res $ status, _res $ nullVal, _res $ nullVal2;const res = { status: 0, nullVal: null, statusText: ''};const status = (_res $ status = res.status) !== null && _res $ status !== void 0 ? _res $ status : 999;const nullVal = (_res $ nullVal = res.nullVal) !== null && _res $ nullVal !== void 0 ? _res $ nullVal : 'another';const statusText = (_res $ nullVal2 = res.nullVal) !== null && _res $ nullVal2 !== void 0 ? _res $ nullVal2 : 'not found';export default (() => 'hello');" `
2019-12-08 19:38:22 +01:00
)
} )
2019-12-03 22:08:49 +01:00
} )
} )