1
0
mirror of synced 2026-02-08 15:00:08 -05:00

Compare commits

...

14 Commits

Author SHA1 Message Date
Brandon Bayer
4068b4493e upgrade monorepo react to v18 alpha 2021-07-05 16:37:39 -04:00
Brandon Bayer
0448535ef0 Add test for Page.redirectAuthenticatedTo (#2531)
(meta)
2021-07-05 16:21:00 -04:00
Brandon Bayer
0df6aef3a3 fix broken api routes in canary release (#2545)
(patch)
2021-07-05 12:50:56 -04:00
Brandon Bayer
5ba2989592 fix cli npm build
(ignore)
2021-07-05 12:45:24 -04:00
Blitz.js Bot
9240d86ed6 (meta) added @esemeniuc as contributor 2021-07-05 12:09:51 -04:00
Eric Semeniuc
27e0dacdaf Route manifest replace nbsp with normal space (#2482)
Co-authored-by: Brandon Bayer <b@bayer.ws> (patch)
2021-07-05 12:09:46 -04:00
Brandon Bayer
8dc1cd3ca8 Fix CSRF error when using session.$setPublicData (#2541)
(patch)
2021-07-05 11:03:16 -04:00
Brandon Bayer
413bc01676 bump recipe/example versions (ignore) 2021-06-24 21:11:02 -06:00
Brandon Bayer
7c025e9bd7 v0.38.3-canary.1 2021-06-24 21:09:13 -06:00
Brandon Bayer
24e51c7ae5 Next.js Fork Migration: Move blitz.config.(js|ts) support into nextjs core (patch) (#2532) 2021-06-24 23:06:55 -04:00
Brandon Bayer
b2f84f1224 bump recipe/example versions (ignore) 2021-06-24 11:40:33 -06:00
Brandon Bayer
8df18f24ad v0.38.3-canary.0 2021-06-24 11:39:25 -06:00
Brandon Bayer
d480d84e46 Move multiple pages folders support plus pages+api sibling into nextjs core (#2502)
(patch)
2021-06-24 13:37:34 -04:00
Brandon Bayer
f0a9fbeb14 bump recipe/example versions (ignore) 2021-06-24 10:00:19 -06:00
158 changed files with 1117 additions and 1562 deletions

View File

@@ -2860,6 +2860,16 @@
"contributions": [
"code"
]
},
{
"login": "esemeniuc",
"name": "Eric Semeniuc",
"avatar_url": "https://avatars.githubusercontent.com/u/3838856?v=4",
"profile": "https://semeniuc.ml/",
"contributions": [
"test",
"code"
]
}
],
"contributorsPerLine": 7,

1
.gitignore vendored
View File

@@ -30,3 +30,4 @@ examples/auth2
db.sqlite-journal
test/integration/**/db.json
test/**/*/out
.blitz**

View File

@@ -6,7 +6,7 @@
<img alt="" src="https://img.shields.io/badge/Join%20our%20community-6700EB.svg?style=for-the-badge&labelColor=000000&logoWidth=20&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAQ9SURBVHgB7d3dVdtAEIbhcSpICUoH0IEogQqSVBBSAU4FSSpIOoAORAfQgSghHXzZ1U/YcMD4R9rZmf2ec3y448LyiNf27iLiGIAmPLrweC9Un3DhrzG6EarLNP09nlwJ1SOZ/lQr5N80/S/p2QMVCBf5N17XCfm1Y/rBHqjAG9PPHvBsz+mf9WAP+HLA9M/YA14cOP2payH7jpj+VCtk1wnTP+vj7xCy6cTpn7EHLMLp059iD1iD8eveJbVCNsSLheX1YA/YgOWnf8YeKB3Wmf7Ud6Fy4f/FHmtpxbl3YlC4MJ/Cj0bWdwPnPbARg+L0S54XQHS32WwuxClzd4CM0z9rPfeAuTtA5ulPXYQ7wZ04Y+oOoDD9KZc9YOoOoDj9s4dwFzgXR6w1wIPoOvPWA9buAHEJ173o3gWiy3AnuBUHLEbgmYwvAk1/wuM8vAgexThzbwPDkx7/DHwVXfFOxP2GmsKd4Ab6zPeAyU8CI7AHFmH2BRCBPXAyk18GzUrqAXCTiR4ssyj0VFw/oCU8+e+RZ33AWz6KMaYbIIWxB+JSLs1bsbkeMN0AqakHvoku9oA2sAfqBvbAQdw0QArsgb25aYBUQT3QgT2gB+yBuqGcHij2UCqXDZACe2Anlw2QYg/QAOyBuoE98CL3DZDCuK4/rh/Q7oGL6U+TOvcNkJoijN8X1C48+T+g75eQDrAH/qmqAVJgDwyqaoAUe4AGYA/UDZX3QLUNkEIZPRCd5+6BahsgVUgPROwBTSijB7jpVAvGHriHvmw9wAZ4BpX1ABvgmakHtPcbRuwBTWAPULgAV9D/jKDY9YRvwvgEaurD44uQHvAol7qBW7WKluVtIHiUS7GyvA0s6CiXDnxrpQfsgbqBS7GKk/2jYHCrVlGyfxTMrVo0ALdq1Q3sgSKofh0M9oA61a+D2QM0AHugbmAPqClmSRjK2apVVQ8UsySsoK1aHdgDesCtWnUDeyCrIpeFg1u3sylyWTi3btMA7IG6gT2wuuK3hoE9sKrit4YVslWLPaAN7IG6ocKt2zmY2h4O9sDiTG0PZw/QANy6XTewBxZj9ogYVHy025LMHhEz9cBn0We6B0yfERReBLfhx0/R1YQHPx/QBPbA0VwcEwf2wNFcHBPHHjiem3MC2QPHcXdSaJjA+KfgTPQ8hhfjBzHC40mhlzJ+Xq9lK4a4PCs43AVaGTed5mZq+iOXZwWHi3AnOj2wFWNcnxYe7gTxLtBKHuamP/J+Wnh8a5irB7ZC5Yk9gPX1QuXC+usHWqGyhYvUYR0a7zboUOFCNVhnk0krZAOW7wFOvzXhom2xnEbIHizTA1wEYhWW6YFGyC6c1gOcfg9wfA80Qj7g8B7g9HuCww+haIR8wf49wOn3Cvv9k8tGyC/s7gFOv3fY3QONkH+v9MBWqB7PeqDn9FcIT//kcitUn6kHOu/T/xfWzlQy3dEHhwAAAABJRU5ErkJggg==">
</a>
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
<a aria-label="All Contributors" href="#contributors-"><img alt="" src="https://img.shields.io/badge/all_contributors-303-17BB8A.svg?style=for-the-badge&labelColor=000000"></a>
<a aria-label="All Contributors" href="#contributors-"><img alt="" src="https://img.shields.io/badge/all_contributors-304-17BB8A.svg?style=for-the-badge&labelColor=000000"></a>
<!-- ALL-CONTRIBUTORS-BADGE:END -->
<a aria-label="License" href="https://github.com/blitz-js/blitz/blob/canary/LICENSE">
<img alt="" src="https://img.shields.io/npm/l/blitz.svg?style=for-the-badge&labelColor=000000&color=blue">
@@ -635,6 +635,7 @@ Thanks to these wonderful people ([emoji key](https://allcontributors.org/docs/e
<tr>
<td align="center"><a href="http://anolilab.de"><img src="https://avatars.githubusercontent.com/u/2716058?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Daniel Bannert</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=prisis" title="Code">💻</a></td>
<td align="center"><a href="https://benjakugler96.github.io/"><img src="https://avatars.githubusercontent.com/u/53273645?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Benja Kugler</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=benjakugler96" title="Code">💻</a></td>
<td align="center"><a href="https://semeniuc.ml/"><img src="https://avatars.githubusercontent.com/u/3838856?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Eric Semeniuc</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=esemeniuc" title="Tests">⚠️</a> <a href="https://github.com/blitz-js/blitz/commits?author=esemeniuc" title="Code">💻</a></td>
</tr>
</table>

View File

@@ -12,16 +12,8 @@ import {
} from "blitz"
import getUser from "app/users/queries/getUser"
import logout from "app/auth/mutations/logout"
import path from "path"
export const getServerSideProps: GetServerSideProps = async ({req, res}) => {
// Ensure these files are not eliminated by trace-based tree-shaking (like Vercel)
// https://github.com/blitz-js/blitz/issues/794
path.resolve("next.config.js")
path.resolve("blitz.config.js")
path.resolve(".next/blitz/db.js")
// End anti-tree-shaking
const session = await getSession(req, res)
console.log("Session id:", session.userId)
try {

View File

@@ -29,14 +29,14 @@
},
"dependencies": {
"@prisma/client": "2.24.1",
"blitz": "0.38.1",
"blitz": "0.38.3-canary.1",
"final-form": "4.20.1",
"passport-auth0": "1.4.0",
"passport-github2": "0.1.12",
"passport-twitter": "1.0.4",
"prisma": "2.24.1",
"react": "0.0.0-experimental-6a589ad71",
"react-dom": "0.0.0-experimental-6a589ad71",
"react": "18.0.0-alpha-ed6c091fe-20210701",
"react-dom": "18.0.0-alpha-ed6c091fe-20210701",
"react-final-form": "6.5.2"
},
"devDependencies": {
@@ -48,7 +48,7 @@
"@types/passport-github2": "1.2.4",
"@types/passport-twitter": "1.0.36",
"@types/preview-email": "2.0.0",
"@types/react": "17.0.2",
"@types/react": "17.0.13",
"cross-env": "7.0.3",
"cypress": "6.2.1",
"eslint": "7.21.0",

View File

@@ -31,11 +31,11 @@
},
"dependencies": {
"@prisma/client": "2.24.1",
"blitz": "0.38.1",
"blitz": "0.38.3-canary.1",
"final-form": "4.20.1",
"prisma": "2.24.1",
"react": "0.0.0-experimental-6a589ad71",
"react-dom": "0.0.0-experimental-6a589ad71",
"react": "18.0.0-alpha-ed6c091fe-20210701",
"react-dom": "18.0.0-alpha-ed6c091fe-20210701",
"react-final-form": "6.5.2",
"secure-password": "4.0.0"
},
@@ -43,7 +43,7 @@
"@cypress/skip-test": "2.6.0",
"@testing-library/react": "11.2.5",
"@testing-library/react-hooks": "^4.0.1",
"@types/react": "17.0.2",
"@types/react": "17.0.13",
"@types/secure-password": "3.1.0",
"cypress": "6.2.1",
"eslint": "7.21.0",

View File

@@ -28,18 +28,18 @@
]
},
"dependencies": {
"blitz": "0.38.1",
"blitz": "0.38.3-canary.1",
"final-form": "4.20.1",
"graphql": "15.5.0",
"graphql-request": "3.4.0",
"react": "0.0.0-experimental-6a589ad71",
"react-dom": "0.0.0-experimental-6a589ad71",
"react": "18.0.0-alpha-ed6c091fe-20210701",
"react-dom": "18.0.0-alpha-ed6c091fe-20210701",
"react-final-form": "6.5.2"
},
"devDependencies": {
"@testing-library/react": "11.2.5",
"@testing-library/react-hooks": "^4.0.1",
"@types/react": "17.0.2",
"@types/react": "17.0.13",
"@types/secure-password": "3.1.0",
"babel-eslint": "~10.1.0",
"eslint": "7.21.0",

View File

@@ -26,14 +26,14 @@
]
},
"dependencies": {
"blitz": "0.38.1",
"blitz": "0.38.3-canary.1",
"knex": "0.21.16",
"react": "0.0.0-experimental-6a589ad71",
"react-dom": "0.0.0-experimental-6a589ad71",
"react": "18.0.0-alpha-ed6c091fe-20210701",
"react-dom": "18.0.0-alpha-ed6c091fe-20210701",
"sqlite3": "5.0.2"
},
"devDependencies": {
"@types/react": "17.0.2",
"@types/react": "17.0.13",
"babel-eslint": "~10.1.0",
"eslint": "7.21.0",
"eslint-config-react-app": "~6.0.0",

View File

@@ -33,10 +33,10 @@
},
"dependencies": {
"@prisma/client": "2.24.1",
"blitz": "0.38.1",
"blitz": "0.38.3-canary.1",
"prisma": "2.24.1",
"react": "0.0.0-experimental-6a589ad71",
"react-dom": "0.0.0-experimental-6a589ad71"
"react": "18.0.0-alpha-ed6c091fe-20210701",
"react-dom": "18.0.0-alpha-ed6c091fe-20210701"
},
"devDependencies": {
"babel-eslint": "~10.1.0",

View File

@@ -33,16 +33,16 @@
},
"dependencies": {
"@prisma/client": "2.24.1",
"blitz": "0.38.1",
"blitz": "0.38.3-canary.1",
"final-form": "4.20.1",
"prisma": "2.24.1",
"react": "0.0.0-experimental-6a589ad71",
"react-dom": "0.0.0-experimental-6a589ad71",
"react": "18.0.0-alpha-ed6c091fe-20210701",
"react-dom": "18.0.0-alpha-ed6c091fe-20210701",
"react-final-form": "6.5.2"
},
"devDependencies": {
"@types/preview-email": "2.0.0",
"@types/react": "17.0.2",
"@types/react": "17.0.13",
"eslint": "7.21.0",
"husky": "5.1.2",
"lint-staged": "10.5.4",

View File

@@ -21,15 +21,15 @@
},
"dependencies": {
"@prisma/client": "2.24.1",
"blitz": "0.38.1",
"blitz": "0.38.3-canary.1",
"final-form": "4.20.1",
"prisma": "2.24.1",
"react": "0.0.0-experimental-6a589ad71",
"react-dom": "0.0.0-experimental-6a589ad71",
"react": "18.0.0-alpha-ed6c091fe-20210701",
"react-dom": "18.0.0-alpha-ed6c091fe-20210701",
"react-final-form": "6.5.2"
},
"devDependencies": {
"@types/react": "17.0.2",
"@types/react": "17.0.13",
"cypress": "6.2.1",
"start-server-and-test": "1.11.7"
}

View File

@@ -1,5 +1,5 @@
{
"version": "0.38.2",
"version": "0.38.3-canary.1",
"packages": ["packages/*"],
"npmClient": "yarn",
"useWorkspaces": true,

View File

@@ -116,8 +116,8 @@
"prettier": "2.2.1",
"pretty-bytes": "5.3.0",
"pretty-ms": "7.0.0",
"react": "0.0.0-experimental-6a589ad71",
"react-dom": "0.0.0-experimental-6a589ad71",
"react": "18.0.0-alpha-ed6c091fe-20210701",
"react-dom": "18.0.0-alpha-ed6c091fe-20210701",
"react-ssr-prepass": "1.0.8",
"release": "6.3.0",
"request-promise-core": "1.1.2",

View File

@@ -6,6 +6,7 @@ import loadConfig from 'next/dist/compiled/babel/core-lib-config'
import { NextBabelLoaderOptions, NextJsLoaderContext } from './types'
import { consumeIterator } from './util'
import { isPageFile as isPageFileFn } from '../../utils'
const nextDistPath = /(next[\\/]dist[\\/]next-server[\\/]lib)|(next[\\/]dist[\\/]client)|(next[\\/]dist[\\/]pages)/
@@ -43,8 +44,8 @@ function getCacheCharacteristics(
filename: string
): CharacteristicsGermaneToCaching {
const { isServer, pagesDir } = loaderOptions
const isPageFile = filename.startsWith(pagesDir)
const isNextDist = nextDistPath.test(filename)
const isPageFile = !isNextDist && isPageFileFn(filename.replace(pagesDir, ''))
const hasModuleExports = source.indexOf('module.exports') !== -1
const fileExt = fileExtensionRegex.exec(filename)?.[1] || 'unknown'

View File

@@ -35,30 +35,39 @@ export default function NoAnonymousDefaultExport({
switch (def.type) {
case 'ArrowFunctionExpression': {
warn(
[
chalk.yellow.bold(
'Anonymous arrow functions cause Fast Refresh to not preserve local component state.'
),
'Please add a name to your function, for example:',
'',
chalk.bold('Before'),
chalk.cyan('export default () => <div />;'),
'',
chalk.bold('After'),
chalk.cyan('const Named = () => <div />;'),
chalk.cyan('export default Named;'),
'',
`A codemod is available to fix the most common cases: ${chalk.cyan(
'https://nextjs.link/codemod-ndc'
)}`,
].join('\n')
)
if (
!process.env.__NEXT_TEST_MODE ||
!!process.env.__NEXT_TEST_ANON_EXPORT
) {
warn(
[
chalk.yellow.bold(
'Anonymous arrow functions cause Fast Refresh to not preserve local component state.'
),
'Please add a name to your function, for example:',
'',
chalk.bold('Before'),
chalk.cyan('export default () => <div />;'),
'',
chalk.bold('After'),
chalk.cyan('const Named = () => <div />;'),
chalk.cyan('export default Named;'),
'',
`A codemod is available to fix the most common cases: ${chalk.cyan(
'https://nextjs.link/codemod-ndc'
)}`,
].join('\n')
)
}
break
}
case 'FunctionDeclaration': {
const isAnonymous = !Boolean(def.id)
if (isAnonymous) {
if (
isAnonymous &&
(!process.env.__NEXT_TEST_MODE ||
!!process.env.__NEXT_TEST_ANON_EXPORT)
) {
warn(
[
chalk.yellow.bold(

View File

@@ -10,6 +10,7 @@ import { ClientPagesLoaderOptions } from './webpack/loaders/next-client-pages-lo
import { ServerlessLoaderQuery } from './webpack/loaders/next-serverless-loader'
import { LoadedEnvFiles } from '@next/env'
import { NextConfig } from '../next-server/server/config'
import { convertPageFilePathToRoutePath } from './utils'
type PagesMapping = {
[page: string]: string
@@ -22,19 +23,19 @@ export function createPagesMapping(
const previousPages: PagesMapping = {}
const pages: PagesMapping = pagePaths.reduce(
(result: PagesMapping, pagePath): PagesMapping => {
let page = `${pagePath
let page = `${convertPageFilePathToRoutePath(pagePath)
.replace(new RegExp(`\\.+(${extensions.join('|')})$`), '')
.replace(/\\/g, '/')}`.replace(/\/index$/, '')
const pageKey = page === '' ? '/' : page
let pageKey = page === '' ? '/' : page
if (pageKey in result) {
warn(
`Duplicate page detected. ${chalk.cyan(
join('pages', previousPages[pageKey])
)} and ${chalk.cyan(
join('pages', pagePath)
)} both resolve to ${chalk.cyan(pageKey)}.`
previousPages[pageKey]
)} and ${chalk.cyan(pagePath)} both resolve to ${chalk.cyan(
pageKey
)}.`
)
} else {
previousPages[pageKey] = pagePath

View File

@@ -15,7 +15,6 @@ import {
PUBLIC_DIR_MIDDLEWARE_CONFLICT,
} from '../lib/constants'
import { fileExists } from '../lib/file-exists'
import { findPagesDir } from '../lib/find-pages-dir'
import loadCustomRoutes, {
CustomRoutes,
getRedirectStatus,
@@ -125,7 +124,7 @@ export default async function build(
const nextBuildSpan = trace('next-build')
return nextBuildSpan.traceAsyncFn(async () => {
// attempt to load global env values so they are available in next.config.js
// attempt to load global env values so they are available in blitz.config.js
const { loadedEnvFiles } = nextBuildSpan
.traceChild('load-dotenv')
.traceFn(() => loadEnvConfig(dir, false, Log))
@@ -162,7 +161,7 @@ export default async function build(
setGlobal('telemetry', telemetry)
const publicDir = path.join(dir, 'public')
const pagesDir = findPagesDir(dir)
const pagesDir = dir
const hasPublicDir = await fileExists(publicDir)
telemetry.record(

View File

@@ -18,7 +18,7 @@ import {
SERVER_PROPS_SSG_CONFLICT,
} from '../lib/constants'
import prettyBytes from '../lib/pretty-bytes'
import { recursiveReadDir } from '../lib/recursive-readdir'
import { recursiveFindPages } from '../lib/recursive-readdir'
import { getRouteMatcher, getRouteRegex } from '../next-server/lib/router/utils'
import { isDynamicRoute } from '../next-server/lib/router/utils/is-dynamic'
import escapePathDelimiters from '../next-server/lib/router/utils/escape-path-delimiters'
@@ -49,14 +49,35 @@ const fsStat = (file: string) => {
return (fileStats[file] = fileSize(file))
}
export const topLevelFoldersThatMayContainPages = [
'pages',
'src',
'app',
'integrations',
]
export function convertPageFilePathToRoutePath(filePath: string) {
return filePath
.replace(/^.*?[\\/]pages[\\/]/, '/')
.replace(/^.*?[\\/]api[\\/]/, '/api/')
}
export function isPageFile(filePathFromAppRoot: string) {
return (
/[\\/]pages[\\/]/.test(filePathFromAppRoot) ||
/[\\/]api[\\/]/.test(filePathFromAppRoot)
)
}
export function buildPageExtensionRegex(pageExtensions: string[]) {
return new RegExp(`(?<!\\.test|\\.spec)\\.(?:${pageExtensions.join('|')})$`)
}
export function collectPages(
directory: string,
pageExtensions: string[]
): Promise<string[]> {
return recursiveReadDir(
directory,
new RegExp(`\\.(?:${pageExtensions.join('|')})$`)
)
return recursiveFindPages(directory, buildPageExtensionRegex(pageExtensions))
}
export interface PageInfo {

View File

@@ -52,20 +52,7 @@ import WebpackConformancePlugin, {
} from './webpack/plugins/webpack-conformance-plugin'
import { WellKnownErrorsPlugin } from './webpack/plugins/wellknown-errors-plugin'
import { regexLikeCss } from './webpack/config/blocks/css'
import fs from 'fs'
import { getProjectRoot } from '../server/lib/utils'
/* ------ Blitz.js ------- */
function doesDbModuleExist() {
const projectRoot = getProjectRoot()
return (
fs.existsSync(path.join(projectRoot, 'db/index.js')) ||
fs.existsSync(path.join(projectRoot, 'db/index.ts')) ||
fs.existsSync(path.join(projectRoot, 'db/index.tsx'))
)
}
/* ------ Blitz.js ------- */
import { existsSync } from 'fs'
type ExcludesFalse = <T>(x: T | false) => x is T
@@ -279,6 +266,12 @@ export default async function getBaseWebpackConfig(
const distDir = path.join(dir, config.distDir)
/* ------ Blitz.js ------- */
const hasDbModule =
existsSync(path.join(dir, 'db/index.js')) ||
existsSync(path.join(dir, 'db/index.ts'))
/* ------ Blitz.js ------- */
// Webpack 5 can use the faster babel loader, webpack 5 has built-in caching for loaders
// For webpack 4 the old loader is used as it has external caching
const babelLoader = isWebpack5
@@ -913,9 +906,7 @@ export default async function getBaseWebpackConfig(
entry: async () => {
return {
...(clientEntries ? clientEntries : {}),
...(isServer && doesDbModuleExist()
? { 'blitz-db': './db/index' }
: {}),
...(isServer && hasDbModule ? { 'blitz-db': './db/index' } : {}),
...entrypoints,
}
},
@@ -1373,12 +1364,13 @@ export default async function getBaseWebpackConfig(
type: 'filesystem',
// Includes:
// - Next.js version
// - next.config.js keys that affect compilation
// - blitz.config.js keys that affect compilation
version: `${process.env.__NEXT_VERSION}|${configVars}`,
cacheDirectory: path.join(distDir, 'cache', 'webpack'),
}
// Adds `next.config.js` as a buildDependency when custom webpack config is provided
// TODO - can we remove this?
// Adds `blitz.config.js` as a buildDependency when custom webpack config is provided
if (config.webpack && config.configFile) {
cache.buildDependencies = {
config: [config.configFile],

View File

@@ -1,6 +1,7 @@
import { join } from 'path'
import * as Log from '../../output/log'
import babelLoader from './babel-loader/src/index'
import { isPageFile as isPageFileFn } from '../../utils'
// increment 'p' to invalidate cache
// eslint-disable-next-line no-useless-concat
@@ -81,7 +82,7 @@ const customBabelLoader = babelLoader((babel) => {
) {
const filename = this.resourcePath
const options = Object.assign({}, cfg.options)
const isPageFile = filename.startsWith(pagesDir)
const isPageFile = isPageFileFn(filename.replace(pagesDir, ''))
if (cfg.hasFilesystemConfig()) {
for (const file of [cfg.babelrc, cfg.config]) {

View File

@@ -7,7 +7,7 @@ export function removePathTrailingSlash(path: string): string {
/**
* Normalizes the trailing slash of a path according to the `trailingSlash` option
* in `next.config.js`.
* in `blitz.config.js`.
*/
export const normalizePathTrailingSlash = process.env.__NEXT_TRAILING_SLASH
? (path: string): string => {

View File

@@ -20,7 +20,6 @@ import {
BUILD_ID_FILE,
CLIENT_PUBLIC_FILES_PATH,
CLIENT_STATIC_FILES_PATH,
CONFIG_FILE,
EXPORT_DETAIL,
EXPORT_MARKER,
PAGES_MANIFEST,
@@ -143,7 +142,7 @@ export default async function exportApp(
return nextExportSpan.traceAsyncFn(async () => {
dir = resolve(dir)
// attempt to load global env values so they are available in next.config.js
// attempt to load global env values so they are available in blitz.config.js
nextExportSpan
.traceChild('load-dotenv')
.traceFn(() => loadEnvConfig(dir, false, Log))
@@ -308,7 +307,7 @@ export default async function exportApp(
if (typeof nextConfig.exportPathMap !== 'function') {
if (!options.silent) {
Log.info(
`No "exportPathMap" found in "${CONFIG_FILE}". Generating map from "./pages"`
`No "exportPathMap" found in "blitz.config.js". Generating map from "./pages"`
)
}
nextConfig.exportPathMap = async (defaultMap: ExportPathMap) => {

View File

@@ -85,7 +85,7 @@ async function lint(
}
}
const pagesDir = findPagesDir(baseDir)
const pagesDir = baseDir
if (nextEslintPluginIsEnabled) {
let updatedPagesDir = false

View File

@@ -11,7 +11,9 @@ export const existsSync = (f: string): boolean => {
}
export function findPagesDir(dir: string): string {
throw new Error('findPagesDir is deprecated in Blitz.js')
// prioritize ./pages over ./src/pages
// eslint-disable-next-line no-unreachable -- disabled in Blitz.js
let curDir = path.join(dir, 'pages')
if (existsSync(curDir)) return curDir

View File

@@ -1,5 +1,6 @@
import { promises } from 'fs'
import { join } from 'path'
import { isPageFile, topLevelFoldersThatMayContainPages } from '../build/utils'
/**
* Recursively read directory
@@ -40,3 +41,45 @@ export async function recursiveReadDir(
return arr.sort()
}
export async function recursiveFindPages(
dir: string,
filter: RegExp,
ignore?: RegExp,
arr: string[] = [],
rootDir: string = dir
): Promise<string[]> {
let folders = await promises.readdir(dir)
if (dir === rootDir) {
folders = folders.filter((folder) =>
topLevelFoldersThatMayContainPages.includes(folder)
)
}
await Promise.all(
folders.map(async (part: string) => {
const absolutePath = join(dir, part)
if (ignore && ignore.test(part)) return
const pathStat = await promises.stat(absolutePath)
if (pathStat.isDirectory()) {
await recursiveFindPages(absolutePath, filter, ignore, arr, rootDir)
return
}
if (!filter.test(part)) {
return
}
const relativeFromRoot = absolutePath.replace(rootDir, '')
if (isPageFile(relativeFromRoot)) {
arr.push(relativeFromRoot)
return
}
})
)
return arr.sort()
}

View File

@@ -15,7 +15,7 @@ export const REACT_LOADABLE_MANIFEST = 'react-loadable-manifest.json'
export const FONT_MANIFEST = 'font-manifest.json'
export const SERVER_DIRECTORY = 'server'
export const SERVERLESS_DIRECTORY = 'serverless'
export const CONFIG_FILE = 'next.config.js'
export const CONFIG_FILE = '.blitz.config.compiled.js'
export const BUILD_ID_FILE = 'BUILD_ID'
export const BLOCKED_PAGES = ['/_document', '/_app']
export const CLIENT_PUBLIC_FILES_PATH = 'public'

View File

@@ -1,6 +1,13 @@
import { existsSync, readFileSync } from 'fs'
import { build as esbuild } from 'esbuild'
import findUp from 'next/dist/compiled/find-up'
import os from 'os'
import { join } from 'path'
import { Header, Redirect, Rewrite } from '../../lib/load-custom-routes'
import { imageConfigDefault } from './image-config'
import { CONFIG_FILE } from '../lib/constants'
import { copy, remove } from 'fs-extra'
const debug = require('debug')('blitz:config')
export type DomainLocales = Array<{
http?: true
@@ -143,3 +150,116 @@ export function normalizeConfig(phase: string, config: any) {
}
return config
}
export async function getConfigSrcPath(dir: string | null) {
if (!dir) return null
let tsPath = join(dir, 'blitz.config.ts')
let jsPath = join(dir, 'blitz.config.js')
let legacyPath = join(dir, 'next.config.js')
if (existsSync(tsPath)) {
return tsPath
} else if (existsSync(jsPath)) {
return jsPath
} else if (existsSync(legacyPath)) {
const isInternalDevelopment = __dirname.includes(
'packages/next/dist/next-server'
)
if (isInternalDevelopment || process.env.VERCEL_BUILDER) {
// We read from next.config.js that Vercel automatically adds
debug(
'Using next.config.js because isInternalDevelopment or VERCEL_BUILDER...'
)
return legacyPath
} else {
console.log('') // newline
throw new Error(
'Blitz does not support next.config.js. Please rename it to blitz.config.js'
)
}
}
if (process.env.__NEXT_TEST_MODE) {
let tsPath2 = join(dir, '..', 'blitz.config.ts')
let jsPath2 = join(dir, '..', 'blitz.config.js')
let legacyPath2 = join(dir, '..', 'next.config.js')
if (existsSync(tsPath2)) {
return tsPath2
} else if (existsSync(jsPath2)) {
return jsPath2
} else if (existsSync(legacyPath2)) {
return legacyPath2
}
}
return null
}
export function getCompiledConfigPath(dir: string) {
return join(dir, CONFIG_FILE)
}
export async function compileConfig(dir: string | null) {
debug('Starting compileConfig...')
if (!dir) {
debug('compileConfig given empty dir argument')
return
}
const srcPath = await getConfigSrcPath(dir)
debug('srcPath:', srcPath)
const compiledPath = getCompiledConfigPath(dir)
debug('compiledPath:', compiledPath)
// Remove compiled file. This is important for example when user
// had a config file but then removed it
remove(compiledPath)
if (!srcPath) {
debug('Did not find a config file')
return
}
if (readFileSync(srcPath, 'utf8').includes('tsconfig-paths/register')) {
// User is manually handling their own typescript stuff
debug(
"Config contains 'tsconfig-paths/register', so skipping build and just copying the file"
)
await copy(srcPath, compiledPath)
return
}
const pkgJsonPath = await findUp('package.json', { cwd: dir })
if (!pkgJsonPath) {
// This will happen when running blitz no inside a blitz app
debug('Unable to find package directory')
return
}
debug('Building config...')
const pkg = require(pkgJsonPath)
await esbuild({
entryPoints: [srcPath],
outfile: compiledPath,
format: 'cjs',
bundle: true,
platform: 'node',
external: [
'*.json',
'@blitzjs',
'@next',
'@zeit',
'blitz',
'next',
'webpack',
...Object.keys(require('blitz/package').dependencies),
...Object.keys(pkg?.dependencies ?? {}),
...Object.keys(pkg?.devDependencies ?? {}),
],
})
debug('Config built.')
}

View File

@@ -48,7 +48,7 @@ export async function shouldLoadWithWebpack5(
}
}
// Use webpack 5 by default in apps that do not have next.config.js
// Use webpack 5 by default in apps that do not have blitz.config.js
if (!path?.length) {
return {
enabled: true,

View File

@@ -1,14 +1,15 @@
import chalk from 'chalk'
import findUp from 'next/dist/compiled/find-up'
import { basename, extname } from 'path'
import { basename, extname, join } from 'path'
import * as Log from '../../build/output/log'
import { hasNextSupport } from '../../telemetry/ci-info'
import { CONFIG_FILE, PHASE_DEVELOPMENT_SERVER } from '../lib/constants'
import { execOnce } from '../lib/utils'
import { defaultConfig, normalizeConfig } from './config-shared'
import { compileConfig, defaultConfig, normalizeConfig } from './config-shared'
import { loadWebpackHook } from './config-utils'
import { ImageConfig, imageConfigDefault, VALID_LOADERS } from './image-config'
import { loadEnvConfig } from '@next/env'
const debug = require('debug')('blitz:config')
export { DomainLocales, NextConfig, normalizeConfig } from './config-shared'
@@ -412,6 +413,10 @@ export default async function loadConfig(
customConfig?: object | null
) {
await loadEnvConfig(dir, phase === PHASE_DEVELOPMENT_SERVER, Log)
if (!['start', 's'].includes(process.argv[2])) {
// Do not compile config for blitz start because it was already compiled during blitz build
await compileConfig(dir)
}
await loadWebpackHook(phase, dir)
if (customConfig) {
@@ -423,11 +428,20 @@ export default async function loadConfig(
// If config file was found
if (path?.length) {
const userConfigModule = require(path)
const userConfig = normalizeConfig(
let userConfig = normalizeConfig(
phase,
userConfigModule.default || userConfigModule
)
if (process.env.VERCEL_BUILDER) {
debug("Loading Vercel's next.config.js...")
const nextConfig = require(join('dir', 'next.config.js'))
debug("Vercel's next.config.js contents:", nextConfig)
for (const [key, value] of Object.entries(nextConfig)) {
userConfig[key] = value
}
}
if (Object.keys(userConfig).length === 0) {
Log.warn(
'Detected blitz.config.js, no exported configuration found. https://nextjs.org/docs/messages/empty-configuration'
@@ -461,21 +475,23 @@ export default async function loadConfig(
...userConfig,
})
} else {
const configBaseName = basename(CONFIG_FILE, extname(CONFIG_FILE))
const nonJsPath = findUp.sync(
const unsupportedPath = findUp.sync(
[
`${configBaseName}.jsx`,
`${configBaseName}.ts`,
`${configBaseName}.tsx`,
`${configBaseName}.json`,
`blitz.config.jsx`,
`blitz.config.tsx`,
`blitz.config.json`,
`next.config.jsx`,
`next.config.ts`,
`next.config.tsx`,
`next.config.json`,
],
{ cwd: dir }
)
if (nonJsPath?.length) {
if (unsupportedPath?.length) {
throw new Error(
`Configuring Blitz.js via '${basename(
nonJsPath
)}' is not supported. Please replace the file with 'blitz.config.js'.`
unsupportedPath
)}' is not supported. Please replace the file with 'blitz.config.(js|ts)'`
)
}
}

View File

@@ -122,7 +122,7 @@ export type ServerConstructor = {
*/
quiet?: boolean
/**
* Object what you would use in next.config.js - @default {}
* Object what you would use in blitz.config.js - @default {}
*/
conf?: NextConfig | null
dev?: boolean
@@ -606,7 +606,7 @@ export default class Server {
let rewrites: CustomRoutes['rewrites']
// rewrites can be stored as an array when an array is
// returned in next.config.js so massage them into
// returned in blitz.config.js so massage them into
// the expected object format
if (Array.isArray(customRoutes.rewrites)) {
rewrites = {

View File

@@ -1,6 +1,6 @@
{
"name": "next",
"version": "0.38.2",
"version": "0.38.3-canary.1",
"nextjsVersion": "11.0.1",
"description": "The React Framework",
"main": "./dist/server/next.js",
@@ -83,9 +83,12 @@
"constants-browserify": "1.0.0",
"crypto-browserify": "3.12.0",
"cssnano-simple": "2.0.0",
"debug": "4.3.1",
"domain-browser": "4.19.0",
"encoding": "0.1.13",
"esbuild": "^0.11.12",
"etag": "1.8.1",
"fs-extra": "^9.1.0",
"get-orientation": "1.1.2",
"https-browserify": "1.0.0",
"image-size": "1.0.0",
@@ -110,6 +113,7 @@
"stream-http": "3.1.1",
"string_decoder": "1.3.0",
"styled-jsx": "3.3.2",
"superjson": "1.7.2",
"timers-browserify": "2.0.12",
"tty-browserify": "0.0.1",
"use-subscription": "1.5.1",
@@ -176,8 +180,8 @@
"@types/lru-cache": "5.1.0",
"@types/node-fetch": "2.5.8",
"@types/path-to-regexp": "1.7.0",
"@types/react": "17.0.2",
"@types/react-dom": "16.9.4",
"@types/react": "17.0.13",
"@types/react-dom": "17.0.8",
"@types/react-is": "16.7.1",
"@types/semver": "7.3.1",
"@types/send": "0.14.4",
@@ -202,7 +206,6 @@
"content-type": "1.0.4",
"cookie": "0.4.1",
"css-loader": "4.3.0",
"debug": "4.3.1",
"devalue": "2.0.1",
"escape-string-regexp": "2.0.0",
"file-loader": "6.0.0",

View File

@@ -166,7 +166,7 @@ export default class HotReloader {
this.buildId = buildId
this.dir = dir
this.middlewares = []
this.pagesDir = pagesDir
this.pagesDir = dir
this.webpackHotMiddleware = null
this.stats = null
this.serverStats = null

View File

@@ -3,7 +3,9 @@ import chalk from 'chalk'
import { warn } from '../../build/output/log'
import { promises } from 'fs'
import { denormalizePagePath } from '../../next-server/server/normalize-page-path'
import { fileExists } from '../../lib/file-exists'
// import { fileExists } from '../../lib/file-exists'
import { recursiveFindPages } from '../../lib/recursive-readdir'
import { buildPageExtensionRegex } from '../../build/utils'
async function isTrueCasePagePath(pagePath: string, pagesDir: string) {
const pageSegments = normalize(pagePath).split(pathSeparator).filter(Boolean)
@@ -22,27 +24,60 @@ export async function findPageFile(
normalizedPagePath: string,
pageExtensions: string[]
): Promise<string | null> {
const foundPagePaths: string[] = []
// console.log('[findPageFile]', { rootDir, normalizedPagePath })
const page = denormalizePagePath(normalizedPagePath)
for (const extension of pageExtensions) {
if (!normalizedPagePath.endsWith('/index')) {
const relativePagePath = `${page}.${extension}`
const pagePath = join(rootDir, relativePagePath)
const allPages = await recursiveFindPages(
rootDir,
buildPageExtensionRegex(pageExtensions)
)
// console.log('allPages', allPages)
if (await fileExists(pagePath)) {
foundPagePaths.push(relativePagePath)
}
}
const relativePagePathWithIndex = join(page, `index.${extension}`)
const pagePathWithIndex = join(rootDir, relativePagePathWithIndex)
if (await fileExists(pagePathWithIndex)) {
foundPagePaths.push(relativePagePathWithIndex)
}
let prefix: string
if (normalizedPagePath.startsWith('/api/')) {
prefix = ''
} else {
prefix = '/pages'
}
let nameMatch: string
if (page === '/') {
nameMatch = normalizedPagePath
} else if (page.endsWith('/index')) {
nameMatch = `${page}/index`
} else {
nameMatch = `(${page}|${page}/index)`
}
nameMatch = nameMatch.replace(/[[\]\\]/g, '\\$&')
const foundPagePaths = allPages.filter((path) =>
path.match(
new RegExp(`${prefix}${nameMatch}\\.(?:${pageExtensions.join('|')})$`)
)
)
// console.log(
// new RegExp(`${prefix}${nameMatch}\\.(?:${pageExtensions.join('|')})$`)
// )
// console.log('FOUND', foundPagePaths)
// for (const extension of pageExtensions) {
// if (!normalizedPagePath.endsWith('/index')) {
// const relativePagePath = `${page}.${extension}`
// const pagePath = join(rootDir, relativePagePath)
//
// if (await fileExists(pagePath)) {
// foundPagePaths.push(relativePagePath)
// }
// }
//
// const relativePagePathWithIndex = join(page, `index.${extension}`)
// const pagePathWithIndex = join(rootDir, relativePagePathWithIndex)
// if (await fileExists(pagePathWithIndex)) {
// foundPagePaths.push(relativePagePathWithIndex)
// }
// }
if (foundPagePaths.length < 1) {
return null
}

View File

@@ -1,5 +1,5 @@
import fs from 'fs'
import path from 'path'
import findUp from 'next/dist/compiled/find-up'
import { dirname } from 'path'
export function printAndExit(message: string, code = 1) {
if (code === 0) {
@@ -16,16 +16,14 @@ export function getNodeOptionsWithoutInspect() {
return (process.env.NODE_OPTIONS || '').replace(NODE_INSPECT_RE, '')
}
export function getProjectRoot() {
return path.dirname(getConfigSrcPath())
}
export async function getProjectRoot(dir: string) {
const pkgJsonPath = await findUp('package.json', { cwd: dir })
export function getConfigSrcPath() {
const tsPath = path.resolve(path.join(process.cwd(), 'blitz.config.ts'))
if (fs.existsSync(tsPath)) {
return tsPath
} else {
const jsPath = path.resolve(path.join(process.cwd(), 'blitz.config.js'))
return jsPath
if (!pkgJsonPath) {
throw new Error(
'Unable to find project root by looking for your package.json'
)
}
return dirname(pkgJsonPath)
}

View File

@@ -12,7 +12,6 @@ import { ampValidation } from '../build/output/index'
import * as Log from '../build/output/log'
import { PUBLIC_DIR_MIDDLEWARE_CONFLICT } from '../lib/constants'
import { fileExists } from '../lib/file-exists'
import { findPagesDir } from '../lib/find-pages-dir'
import loadCustomRoutes, { CustomRoutes } from '../lib/load-custom-routes'
import { verifyTypeScriptSetup } from '../lib/verifyTypeScriptSetup'
import {
@@ -39,6 +38,12 @@ import { findPageFile } from './lib/find-page-file'
import { getNodeOptionsWithoutInspect } from './lib/utils'
import { withCoalescedInvoke } from '../lib/coalesced-function'
import { NextConfig } from '../next-server/server/config'
import {
buildPageExtensionRegex,
convertPageFilePathToRoutePath,
isPageFile,
topLevelFoldersThatMayContainPages,
} from '../build/utils'
if (typeof React.Suspense === 'undefined') {
throw new Error(
@@ -107,7 +112,7 @@ export default class DevServer extends Server {
)
}
this.isCustomServer = !options.isNextDevCommand
this.pagesDir = findPagesDir(this.dir)
this.pagesDir = this.dir
this.staticPathsWorker = new Worker(
require.resolve('./static-paths-worker'),
{
@@ -187,8 +192,8 @@ export default class DevServer extends Server {
return
}
const regexPageExtension = new RegExp(
`\\.+(?:${this.nextConfig.pageExtensions.join('|')})$`
const regexPageExtension = buildPageExtensionRegex(
this.nextConfig.pageExtensions
)
let resolved = false
@@ -208,18 +213,31 @@ export default class DevServer extends Server {
})
let wp = (this.webpackWatcher = new Watchpack())
wp.watch([], [pagesDir!], 0)
wp.watch(
[],
topLevelFoldersThatMayContainPages.map((dir) =>
pathJoin(pagesDir!, dir)
),
0
)
wp.on('aggregated', () => {
const routedPages = []
const knownFiles = wp.getTimeInfoEntries()
for (const [fileName, { accuracy }] of knownFiles) {
if (accuracy === undefined || !regexPageExtension.test(fileName)) {
for (const [filePath, { accuracy }] of knownFiles) {
const relativePath = '/' + relative(pagesDir!, filePath)
if (
accuracy === undefined ||
!isPageFile(relativePath) ||
!regexPageExtension.test(filePath)
) {
continue
}
let pageName =
'/' + relative(pagesDir!, fileName).replace(/\\+/g, '/')
let pageName = relativePath.replace(/\\+/g, '/')
pageName = convertPageFilePathToRoutePath(pageName)
pageName = pageName.replace(regexPageExtension, '')
pageName = pageName.replace(/\/index$/, '') || '/'

View File

@@ -11,6 +11,7 @@ import {
import { pageNotFoundError } from '../next-server/server/require'
import { findPageFile } from './lib/find-page-file'
import getRouteFromEntrypoint from '../next-server/server/get-route-from-entrypoint'
import { convertPageFilePathToRoutePath } from '../build/utils'
export const ADDED = Symbol('added')
export const BUILDING = Symbol('building')
@@ -158,7 +159,7 @@ export default function onDemandEntryHandler(
throw pageNotFoundError(normalizedPagePath)
}
let pageUrl = pagePath.replace(/\\/g, '/')
let pageUrl = convertPageFilePathToRoutePath(pagePath.replace(/\\/g, '/'))
pageUrl = `${pageUrl[0] !== '/' ? '/' : ''}${pageUrl
.replace(new RegExp(`\\.+(?:${pageExtensions.join('|')})$`), '')

View File

@@ -123,8 +123,8 @@ describe('Build Output', () => {
)
expect(indexSize.endsWith('B')).toBe(true)
const blitzExtra = 38
const blitzExtraGz = 10.2
const blitzExtra = 32
const blitzExtraGz = 8.7
expect(parseFloat(indexFirstLoad)).toBeCloseTo(
gz ? 64 + blitzExtraGz : 196 + blitzExtra,
0
@@ -168,8 +168,8 @@ describe('Build Output', () => {
)
expect(mainSize.endsWith('kB')).toBe(true)
const blitzFrameworkExtra = 13
const blitzFrameworkExtraGz = 3.2
const blitzFrameworkExtra = 7
const blitzFrameworkExtraGz = 1.6
expect(parseFloat(frameworkSize)).toBeCloseTo(
gz ? 42.0 + blitzFrameworkExtraGz : 130 + blitzFrameworkExtra,
0

View File

@@ -74,6 +74,7 @@ describe('Client Navigation accessibility', () => {
const title = await browser.eval('document.title')
await waitFor(500)
const routeAnnouncerValue = await browser
.waitForElementByCss('#__next-route-announcer__')
.text()
@@ -92,6 +93,7 @@ describe('Client Navigation accessibility', () => {
const pathname = '/page-without-h1-or-title'
await waitFor(500)
const routeAnnouncerValue = await browser
.waitForElementByCss('#__next-route-announcer__')
.text()

View File

@@ -1,7 +1,7 @@
[
{
"url": "https://fonts.googleapis.com/css?family=Voces",
"content": "@font-face{font-family:'Voces';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.com/s/voces/v12/-F6_fjJyLyU8d7PGDmk.woff) format('woff')}@font-face{font-family:'Voces';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.com/s/voces/v12/-F6_fjJyLyU8d7PIDm_6pClI_ik.woff2) format('woff2');unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:'Voces';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.com/s/voces/v12/-F6_fjJyLyU8d7PGDm_6pClI.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}"
"content": "@font-face{font-family:'Voces';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.com/s/voces/v15/-F6_fjJyLyU8d7PGDmk.woff) format('woff')}@font-face{font-family:'Voces';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.com/s/voces/v15/-F6_fjJyLyU8d7PIDm_6pClI_ik.woff2) format('woff2');unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:'Voces';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.com/s/voces/v15/-F6_fjJyLyU8d7PGDm_6pClI.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}"
},
{
"url": "https://fonts.googleapis.com/css2?family=Modak",

View File

@@ -1,6 +0,0 @@
module.exports = {
onDemandEntries: {
// Make sure entries are not getting disposed.
maxInactiveAge: 1000 * 60 * 60,
},
}

View File

@@ -1,3 +0,0 @@
export default function Abc() {
return <div />
}

View File

@@ -1,17 +0,0 @@
/* eslint-env jest */
import { nextBuild } from 'next-test-utils'
import { join } from 'path'
jest.setTimeout(1000 * 60 * 2)
const appDir = join(__dirname, '..')
describe('Empty JSConfig Support', () => {
test('should compile successfully', async () => {
const { code, stdout } = await nextBuild(appDir, [], {
stdout: true,
})
expect(code).toBe(0)
expect(stdout).toMatch(/Compiled successfully/)
})
})

View File

@@ -0,0 +1,6 @@
/* eslint-disable-next-line */
import fs from 'fs'
export default (_req, res) => {
res.send('ok')
}

View File

@@ -0,0 +1,6 @@
/* eslint-disable-next-line */
import fs from 'fs'
export default (_req, res) => {
res.send('ok')
}

View File

@@ -0,0 +1,2 @@
const Page = () => throw new Error('should not be processed as a page')
export default Page

View File

@@ -0,0 +1,9 @@
import React from 'react'
export default class App extends React.Component {
render() {
const { Component, pageProps } = this.props
return <Component {...pageProps} />
}
}

View File

@@ -0,0 +1,2 @@
const Page = () => <div id="page-container">Home page</div>
export default Page

View File

@@ -0,0 +1,2 @@
const Page = () => throw new Error('should not be processed as a page')
export default Page

View File

@@ -0,0 +1,2 @@
const Page = () => <div id="page-container">Some page</div>
export default Page

View File

@@ -0,0 +1,2 @@
const Page = () => <div id="page-container">Some page</div>
export default Page

View File

@@ -0,0 +1,6 @@
/* eslint-disable-next-line */
import fs from 'fs'
export default (_req, res) => {
res.send('ok')
}

View File

@@ -0,0 +1,6 @@
/* eslint-disable-next-line */
import fs from 'fs'
export default (_req, res) => {
res.send('ok')
}

View File

@@ -0,0 +1,6 @@
/* eslint-disable-next-line */
import fs from 'fs'
export default (_req, res) => {
res.send('ok')
}

View File

@@ -0,0 +1,2 @@
const Page = () => <div id="page-container">Hello World</div>
export default Page

View File

@@ -0,0 +1,2 @@
const Page = () => <div id="page-container">Some page</div>
export default Page

View File

@@ -0,0 +1,2 @@
const Page = () => <div id="page-container">Some page</div>
export default Page

View File

@@ -0,0 +1,126 @@
import {
nextBuild,
findPort,
killApp,
nextStart,
launchApp,
renderViaHTTP,
} from 'next-test-utils'
import fs from 'fs-extra'
import webdriver from 'next-webdriver'
import { join } from 'path'
let app
let appPort
const appDir = join(__dirname, '..')
const nextConfig = join(appDir, 'next.config.js')
const specPage = join(appDir, 'app/pages/home.spec.js')
const testPage = join(appDir, 'app/pages/home.test.js')
const specApi = join(appDir, 'app/api/hook.spec.js')
const testApi = join(appDir, 'app/api/hook.test.js')
jest.setTimeout(1000 * 60 * 2)
beforeAll(async () => {
await fs.copy(specPage, testPage)
await fs.copy(specApi, testApi)
})
afterAll(async () => {
await fs.remove(testPage)
await fs.remove(testApi)
})
const runTests = (mode) => {
it('should load the pages', async () => {
const browser = await webdriver(appPort, '/')
let text = await browser.elementByCss('#page-container').text()
expect(text).toMatch('Hello World')
await browser.eval('window.location = "/home"')
text = await browser.elementByCss('#page-container').text()
expect(text).toMatch('Home page')
await browser.eval('window.location = "/pages/new"')
text = await browser.elementByCss('#page-container').text()
expect(text).toMatch('Some page')
await browser.eval('window.location = "/api/hello-api"')
text = await browser.elementByCss('pre').text()
expect(text).toMatch('ok')
await browser.eval('window.location = "/api/api-health"')
text = await browser.elementByCss('pre').text()
expect(text).toMatch('ok')
await browser.eval('window.location = "/api/auth/twitter"')
text = await browser.elementByCss('pre').text()
expect(text).toMatch('ok')
})
it('should not have test or spec pages', async () => {
let html = await renderViaHTTP(appPort, '/home.test')
expect(html).toContain('This page could not be found')
html = await renderViaHTTP(appPort, '/home.spec')
expect(html).toContain('This page could not be found')
})
if (mode !== 'dev') {
it('should build routes', async () => {
const pagesManifest = JSON.parse(
await fs.readFile(
join(appDir, `.next/${mode}/pages-manifest.json`),
'utf8'
)
)
const pages = Object.keys(pagesManifest)
expect(pages.includes('/home')).toBeTruthy()
expect(pages.includes('/pages/new')).toBeTruthy()
expect(pages.includes('/api/api-health')).toBeTruthy()
expect(pages.includes('/api/v1/api/launch-api')).toBeTruthy()
expect(
pages.includes('/api/v1/pages/nested-inside-api-pages')
).toBeTruthy()
expect(pages.includes('/home.test')).toBeFalsy()
expect(pages.includes('/home.spec')).toBeFalsy()
expect(pages.includes('/api/hook.spec')).toBeFalsy()
expect(pages.includes('/api/hook.test')).toBeFalsy()
})
}
}
describe('dev mode', () => {
beforeAll(async () => {
appPort = await findPort()
app = await launchApp(appDir, appPort)
})
afterAll(() => killApp(app))
runTests('dev')
})
describe('production mode', () => {
beforeAll(async () => {
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(() => killApp(app))
runTests('server')
})
describe('serverless mode', () => {
beforeAll(async () => {
await fs.writeFile(nextConfig, `module.exports = { target: 'serverless' }`)
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
})
afterAll(async () => {
await killApp(app)
await fs.remove(nextConfig)
})
runTests('serverless')
})

View File

@@ -17,13 +17,16 @@ describe('no anonymous default export warning', () => {
beforeEach(async () => {
await fs.remove(join(appDir, '.next'))
})
afterEach(async () => {
await fs.remove(join(appDir, '.next'))
})
it('show correct warnings for page', async () => {
let stderr = ''
const appPort = await findPort()
const app = await launchApp(appDir, appPort, {
env: { __NEXT_TEST_WITH_DEVTOOL: true },
env: { __NEXT_TEST_WITH_DEVTOOL: true, __NEXT_TEST_ANON_EXPORT: true },
onStderr(msg) {
stderr += msg || ''
},
@@ -50,7 +53,7 @@ describe('no anonymous default export warning', () => {
const appPort = await findPort()
const app = await launchApp(appDir, appPort, {
env: { __NEXT_TEST_WITH_DEVTOOL: true },
env: { __NEXT_TEST_WITH_DEVTOOL: true, __NEXT_TEST_ANON_EXPORT: true },
onStderr(msg) {
stderr += msg || ''
},
@@ -77,7 +80,7 @@ describe('no anonymous default export warning', () => {
const appPort = await findPort()
const app = await launchApp(appDir, appPort, {
env: { __NEXT_TEST_WITH_DEVTOOL: true },
env: { __NEXT_TEST_WITH_DEVTOOL: true, __NEXT_TEST_ANON_EXPORT: true },
onStderr(msg) {
stderr += msg || ''
},

View File

@@ -2,7 +2,13 @@
import { join } from 'path'
import webdriver from 'next-webdriver'
import { killApp, findPort, nextBuild, nextStart } from 'next-test-utils'
import {
killApp,
findPort,
nextBuild,
nextStart,
waitFor,
} from 'next-test-utils'
const appDir = join(__dirname, '../')
let appPort
@@ -59,6 +65,7 @@ describe('Analytics relayer', () => {
expect(largestContentfulPaint).not.toBeNaN()
expect(largestContentfulPaint).toBeGreaterThan(0)
await waitFor(100)
const beacons = (await browser.eval('window.__BEACONS')).map(([, value]) =>
Object.fromEntries(new URLSearchParams(value))
)

View File

@@ -85,7 +85,7 @@ describe('Production response size', () => {
const delta = responseSizesBytes / 1024
// Expected difference: < 0.5
const blitzExtra = 36.08
const blitzExtra = 30.24
expect(delta).toBeCloseTo(286.8 + blitzExtra, 0)
})
})

View File

@@ -1 +1 @@
export default () => <h1>PAGES DIR</h1>
export default () => <h1>SRC</h1>

View File

@@ -23,9 +23,11 @@ function runTests(dev) {
expect(html).toMatch(/PAGES/)
})
it('should render not render from src/pages', async () => {
it('should render from src/pages', async () => {
const html = await renderViaHTTP(appPort, '/hello')
expect(html).toMatch(/404/)
expect(html).toMatch(/SRC/)
const html2 = await renderViaHTTP(appPort, '/world')
expect(html2).toMatch(/SRC/)
})
}

View File

@@ -72,7 +72,8 @@ describe('Telemetry CLI', () => {
expect(stdout).toMatch(/Status: Disabled/)
})
it('detects isSrcDir dir correctly for `next build`', async () => {
it.skip('detects isSrcDir dir correctly for `next build`', async () => {
// blitz
const { stderr } = await runNextCommand(['build', appDir], {
stderr: true,
env: {
@@ -132,11 +133,9 @@ describe('Telemetry CLI', () => {
const event1 = /NEXT_BUILD_COMPLETED[\s\S]+?{([\s\S]+?)}/.exec(stderr).pop()
expect(event1).toMatch(/hasDunderPages.*?true/)
expect(event1).toMatch(/hasTestPages.*?true/)
const event2 = /NEXT_BUILD_OPTIMIZED[\s\S]+?{([\s\S]+?)}/.exec(stderr).pop()
expect(event2).toMatch(/hasDunderPages.*?true/)
expect(event2).toMatch(/hasTestPages.*?true/)
})
it('detects correct cli session defaults', async () => {
@@ -337,7 +336,8 @@ describe('Telemetry CLI', () => {
expect(event1).toMatch(/"totalPageCount": 6/)
})
it('detects isSrcDir dir correctly for `next dev`', async () => {
it.skip('detects isSrcDir dir correctly for `next dev`', async () => {
// blitz
let port = await findPort()
let stderr = ''

View File

@@ -89,12 +89,10 @@ describe('config', () => {
PHASE_DEVELOPMENT_SERVER,
join(__dirname, '_resolvedata', 'typescript-config')
)
).rejects.toThrow(
/Configuring Blitz.js via .+ is not supported. Please replace the file with 'blitz.config.js'/
)
).rejects.toThrow(/Configuring Blitz.js via .* is not supported/)
})
it('Should not throw an error when two versions of next.config.js are present', async () => {
it('Should not throw an error when two versions of blitz.config.js are present', async () => {
const config = await loadConfig(
PHASE_DEVELOPMENT_SERVER,
join(__dirname, '_resolvedata', 'js-ts-config')

View File

@@ -1,4 +1,5 @@
/* eslint-env jest */
import { Console } from 'console'
process.env.BLITZ_TEST_ENVIRONMENT = true
@@ -7,3 +8,6 @@ if (process.env.JEST_RETRY_TIMES) {
console.log(`Configuring jest retries: ${retries}`)
jest.retryTimes(retries)
}
// Reset to default console instead of the verbose Jest one
global.console = new Console({ stdout: process.stdout, stderr: process.stderr })

View File

@@ -118,15 +118,13 @@ export function runNextCommand(argv, options = {}) {
}
let stderrOutput = ''
if (options.stderr) {
instance.stderr.on('data', function (chunk) {
stderrOutput += chunk
instance.stderr.on('data', function (chunk) {
stderrOutput += chunk
if (options.stderr === 'log') {
console.log(chunk.toString())
}
})
}
if (options.stderr === 'log') {
console.log(chunk.toString())
}
})
let stdoutOutput = ''
if (options.stdout) {
@@ -146,6 +144,7 @@ export function runNextCommand(argv, options = {}) {
!options.ignoreFail &&
code !== 0
) {
console.log(stderrOutput)
return reject(new Error(`command failed with code ${code}`))
}

View File

@@ -174,10 +174,10 @@ export default async function webdriver(
const url = `http://${deviceIP}:${appPort}${path}`
browser.initUrl = url
console.log(`\n> Loading browser with ${url}\n`)
console.log(`> Loading browser with ${url}`)
await browser.get(url)
console.log(`\n> Loaded browser with ${url}\n`)
console.log(`> Loaded browser with ${url}`)
// Wait for application to hydrate
if (waitHydration) {

View File

@@ -5,24 +5,24 @@ import { normalizePagePath } from 'next/dist/next-server/server/normalize-page-p
import { join } from 'path'
const resolveDataDir = join(__dirname, '..', 'isolated', '_resolvedata')
const dirWithPages = join(resolveDataDir, 'readdir', 'pages')
const dirWithPages = join(resolveDataDir, 'readdir')
describe('findPageFile', () => {
it('should work', async () => {
const pagePath = normalizePagePath('/nav/about')
const result = await findPageFile(dirWithPages, pagePath, ['jsx', 'js'])
expect(result).toMatch(/^[\\/]nav[\\/]about\.js/)
expect(result).toMatch(/^[\\/]pages[\\/]nav[\\/]about\.js/)
})
it('should work with nested index.js', async () => {
const pagePath = normalizePagePath('/nested')
const result = await findPageFile(dirWithPages, pagePath, ['jsx', 'js'])
expect(result).toMatch(/^[\\/]nested[\\/]index\.js/)
expect(result).toMatch(/^[\\/]pages[\\/]nested[\\/]index\.js/)
})
it('should prefer prefered.js before preferred/index.js', async () => {
const pagePath = normalizePagePath('/prefered')
const result = await findPageFile(dirWithPages, pagePath, ['jsx', 'js'])
expect(result).toMatch(/^[\\/]prefered\.js/)
expect(result).toMatch(/^[\\/]pages[\\/]prefered\.js/)
})
})

View File

@@ -14,7 +14,7 @@ describe('Link rendering', () => {
)
const html = ReactDOM.renderToString(element)
expect(html).toMatchInlineSnapshot(
`"<a href=\\"/my-path\\" data-reactroot=\\"\\">to another page</a>"`
`"<a href=\\"/my-path\\">to another page</a>"`
)
})
})

View File

@@ -51,9 +51,7 @@ const babel = async (code, queryOpts = {}) => {
isServer,
distDir: path.resolve(dir, '.next'),
pagesDir:
'pagesDir' in queryOpts
? queryOpts.pagesDir
: path.resolve(dir, 'pages'),
'pagesDir' in queryOpts ? queryOpts.pagesDir : path.resolve(dir),
cache: false,
development,
hasReactRefresh: Boolean(!isServer && development),

View File

@@ -31,7 +31,7 @@
"dev:nextjs-types": "yarn wait:nextjs && yarn workspace next types && echo 'Finished building nextjs types'",
"dev:blitz": "cross-env BLITZ_PROD_BUILD=true preconstruct watch",
"dev:tsc": "yarn dev:nextjs-types && tsc --watch --pretty --preserveWatchOutput",
"dev:cli": "yarn wait:nextjs && yarn workspace @blitzjs/cli dev",
"dev:cli": "yarn wait:nextjs-types && yarn workspace @blitzjs/cli dev",
"dev:templates": "yarn workspace @blitzjs/generator dev",
"dev": "concurrently --names \"nextjs,blitz,typecheck,cli,templates\" -c \"magenta,cyan,green,yellow,black\" -p \"{name}\" \"npm:dev:nextjs\" \"npm:dev:blitz\" \"npm:dev:tsc\" \"npm:dev:cli\" \"npm:dev:templates\"",
"build:nextjs": "yarn workspace next prepublish",
@@ -124,8 +124,8 @@
"@types/progress": "^2.0.3",
"@types/pump": "1.1.0",
"@types/pumpify": "1.4.1",
"@types/react": "17.0.2",
"@types/react-dom": "16.9.4",
"@types/react": "17.0.13",
"@types/react-dom": "17.0.8",
"@types/readable-stream": "2.3.9",
"@types/rimraf": "3.0.0",
"@types/tar": "4.0.4",
@@ -178,6 +178,8 @@
"prettier": "2.2.1",
"prettier-plugin-prisma": "0.4.0",
"prompt": "1.1.0",
"react": "18.0.0-alpha-ed6c091fe-20210701",
"react-dom": "18.0.0-alpha-ed6c091fe-20210701",
"react-test-renderer": "17.0.1",
"release": "6.3.0",
"rimraf": "^3.0.2",

View File

@@ -1,6 +1,6 @@
{
"name": "@blitzjs/babel-preset",
"version": "0.38.2",
"version": "0.38.3-canary.1",
"license": "MIT",
"scripts": {
"test": "jest",

View File

@@ -85,17 +85,21 @@ const pagesToSkip = ([] as string[]).concat(
);
function isPage(filePath: string) {
if (!filePath.includes('pages' + nodePath.sep)) {
if (!filePath.includes(nodePath.sep + 'pages' + nodePath.sep)) {
return false;
}
if (filePath.includes('pages' + nodePath.sep + 'api')) {
if (
filePath.includes(
nodePath.sep + 'pages' + nodePath.sep + 'api' + nodePath.sep
)
) {
return false;
}
return !pagesToSkip.some((fileToSkip) => filePath.includes(fileToSkip));
}
function isApiRoute(filePath: string) {
if (filePath.includes('pages' + nodePath.sep + 'api')) {
if (filePath.includes(nodePath.sep + 'api' + nodePath.sep)) {
return true;
}
return false;

View File

@@ -1,7 +1,7 @@
{
"name": "blitz",
"description": "Blitz is a Rails-like framework for monolithic, full-stack React apps — built on Next.js",
"version": "0.38.2",
"version": "0.38.3-canary.1",
"license": "MIT",
"scripts": {
"test": "jest",
@@ -51,13 +51,13 @@
"url": "https://github.com/blitz-js/blitz"
},
"dependencies": {
"@blitzjs/babel-preset": "0.38.2",
"@blitzjs/cli": "0.38.2",
"@blitzjs/config": "0.38.2",
"@blitzjs/core": "0.38.2",
"@blitzjs/display": "0.38.2",
"@blitzjs/generator": "0.38.2",
"@blitzjs/server": "0.38.2",
"@blitzjs/babel-preset": "0.38.3-canary.1",
"@blitzjs/cli": "0.38.3-canary.1",
"@blitzjs/config": "0.38.3-canary.1",
"@blitzjs/core": "0.38.3-canary.1",
"@blitzjs/display": "0.38.3-canary.1",
"@blitzjs/generator": "0.38.3-canary.1",
"@blitzjs/server": "0.38.3-canary.1",
"@testing-library/jest-dom": "5.11.9",
"@testing-library/react": "11.2.5",
"@testing-library/react-hooks": "^4.0.1",
@@ -66,7 +66,7 @@
"cross-spawn": "7.0.3",
"debug": "4.3.1",
"envinfo": "^7.7.3",
"eslint-config-blitz": "0.38.2",
"eslint-config-blitz": "0.38.3-canary.1",
"jest": "^26.6.3",
"jest-watch-typeahead": "^0.6.1",
"minimist": "1.2.5",

View File

@@ -1,7 +1,7 @@
{
"name": "@blitzjs/cli",
"description": "Blitz.js CLI",
"version": "0.38.2",
"version": "0.38.3-canary.1",
"license": "MIT",
"scripts": {
"dev": "rimraf lib && tsc --watch --pretty --preserveWatchOutput",
@@ -22,8 +22,8 @@
"/lib"
],
"dependencies": {
"@blitzjs/display": "0.38.2",
"@blitzjs/repl": "0.38.2",
"@blitzjs/display": "0.38.3-canary.1",
"@blitzjs/repl": "0.38.3-canary.1",
"@oclif/command": "1.8.0",
"@oclif/config": "1.17.0",
"@oclif/plugin-autocomplete": "0.3.0",
@@ -56,9 +56,9 @@
"v8-compile-cache": "2.2.0"
},
"devDependencies": {
"@blitzjs/generator": "0.38.2",
"@blitzjs/installer": "0.38.2",
"@blitzjs/server": "0.38.2",
"@blitzjs/generator": "0.38.3-canary.1",
"@blitzjs/installer": "0.38.3-canary.1",
"@blitzjs/server": "0.38.3-canary.1",
"@oclif/dev-cli": "1.26.0",
"@oclif/test": "1.2.8",
"nock": "13.0.6",

View File

@@ -2,8 +2,9 @@ require("v8-compile-cache")
const cacheFile = require("path").join(__dirname, ".blitzjs-cli-cache")
const lazyLoad = require("@salesforce/lazy-require").default.create(cacheFile)
lazyLoad.start()
import {buildConfig} from "@blitzjs/config"
import {getProjectRoot} from "@blitzjs/config"
import {run as oclifRun} from "@oclif/command"
import {compileConfig} from "next/dist/next-server/server/config-shared"
// Load the .env environment variable so it's available for all commands
require("dotenv-expand")(require("dotenv-flow").config({silent: true}))
@@ -13,7 +14,7 @@ function buildConfigIfNeeded() {
return Promise.resolve()
}
return buildConfig()
return compileConfig(getProjectRoot())
}
function runOclif() {

View File

@@ -12,7 +12,8 @@
"esModuleInterop": true,
"isolatedModules": false,
"noEmit": false,
"incremental": true
"incremental": false,
"tsBuildInfoFile": null
},
"include": ["src/**/*", "types", "../../types"]
}

View File

@@ -1,6 +1,6 @@
{
"name": "@blitzjs/config",
"version": "0.38.2",
"version": "0.38.3-canary.1",
"description": "Loads the blitz app config",
"license": "MIT",
"scripts": {

View File

@@ -1,9 +1,6 @@
import * as esbuild from "esbuild"
import fs from "fs"
import {existsSync, readJSONSync} from "fs-extra"
import {NextConfig} from "next/dist/next-server/server/config"
import path, {join} from "path"
import pkgDir from "pkg-dir"
const debug = require("debug")("blitz:config")
type NextExperimental = NextConfig["experimental"]
@@ -45,14 +42,10 @@ export interface BlitzConfigNormalized extends BlitzConfig {
}
export function getProjectRoot() {
// TODO consolidate with nextjs/packages/next/server/lib/utils.ts
// IF THIS IS UPDATED, so does the one inside nextjs
return path.dirname(getConfigSrcPath())
}
export function getConfigSrcPath() {
// TODO consolidate with nextjs/packages/next/server/lib/utils.ts
// IF THIS IS UPDATED, so does the one inside nextjs
const tsPath = path.resolve(path.join(process.cwd(), "blitz.config.ts"))
if (existsSync(tsPath)) {
return tsPath
@@ -61,64 +54,6 @@ export function getConfigSrcPath() {
return jsPath
}
}
export function getConfigBuildPath() {
return path.join(getProjectRoot(), ".blitz", "blitz.config.js")
}
interface BuildConfigOptions {
watch?: boolean
}
export async function buildConfig({watch}: BuildConfigOptions = {}) {
debug("Starting buildConfig...")
const dir = pkgDir.sync()
if (!dir) {
// This will happen when running blitz no inside a blitz app
debug("Unable to find package directory")
return
}
const pkg = readJSONSync(path.join(dir, "package.json"))
const srcPath = getConfigSrcPath()
if (fs.readFileSync(srcPath, "utf8").includes("tsconfig-paths/register")) {
// User is manually handling their own typescript stuff
debug("Config contains 'tsconfig-paths/register', so skipping build")
return
}
const esbuildOptions: esbuild.BuildOptions = {
entryPoints: [srcPath],
outfile: getConfigBuildPath(),
format: "cjs",
bundle: true,
platform: "node",
external: [
"blitz",
"next",
...Object.keys(require("blitz/package").dependencies),
...Object.keys(pkg?.dependencies ?? {}),
...Object.keys(pkg?.devDependencies ?? {}),
],
}
if (watch) {
esbuildOptions.watch = {
onRebuild(error) {
if (error) {
console.error("Failed to re-build blitz config")
} else {
console.log("\n> Blitz config changed - restart for changes to take effect\n")
}
},
}
}
debug("Building config...")
debug("Src: ", getConfigSrcPath())
debug("Build: ", getConfigBuildPath())
await esbuild.build(esbuildOptions)
}
declare global {
namespace NodeJS {
interface Global {
@@ -137,11 +72,13 @@ export const getConfig = (reload?: boolean): BlitzConfigNormalized => {
const {PHASE_DEVELOPMENT_SERVER, PHASE_PRODUCTION_SERVER} = require("next/constants")
const projectRoot = getProjectRoot()
let pkgJson: any
const pkgJsonPath = join(getProjectRoot(), "package.json")
if (existsSync(pkgJsonPath)) {
pkgJson = readJSONSync(join(getProjectRoot(), "package.json"))
pkgJson = readJSONSync(pkgJsonPath)
}
let blitzConfig = {
@@ -150,15 +87,8 @@ export const getConfig = (reload?: boolean): BlitzConfigNormalized => {
},
}
const projectRoot = getProjectRoot()
const nextConfigPath = path.join(projectRoot, "next.config.js")
let blitzConfigPath
if (existsSync(path.join(projectRoot, ".blitz"))) {
blitzConfigPath = path.join(projectRoot, ".blitz", "blitz.config.js")
} else {
// projectRoot is inside .blitz/build/
blitzConfigPath = path.join(projectRoot, "..", "blitz.config.js")
}
const blitzConfigPath = path.join(projectRoot, ".blitz.config.compiled.js")
debug("nextConfigPath: " + nextConfigPath)
debug("blitzConfigPath: " + blitzConfigPath)

View File

@@ -1,7 +1,7 @@
{
"name": "@blitzjs/core",
"description": "Blitz.js core functionality",
"version": "0.38.2",
"version": "0.38.3-canary.1",
"license": "MIT",
"scripts": {
"test": "jest",
@@ -31,8 +31,8 @@
"server"
],
"dependencies": {
"@blitzjs/config": "0.38.2",
"@blitzjs/display": "0.38.2",
"@blitzjs/config": "0.38.3-canary.1",
"@blitzjs/display": "0.38.3-canary.1",
"@types/secure-password": "3.1.0",
"b64-lite": "^1.4.0",
"bad-behavior": "^1.0.1",
@@ -44,7 +44,7 @@
"jsonwebtoken": "8.5.1",
"lodash.frompairs": "4.0.1",
"nanoid": "^3.1.20",
"next": "0.38.2",
"next": "0.38.3-canary.1",
"npm-which": "^3.0.1",
"null-loader": "4.0.1",
"passport": "0.4.1",
@@ -53,7 +53,7 @@
"superjson": "1.7.2"
},
"devDependencies": {
"react": "0.0.0-experimental-6a589ad71",
"react": "18.0.0-alpha-ed6c091fe-20210701",
"zod": "3.2.0"
},
"repository": "https://github.com/blitz-js/blitz",

View File

@@ -61,6 +61,7 @@ export function withBlitzInnerWrapper(Page: BlitzPage) {
redirectAuthenticatedTo = formatWithValidation(redirectAuthenticatedTo)
}
clientDebug("[BlitzInnerRoot] redirecting to", redirectAuthenticatedTo)
const error = new RedirectError(redirectAuthenticatedTo)
error.stack = null!
throw error
@@ -76,6 +77,7 @@ export function withBlitzInnerWrapper(Page: BlitzPage) {
const url = new URL(redirectTo, window.location.href)
url.searchParams.append("next", window.location.pathname)
clientDebug("[BlitzInnerRoot] redirecting to", url.toString())
const error = new RedirectError(url.toString())
error.stack = null!
throw error

View File

@@ -94,6 +94,7 @@ class ErrorBoundary extends React.Component<
if (error instanceof RedirectError) {
clientDebug("Redirecting from ErrorBoundary to", error.url)
await (this.context as Router)?.push(error.url)
return
}
this.props.onError?.(error, info)
}
@@ -111,6 +112,7 @@ class ErrorBoundary extends React.Component<
handleRouteChange = () => {
clientDebug("Resetting error boundary on route change")
this.props.onReset?.()
this.reset()
}

View File

@@ -104,7 +104,13 @@ export const executeRpcCall = <TInput, TResult>(
}
if (response.headers.get(HEADER_SESSION_CREATED)) {
clientDebug("Session created")
await queryClient.invalidateQueries("")
// await queryClient.invalidateQueries("")
setTimeout(async () => {
// Do these in the next tick to prevent various bugs like https://github.com/blitz-js/blitz/issues/2207
clientDebug("Invalidating react-query cache...")
await queryClient.cancelQueries()
await queryClient.resetQueries()
})
}
if (response.headers.get(HEADER_CSRF_ERROR)) {
const err = new CSRFTokenMismatchError()

View File

@@ -99,21 +99,11 @@ export function passportAuth(config: BlitzPassportConfig): BlitzApiHandler {
try {
let error = err
if (!error) {
if (result === false) {
log.warning(
`Login via ${strategyName} failed - usually this means the user did not authenticate properly with the provider`,
)
error = `Login failed`
}
assert(
typeof result === "object" && result !== null,
`Your '${strategyName}' passport verify callback returned empty data. Ensure you call 'done(null, {publicData: {userId: 1}})' along with any other publicData fields you need)`,
)
assert(
(result as any).publicData,
`'publicData' is missing from your '${strategyName}' passport verify callback. Ensure you call 'done(null, {publicData: {userId: 1}})' along with any other publicData fields you need)`,
if (!error && result === false) {
log.warning(
`Login via ${strategyName} failed - usually this means the user did not authenticate properly with the provider`,
)
error = `Login failed`
}
const redirectUrlFromVerifyResult =
@@ -132,6 +122,14 @@ export function passportAuth(config: BlitzPassportConfig): BlitzApiHandler {
return
}
assert(
typeof result === "object" && result !== null,
`Your '${strategyName}' passport verify callback returned empty data. Ensure you call 'done(null, {publicData: {userId: 1}})' along with any other publicData fields you need)`,
)
assert(
(result as any).publicData,
`'publicData' is missing from your '${strategyName}' passport verify callback. Ensure you call 'done(null, {publicData: {userId: 1}})' along with any other publicData fields you need)`,
)
assert(isVerifyCallbackResult(result), "Passport verify callback is invalid")
delete (result.publicData as any)[INTERNAL_REDIRECT_URL_KEY]

View File

@@ -551,6 +551,7 @@ export const setCSRFCookie = (
expiresAt: Date,
) => {
debug("setCSRFCookie", antiCSRFToken)
assert(antiCSRFToken !== undefined, "Internal error: antiCSRFToken is being set to undefined")
setCookie(
res,
cookie.serialize(COOKIE_CSRF_TOKEN(), antiCSRFToken, {
@@ -726,8 +727,8 @@ export async function getSessionKernel(
return {
handle: payload.handle,
publicData: payload.publicData,
antiCSRFToken: payload.antiCSRFToken,
jwtPayload: payload,
antiCSRFToken,
anonymousSessionToken,
}
}

View File

@@ -31,8 +31,7 @@ export {rpcApiHandler} from "./rpc-server"
export const fixNodeFileTrace = () => {
const path = require("path")
path.resolve("next.config.js")
path.resolve(".blitz/blitz.config.js")
path.resolve(".blitz.config.compiled.js")
path.resolve(".next/server/blitz-db.js")
path.resolve(".next/serverless/blitz-db.js")
}

View File

@@ -1,6 +1,6 @@
{
"name": "@blitzjs/display",
"version": "0.38.2",
"version": "0.38.3-canary.1",
"description": "Display package for the Blitz CLI",
"homepage": "https://github.com/blitz-js/blitz#readme",
"license": "MIT",
@@ -28,8 +28,8 @@
"url": "git+https://github.com/blitz-js/blitz.git"
},
"dependencies": {
"@blitzjs/config": "0.38.2",
"@blitzjs/display": "0.38.2",
"@blitzjs/config": "0.38.3-canary.1",
"@blitzjs/display": "0.38.3-canary.1",
"chalk": "^4.1.0",
"console-table-printer": "^2.7.5",
"ora": "^5.3.0",

View File

@@ -1,6 +1,6 @@
{
"name": "eslint-config-blitz",
"version": "0.38.2",
"version": "0.38.3-canary.1",
"description": "Blitz.js eslint config",
"license": "MIT",
"scripts": {

View File

@@ -1,6 +1,6 @@
{
"name": "@blitzjs/file-pipeline",
"version": "0.38.2",
"version": "0.38.3-canary.1",
"description": "Display package for the Blitz CLI",
"homepage": "https://github.com/blitz-js/blitz#readme",
"license": "MIT",
@@ -28,7 +28,7 @@
"url": "git+https://github.com/blitz-js/blitz.git"
},
"dependencies": {
"@blitzjs/display": "0.38.2",
"@blitzjs/display": "0.38.3-canary.1",
"chalk": "^4.1.0",
"chokidar": "3.5.1",
"flush-write-stream": "2.0.0",

View File

@@ -14,9 +14,17 @@ export class RouteCache implements RouteCacheInterface {
}
getUrifromPath(path: string) {
const findStr = "/pages"
const findStrIdx = path.indexOf(findStr)
const uri = path.substring(findStrIdx + findStr.length, path.lastIndexOf("."))
let uri = path
let findStr = "/pages"
let findStrIdx = path.indexOf(findStr)
if (findStrIdx >= 0) {
uri = path.substring(findStrIdx + findStr.length, path.lastIndexOf("."))
} else {
findStr = "/api"
findStrIdx = path.indexOf(findStr)
uri = "/api" + path.substring(findStrIdx + findStr.length, path.lastIndexOf("."))
}
const uriWithoutIndex = uri.replace("/index", "")
return uriWithoutIndex.length > 0 ? uriWithoutIndex : "/"
}
@@ -44,7 +52,7 @@ export class RouteCache implements RouteCacheInterface {
private getType(file: File): RouteType | null {
const pagesPathRegex = /(pages[\\/][^_.].+(?<!\.test)\.(m?[tj]sx?|mdx))$/
const rpcPathRegex = /(api[\\/].+[\\/](queries|mutations).+)$/
const apiPathRegex = /(api[\\/].+)$/
const apiPathRegex = /(api[\\/].+\.[tj]s)$/
if (rpcPathRegex.test(file.path)) {
return "rpc"
@@ -58,7 +66,8 @@ export class RouteCache implements RouteCacheInterface {
}
add(file: File) {
if (this.routeCache[file.orginalRelative]) return
const srcPath = file.originalRelative ?? file.relative
if (this.routeCache[srcPath]) return
const type = this.getType(file)
if (!type) {
@@ -68,8 +77,8 @@ export class RouteCache implements RouteCacheInterface {
const uri = this.getUrifromPath(this.normalizePath(file.path))
const isErrorCode = this.isErrorCode(uri)
if (!isErrorCode) {
this.routeCache[file.originalRelative] = {
path: file.originalRelative,
this.routeCache[srcPath] = {
path: srcPath,
uri,
verb: this.getVerb(type),
type,
@@ -78,7 +87,8 @@ export class RouteCache implements RouteCacheInterface {
}
delete(file: File) {
delete this.routeCache[file.originalRelative]
const srcPath = file.originalRelative ?? file.relative
delete this.routeCache[srcPath]
}
filterByPath(filterFn: (givenPath: string) => boolean) {
@@ -96,7 +106,8 @@ export class RouteCache implements RouteCacheInterface {
get(file: File): RouteCacheEntry
get(key?: string | File) {
if (typeof key === "string") return this.routeCache[key]
if (key?.originalRelative) return this.routeCache[key.originalRelative]
const srcPath = key?.originalRelative ?? key?.relative
if (srcPath) return this.routeCache[srcPath]
return this.routeCache
}

View File

@@ -1,6 +1,6 @@
{
"name": "@blitzjs/generator",
"version": "0.38.2",
"version": "0.38.3-canary.1",
"description": "File generation for the Blitz CLI",
"homepage": "https://github.com/blitz-js/blitz#readme",
"license": "MIT",
@@ -33,8 +33,8 @@
"dependencies": {
"@babel/core": "7.12.10",
"@babel/plugin-transform-typescript": "7.12.1",
"@blitzjs/display": "0.38.2",
"@blitzjs/server": "0.38.2",
"@blitzjs/display": "0.38.3-canary.1",
"@blitzjs/server": "0.38.3-canary.1",
"@mrleebo/prisma-ast": "^0.2.4",
"@types/jscodeshift": "0.7.2",
"chalk": "^4.1.0",

View File

@@ -13,7 +13,7 @@ web_modules/
*.sqlite
*.sqlite-journal
.now
.blitz-console-history
.blitz**
blitz-log.log
# misc

View File

@@ -1,6 +1,6 @@
{
"name": "@blitzjs/installer",
"version": "0.38.2",
"version": "0.38.3-canary.1",
"description": "Package installation for the Blitz CLI",
"homepage": "https://github.com/blitz-js/blitz#readme",
"license": "MIT",
@@ -30,9 +30,9 @@
"dependencies": {
"@babel/core": "7.12.10",
"@babel/plugin-transform-typescript": "7.12.1",
"@blitzjs/config": "0.38.2",
"@blitzjs/display": "0.38.2",
"@blitzjs/generator": "0.38.2",
"@blitzjs/config": "0.38.3-canary.1",
"@blitzjs/display": "0.38.3-canary.1",
"@blitzjs/generator": "0.38.3-canary.1",
"@mrleebo/prisma-ast": "^0.2.4",
"@prisma/sdk": "2.19.0",
"@types/jscodeshift": "0.7.2",

View File

@@ -1,6 +1,6 @@
{
"name": "@blitzjs/repl",
"version": "0.38.2",
"version": "0.38.3-canary.1",
"description": "Repl package for Blitz CLI",
"homepage": "https://github.com/blitz-js/blitz/packages/repl/#readme",
"license": "MIT",
@@ -28,7 +28,7 @@
"url": "git+https://github.com/blitz-js/blitz.git"
},
"dependencies": {
"@blitzjs/config": "0.38.2",
"@blitzjs/config": "0.38.3-canary.1",
"chokidar": "3.5.1",
"globby": "11.0.2",
"pkg-dir": "^5.0.0",

View File

@@ -1,7 +1,7 @@
{
"name": "@blitzjs/server",
"description": "Blitz.js server functionality",
"version": "0.38.2",
"version": "0.38.3-canary.1",
"license": "MIT",
"bin": {
"next-patched": "./bin/next-patched"
@@ -23,10 +23,10 @@
"register"
],
"dependencies": {
"@blitzjs/config": "0.38.2",
"@blitzjs/core": "0.38.2",
"@blitzjs/display": "0.38.2",
"@blitzjs/file-pipeline": "0.38.2",
"@blitzjs/config": "0.38.3-canary.1",
"@blitzjs/core": "0.38.3-canary.1",
"@blitzjs/display": "0.38.3-canary.1",
"@blitzjs/file-pipeline": "0.38.3-canary.1",
"cross-spawn": "7.0.3",
"detect-port": "1.3.0",
"esbuild": "^0.11.12",

View File

@@ -1,86 +0,0 @@
import {transform} from "@blitzjs/file-pipeline"
import {Stage} from "@blitzjs/file-pipeline"
import {pathExistsSync} from "fs-extra"
import {resolve} from "path"
import File from "vinyl"
const isNextConfigPath = (p: string) => /next\.config\.(js|ts)/.test(p)
const isNowBuild = () => process.env.NOW_BUILDER || process.env.VERCEL_BUILDER
/**
* Returns a Stage that manages converting from blitz.config.js to next.config.js
*/
export const createStageConfig: Stage = ({config, processNewFile, processNewChildFile}) => {
// Preconditions
const hasNextConfig = pathExistsSync(resolve(config.src, "next.config.js"))
const hasBlitzConfig =
pathExistsSync(resolve(config.src, "blitz.config.js")) ||
pathExistsSync(resolve(config.src, "blitz.config.ts"))
if (hasNextConfig && !isNowBuild()) {
// TODO: Pause the stream and ask the user if they wish to have their configuration file renamed
const err = new Error(
"Blitz does not support next.config.js. Please rename your next.config.js to blitz.config.js",
)
err.name = "NextConfigSupportError"
throw err
}
if (!hasBlitzConfig) {
// Assume a bare blitz config
processNewFile(
new File({
cwd: config.src,
path: resolve(config.src, "blitz.config.js"),
contents: Buffer.from("module.exports = {};"),
}),
)
}
if (!hasNextConfig) {
processNewFile(
new File({
cwd: config.src,
path: resolve(config.src, "next.config.js"),
contents: Buffer.from(`
const config = require('../blitz.config.js');
module.exports = config;
`),
}),
)
}
// No need to filter yet
const stream = transform.file((file) => {
if (!isNextConfigPath(file.path)) return file
// File is next.config.js
// Vercel now adds configuration needed for Now, like serverless target,
// so we need to keep and use that
if (isNowBuild()) {
// Assume we have a next.config.js if NOW_BUILDER is true as the cli creates one
// Divert next.config to next-vercel.config.js
processNewChildFile({
parent: file,
child: new File({
cwd: config.src,
path: resolve(config.src, "next-vercel.config.js"),
contents: file.contents,
}),
stageId: "config",
subfileId: "vercel-config",
})
file.contents = Buffer.from(`
const vercelConfig = require('./next-vercel.config.js');
const config = require('../blitz.config.js');
module.exports = {...config, ...vercelConfig};
`)
}
return file
})
return {stream}
}

View File

@@ -1,7 +1,5 @@
import {ServerEnvironment} from "../config"
import {createStageConfig} from "./config"
import {createStageManifest} from "./manifest"
import {createStagePages} from "./pages"
import {createStageRelative} from "./relative"
import {createStageRewriteImports} from "./rewrite-imports"
import {createStageRouteImportManifest} from "./route-import-manifest/route-import-manifest"
@@ -23,23 +21,16 @@ export const configureStages = async (config: StagesConfig) => ({
// Order is important
createStageRelative,
createStageRewriteImports,
createStagePages,
createStageRpc(config.isTypeScript),
createStageRoutes,
createStageRouteImportManifest,
createStageConfig,
await createStageManifest(config.writeManifestFile, config.buildFolder, config.env),
],
})
export const configureRouteStages = (config: StagesConfig) => [
createStagePages,
createStageRpc(config.isTypeScript),
createStageRoutes,
]
export const configureGenerateStages = () => [
createStagePages,
createStageRoutes,
createStageRouteImportManifest,
]
export const configureGenerateStages = () => [createStageRoutes, createStageRouteImportManifest]

Some files were not shown because too many files have changed in this diff Show More