Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Hannes Bornö
committed
Oct 19, 2022
1 parent
2d1f729
commit 7afa5e1
Showing
6 changed files
with
217 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
import path from 'path' | ||
import { promises as fs } from 'fs' | ||
import chalk from 'next/dist/compiled/chalk' | ||
import * as Log from '../build/output/log' | ||
|
||
const globOrig = | ||
require('next/dist/compiled/glob') as typeof import('next/dist/compiled/glob') | ||
const glob = (cwd: string, pattern: string): Promise<string[]> => { | ||
return new Promise((resolve, reject) => { | ||
globOrig(pattern, { cwd }, (err, files) => { | ||
if (err) { | ||
return reject(err) | ||
} | ||
resolve(files) | ||
}) | ||
}) | ||
} | ||
|
||
function getRootLayout(isTs: boolean) { | ||
if (isTs) { | ||
return `export default function RootLayout({ | ||
children, | ||
}: { | ||
children: React.ReactNode | ||
}) { | ||
return ( | ||
<html> | ||
<head></head> | ||
<body>{children}</body> | ||
</html> | ||
) | ||
} | ||
` | ||
} | ||
|
||
return `export default function RootLayout({ children }) { | ||
return ( | ||
<html> | ||
<head></head> | ||
<body>{children}</body> | ||
</html> | ||
) | ||
} | ||
` | ||
} | ||
|
||
export async function verifyRootLayout({ | ||
dir, | ||
appDir, | ||
tsconfigPath, | ||
}: { | ||
dir: string | ||
appDir: string | ||
tsconfigPath: string | ||
}) { | ||
try { | ||
// Only create root layout if no other layout exists | ||
const layoutFiles = await glob(dir, 'app/**/layout.{js,jsx,ts,tsx}') | ||
const hasLayout = layoutFiles.length !== 0 | ||
if (!hasLayout) { | ||
const resolvedTsConfigPath = path.join(dir, tsconfigPath) | ||
const hasTsConfig = await fs.access(resolvedTsConfigPath).then( | ||
() => true, | ||
() => false | ||
) | ||
|
||
const rootLayoutPath = path.join( | ||
appDir, | ||
`layout.${hasTsConfig ? 'tsx' : 'js'}` | ||
) | ||
await fs.writeFile(rootLayoutPath, getRootLayout(hasTsConfig)) | ||
console.log( | ||
chalk.green( | ||
`${chalk.bold( | ||
'appDir' | ||
)} is enabled but you're missing a root layout, we created ${chalk.bold( | ||
`app/layout.${hasTsConfig ? 'tsx' : 'js'}` | ||
)} for you.` | ||
) + '\n' | ||
) | ||
} | ||
} catch (error) { | ||
Log.error('Failed to create root layout', error) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
import path from 'path' | ||
import { createNext, FileRef } from 'e2e-utils' | ||
import { NextInstance } from 'test/lib/next-modes/base' | ||
import webdriver from 'next-webdriver' | ||
|
||
describe('app-dir create root layout', () => { | ||
const isDev = (global as any).isNextDev | ||
|
||
if (!isDev) { | ||
it('should only run in dev', () => {}) | ||
return | ||
} | ||
|
||
if (process.env.NEXT_TEST_REACT_VERSION === '^17') { | ||
it('should skip for react v17', () => {}) | ||
return | ||
} | ||
let next: NextInstance | ||
|
||
describe('page.js', () => { | ||
beforeAll(async () => { | ||
next = await createNext({ | ||
files: { | ||
'app/page.js': new FileRef( | ||
path.join(__dirname, 'create-root-layout/app/page.js') | ||
), | ||
'next.config.js': new FileRef( | ||
path.join(__dirname, 'create-root-layout/next.config.js') | ||
), | ||
}, | ||
dependencies: { | ||
react: 'experimental', | ||
'react-dom': 'experimental', | ||
}, | ||
}) | ||
}) | ||
afterAll(() => next.destroy()) | ||
|
||
it('create root layout', async () => { | ||
const outputIndex = next.cliOutput.length | ||
const browser = await webdriver(next.url, '/') | ||
|
||
expect(await browser.elementById('page-text').text()).toBe('Hello world!') | ||
|
||
expect(next.cliOutput.slice(outputIndex)).toInclude( | ||
"appDir is enabled but you're missing a root layout, we created app/layout.js for you." | ||
) | ||
|
||
expect(await next.readFile('app/layout.js')).toMatchInlineSnapshot(` | ||
"export default function RootLayout({ children }) { | ||
return ( | ||
<html> | ||
<head></head> | ||
<body>{children}</body> | ||
</html> | ||
) | ||
} | ||
" | ||
`) | ||
}) | ||
}) | ||
|
||
describe('page.tsx', () => { | ||
beforeAll(async () => { | ||
next = await createNext({ | ||
files: { | ||
'app/page.tsx': new FileRef( | ||
path.join(__dirname, 'create-root-layout/app/page.js') | ||
), | ||
'next.config.js': new FileRef( | ||
path.join(__dirname, 'create-root-layout/next.config.js') | ||
), | ||
}, | ||
dependencies: { | ||
react: 'experimental', | ||
'react-dom': 'experimental', | ||
typescript: 'latest', | ||
'@types/react': 'latest', | ||
'@types/node': 'latest', | ||
}, | ||
}) | ||
}) | ||
afterAll(() => next.destroy()) | ||
|
||
it('create root layout', async () => { | ||
const outputIndex = next.cliOutput.length | ||
const browser = await webdriver(next.url, '/') | ||
|
||
expect(await browser.elementById('page-text').text()).toBe('Hello world!') | ||
|
||
expect(next.cliOutput.slice(outputIndex)).toInclude( | ||
"appDir is enabled but you're missing a root layout, we created app/layout.tsx for you." | ||
) | ||
|
||
expect(await next.readFile('app/layout.tsx')).toMatchInlineSnapshot(` | ||
"export default function RootLayout({ | ||
children, | ||
}: { | ||
children: React.ReactNode | ||
}) { | ||
return ( | ||
<html> | ||
<head></head> | ||
<body>{children}</body> | ||
</html> | ||
) | ||
} | ||
" | ||
`) | ||
}) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export default function Page() { | ||
return <p id="page-text">Hello world!</p> | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
module.exports = { | ||
experimental: { | ||
appDir: true, | ||
}, | ||
} |