@@ -87,9 +87,10 @@ module.exports = {
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ["**/__fixtures__/**"],
|
||||
files: ["test/**", "**/__fixtures__/**"],
|
||||
rules: {
|
||||
"import/no-default-export": "off",
|
||||
"require-await": "off",
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
40
.github/workflows/main.yml
vendored
40
.github/workflows/main.yml
vendored
@@ -89,7 +89,7 @@ jobs:
|
||||
path: ./*
|
||||
key: ${{ github.sha }}
|
||||
|
||||
build_and_test_pkgs:
|
||||
testBlitzPackages:
|
||||
name: Blitz Packages Tests
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
@@ -108,7 +108,7 @@ jobs:
|
||||
env:
|
||||
CI: true
|
||||
|
||||
build_and_test_examples:
|
||||
testBlitzExamples:
|
||||
timeout-minutes: 30
|
||||
name: Blitz Example Apps Tests
|
||||
strategy:
|
||||
@@ -193,6 +193,32 @@ jobs:
|
||||
- run: node run-tests.js --timings --type unit -g 1/1
|
||||
if: ${{needs.build.outputs.docsChange != 'docs only change'}}
|
||||
|
||||
testIntegrationBlitz:
|
||||
name: Blitz - Test Integration
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
env:
|
||||
NEXT_TELEMETRY_DISABLED: 1
|
||||
NEXT_TEST_JOB: 1
|
||||
HEADLESS: true
|
||||
strategy:
|
||||
fail-fast: false
|
||||
steps:
|
||||
- run: echo ${{needs.build.outputs.docsChange}}
|
||||
- uses: actions/cache@v2
|
||||
if: ${{needs.build.outputs.docsChange != 'docs only change'}}
|
||||
id: restore-build
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ github.sha }}
|
||||
|
||||
# TODO: remove after we fix watchpack watching too much
|
||||
- run: echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p
|
||||
if: ${{needs.build.outputs.docsChange != 'docs only change'}}
|
||||
|
||||
- run: xvfb-run node nextjs/run-tests.js -c 3
|
||||
if: ${{needs.build.outputs.docsChange != 'docs only change'}}
|
||||
|
||||
testIntegration:
|
||||
name: Nextjs - Test Integration
|
||||
defaults:
|
||||
@@ -259,7 +285,15 @@ jobs:
|
||||
testsPass:
|
||||
name: thank you, next
|
||||
runs-on: ubuntu-latest
|
||||
needs: [checkPrecompiled, testIntegration, testUnit]
|
||||
needs:
|
||||
[
|
||||
checkPrecompiled,
|
||||
testIntegration,
|
||||
testIntegrationBlitz,
|
||||
testUnit,
|
||||
testBlitzPackages,
|
||||
testBlitzExamples,
|
||||
]
|
||||
steps:
|
||||
- run: exit 0
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
.log
|
||||
.DS_Store
|
||||
.jest-*
|
||||
lib
|
||||
packages/cli/lib
|
||||
node_modules
|
||||
reports
|
||||
*.log
|
||||
|
||||
@@ -26,4 +26,24 @@ module.exports = {
|
||||
},
|
||||
],
|
||||
],
|
||||
overrides: [
|
||||
{
|
||||
test: "./test/**/*",
|
||||
presets: [
|
||||
[
|
||||
"@babel/preset-env",
|
||||
{
|
||||
modules: false,
|
||||
// loose: true,
|
||||
exclude: [
|
||||
"@babel/plugin-transform-async-to-generator",
|
||||
"@babel/plugin-transform-regenerator",
|
||||
],
|
||||
},
|
||||
],
|
||||
"blitz/babel",
|
||||
],
|
||||
plugins: [],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
@@ -1,27 +1,25 @@
|
||||
const withMonorepoBuildTooling = require("@preconstruct/next")
|
||||
const {sessionMiddleware, simpleRolesIsAuthorized} = require("blitz")
|
||||
const withBundleAnalyzer = require("@next/bundle-analyzer")({
|
||||
enabled: process.env.ANALYZE === "true",
|
||||
})
|
||||
|
||||
module.exports = withMonorepoBuildTooling(
|
||||
withBundleAnalyzer({
|
||||
middleware: [
|
||||
sessionMiddleware({
|
||||
isAuthorized: simpleRolesIsAuthorized,
|
||||
// sessionExpiryMinutes: 4,
|
||||
}),
|
||||
],
|
||||
cli: {
|
||||
clearConsoleOnBlitzDev: false,
|
||||
},
|
||||
log: {
|
||||
// level: "trace",
|
||||
},
|
||||
experimental: {
|
||||
isomorphicResolverImports: false,
|
||||
},
|
||||
/*
|
||||
module.exports = withBundleAnalyzer({
|
||||
middleware: [
|
||||
sessionMiddleware({
|
||||
isAuthorized: simpleRolesIsAuthorized,
|
||||
// sessionExpiryMinutes: 4,
|
||||
}),
|
||||
],
|
||||
cli: {
|
||||
clearConsoleOnBlitzDev: false,
|
||||
},
|
||||
log: {
|
||||
// level: "trace",
|
||||
},
|
||||
experimental: {
|
||||
isomorphicResolverImports: false,
|
||||
},
|
||||
/*
|
||||
webpack: (config, {buildId, dev, isServer, defaultLoaders, webpack}) => {
|
||||
// Note: we provide webpack above so you should not `require` it
|
||||
// Perform customizations to webpack config
|
||||
@@ -34,5 +32,4 @@ module.exports = withMonorepoBuildTooling(
|
||||
return config
|
||||
},
|
||||
*/
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
const {sessionMiddleware, simpleRolesIsAuthorized} = require("blitz")
|
||||
const withMonorepoBuildTooling = require("@preconstruct/next")
|
||||
|
||||
module.exports = withMonorepoBuildTooling({
|
||||
module.exports = {
|
||||
middleware: [
|
||||
sessionMiddleware({
|
||||
isAuthorized: simpleRolesIsAuthorized,
|
||||
@@ -15,4 +14,4 @@ module.exports = withMonorepoBuildTooling({
|
||||
return config
|
||||
},
|
||||
*/
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
const { sessionMiddleware, simpleRolesIsAuthorized } = require("blitz")
|
||||
const withMonorepoBuildTooling = require("@preconstruct/next")
|
||||
const { GraphQLClient, gql } = require("graphql-request")
|
||||
|
||||
const graphQLClient = new GraphQLClient("https://graphql.fauna.com/graphql", {
|
||||
@@ -18,7 +17,7 @@ const normalizeSession = (faunaSession) => {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = withMonorepoBuildTooling({
|
||||
module.exports = {
|
||||
middleware: [
|
||||
sessionMiddleware({
|
||||
isAuthorized: simpleRolesIsAuthorized,
|
||||
@@ -159,4 +158,4 @@ module.exports = withMonorepoBuildTooling({
|
||||
return config
|
||||
},
|
||||
*/
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
const withMonorepoBuildTooling = require("@preconstruct/next")
|
||||
|
||||
module.exports = withMonorepoBuildTooling({
|
||||
module.exports = {
|
||||
webpack: (config, { buildId, dev, isServer, defaultLoaders, webpack }) => {
|
||||
// Note: we provide webpack above so you should not `require` it
|
||||
// Perform customizations to webpack config
|
||||
@@ -12,4 +10,4 @@ module.exports = withMonorepoBuildTooling({
|
||||
// Important: return the modified config
|
||||
return config
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
const withMonorepoBuildTooling = require("@preconstruct/next")
|
||||
|
||||
module.exports = withMonorepoBuildTooling({
|
||||
module.exports = {
|
||||
middleware: [],
|
||||
/* Uncomment this to customize the webpack config
|
||||
webpack: (config, { buildId, dev, isServer, defaultLoaders, webpack }) => {
|
||||
@@ -10,4 +8,4 @@ module.exports = withMonorepoBuildTooling({
|
||||
return config
|
||||
},
|
||||
*/
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
const withMonorepoBuildTooling = require("@preconstruct/next")
|
||||
|
||||
module.exports = withMonorepoBuildTooling({
|
||||
module.exports = {
|
||||
middleware: [
|
||||
(req, res, next) => {
|
||||
res.blitzCtx.referer = req.headers.referer
|
||||
@@ -24,4 +22,4 @@ module.exports = withMonorepoBuildTooling({
|
||||
// // Important: return the modified config
|
||||
// return config
|
||||
// },
|
||||
})
|
||||
}
|
||||
|
||||
37
jest-unit.config.js
Normal file
37
jest-unit.config.js
Normal file
@@ -0,0 +1,37 @@
|
||||
const {jsWithBabel: tsjPreset} = require("ts-jest/preset")
|
||||
|
||||
module.exports = {
|
||||
testEnvironment: "node",
|
||||
moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],
|
||||
modulePathIgnorePatterns: ["<rootDir>/tmp", "<rootDir>/dist", "<rootDir>/templates"],
|
||||
moduleNameMapper: {},
|
||||
setupFilesAfterEnv: ["<rootDir>/jest.setup.js"],
|
||||
transform: {
|
||||
...tsjPreset.transform,
|
||||
},
|
||||
transformIgnorePatterns: ["[/\\\\]node_modules[/\\\\].+\\.(js|jsx)$"],
|
||||
testMatch: ["<rootDir>/**/*.(spec|test).{ts,tsx,js,jsx}"],
|
||||
testURL: "http://localhost",
|
||||
// watchPlugins: [
|
||||
// require.resolve("jest-watch-typeahead/filename"),
|
||||
// require.resolve("jest-watch-typeahead/testname"),
|
||||
// ],
|
||||
coverageReporters: ["json", "lcov", "text", "clover"],
|
||||
// collectCoverage: !!`Boolean(process.env.CI)`,
|
||||
collectCoverageFrom: ["src/**/*.{ts,tsx,js,jsx}"],
|
||||
coveragePathIgnorePatterns: ["/templates/"],
|
||||
// coverageThreshold: {
|
||||
// global: {
|
||||
// branches: 100,
|
||||
// functions: 100,
|
||||
// lines: 100,
|
||||
// statements: 100,
|
||||
// },
|
||||
// },
|
||||
globals: {
|
||||
"ts-jest": {
|
||||
tsconfig: __dirname + "/tsconfig.test.json",
|
||||
isolatedModules: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -1,37 +1,10 @@
|
||||
const {jsWithBabel: tsjPreset} = require("ts-jest/preset")
|
||||
|
||||
module.exports = {
|
||||
testEnvironment: "node",
|
||||
moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],
|
||||
modulePathIgnorePatterns: ["<rootDir>/tmp", "<rootDir>/dist", "<rootDir>/templates"],
|
||||
moduleNameMapper: {},
|
||||
setupFilesAfterEnv: ["<rootDir>/jest.setup.js"],
|
||||
transform: {
|
||||
...tsjPreset.transform,
|
||||
},
|
||||
transformIgnorePatterns: ["[/\\\\]node_modules[/\\\\].+\\.(js|jsx)$"],
|
||||
testMatch: ["<rootDir>/**/*.(spec|test).{ts,tsx,js,jsx}"],
|
||||
testURL: "http://localhost",
|
||||
// watchPlugins: [
|
||||
// require.resolve("jest-watch-typeahead/filename"),
|
||||
// require.resolve("jest-watch-typeahead/testname"),
|
||||
// ],
|
||||
coverageReporters: ["json", "lcov", "text", "clover"],
|
||||
// collectCoverage: !!`Boolean(process.env.CI)`,
|
||||
collectCoverageFrom: ["src/**/*.{ts,tsx,js,jsx}"],
|
||||
coveragePathIgnorePatterns: ["/templates/"],
|
||||
// coverageThreshold: {
|
||||
// global: {
|
||||
// branches: 100,
|
||||
// functions: 100,
|
||||
// lines: 100,
|
||||
// statements: 100,
|
||||
// },
|
||||
// },
|
||||
globals: {
|
||||
"ts-jest": {
|
||||
tsconfig: __dirname + "/tsconfig.test.json",
|
||||
isolatedModules: true,
|
||||
},
|
||||
},
|
||||
testMatch: ["**/*.test.js", "**/*.test.ts"],
|
||||
verbose: true,
|
||||
rootDir: "test",
|
||||
modulePaths: ["<rootDir>/lib"],
|
||||
globalSetup: "<rootDir>/jest-global-setup.js",
|
||||
globalTeardown: "<rootDir>/jest-global-teardown.js",
|
||||
setupFilesAfterEnv: ["<rootDir>/jest-setup-after-env.js"],
|
||||
testEnvironment: "<rootDir>/jest-environment.js",
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
"dev": "taskr",
|
||||
"release": "taskr release",
|
||||
"prepublish": "npm run release && yarn types",
|
||||
"types": "tsc --declaration --emitDeclarationOnly --declarationDir dist",
|
||||
"types": "rimraf \"dist/**/*.d.ts\" && tsc --declaration --emitDeclarationOnly --declarationDir dist",
|
||||
"typescript": "tsc --noEmit --declaration",
|
||||
"ncc-compiled": "ncc cache clean && taskr ncc"
|
||||
},
|
||||
|
||||
@@ -64,7 +64,7 @@ async function main() {
|
||||
tests = (
|
||||
await glob('**/*.test.js', {
|
||||
nodir: true,
|
||||
cwd: path.join(__dirname, 'test'),
|
||||
cwd: path.join(process.cwd(), 'test'),
|
||||
})
|
||||
).filter((test) => {
|
||||
// only include the specified type
|
||||
@@ -79,7 +79,7 @@ async function main() {
|
||||
if (outputTimings && groupArg) {
|
||||
console.log('Fetching previous timings data')
|
||||
try {
|
||||
const timingsFile = path.join(__dirname, 'test-timings.json')
|
||||
const timingsFile = path.join(process.cwd(), 'test-timings.json')
|
||||
try {
|
||||
prevTimings = JSON.parse(await fs.readFile(timingsFile, 'utf8'))
|
||||
console.log('Loaded test timings from disk successfully')
|
||||
@@ -234,7 +234,7 @@ async function main() {
|
||||
} catch (err) {
|
||||
if (i < NUM_RETRIES) {
|
||||
try {
|
||||
const testDir = path.dirname(path.join(__dirname, test))
|
||||
const testDir = path.dirname(path.join(process.cwd(), test))
|
||||
console.log('Cleaning test files at', testDir)
|
||||
await exec(`git clean -fdx "${testDir}"`)
|
||||
await exec(`git checkout "${testDir}"`)
|
||||
|
||||
18
package.json
18
package.json
@@ -24,17 +24,23 @@
|
||||
},
|
||||
"scripts": {
|
||||
"postinstall": "husky install && patch-package && symlink-dir node_modules/@blitzjs/next node_modules/next",
|
||||
"wait:nextjs": "wait-on -d 1000 nextjs/packages/next/dist/build/index.js",
|
||||
"wait:nextjs-types": "wait-on -d 1000 nextjs/packages/next/dist/build/index.d.ts",
|
||||
"dev:nextjs": "yarn workspace @blitzjs/next dev",
|
||||
"dev:tsc": "tsc --watch --pretty --preserveWatchOutput",
|
||||
"dev:cli": "yarn workspace @blitzjs/cli dev",
|
||||
"dev:nextjs-types": "yarn wait:nextjs && yarn workspace @blitzjs/next types && echo 'Finished building nextjs types'",
|
||||
"dev:blitz": "preconstruct watch",
|
||||
"dev:tsc": "yarn dev:nextjs-types && tsc --watch --pretty --preserveWatchOutput",
|
||||
"dev:cli": "yarn wait:nextjs && yarn workspace @blitzjs/cli dev",
|
||||
"dev:templates": "yarn workspace @blitzjs/generator dev",
|
||||
"dev": "yarn workspace @blitzjs/next prepublish && preconstruct dev && concurrently --names \"typecheck,cli,templates\" -c \"blue,green,yellow,magenta\" -p \"{name}\" \"npm:dev:tsc\" \"npm:dev:cli\" \"npm:dev:templates\"",
|
||||
"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 @blitzjs/next prepublish",
|
||||
"build": "yarn build:nextjs && cross-env BLITZ_PROD_BUILD=true preconstruct build && ultra -r --filter \"packages/*\" buildpkg && tsc",
|
||||
"lint": "eslint --ext \".js,.ts,.tsx\" .",
|
||||
"link-cli": "yarn workspace blitz link",
|
||||
"unlink-cli": "yarn workspace blitz unlink",
|
||||
"test": "yarn run lint && yarn run build && ultra -r test",
|
||||
"testheadless": "cross-env HEADLESS=true yarn test:integration",
|
||||
"test:integration": "jest --runInBand",
|
||||
"test:packages": "yarn run build && yarn testonly:packages",
|
||||
"test:examples": "yarn run build && yarn testonly:examples",
|
||||
"test:nextjs-size": "yarn --cwd nextjs testheadless --testPathPattern \"integration/(build-output|size-limit|fallback-modules)\"",
|
||||
@@ -77,8 +83,7 @@
|
||||
"@babel/preset-typescript": "7.12.7",
|
||||
"@juanm04/cpx": "2.0.0",
|
||||
"@manypkg/cli": "0.17.0",
|
||||
"@preconstruct/cli": "2.0.5",
|
||||
"@preconstruct/next": "2.0.0",
|
||||
"@preconstruct/cli": "2.0.7",
|
||||
"@rollup/pluginutils": "4.1.0",
|
||||
"@size-limit/preset-small-lib": "4.9.2",
|
||||
"@testing-library/jest-dom": "5.11.9",
|
||||
@@ -95,6 +100,7 @@
|
||||
"@types/flush-write-stream": "1.0.0",
|
||||
"@types/from2": "2.3.0",
|
||||
"@types/fs-extra": "8.1.0",
|
||||
"@types/get-port": "4.2.0",
|
||||
"@types/gulp-if": "0.0.33",
|
||||
"@types/htmlescape": "^1.1.1",
|
||||
"@types/ink-spinner": "3.0.0",
|
||||
@@ -153,6 +159,7 @@
|
||||
"eslint-plugin-simple-import-sort": "7.0.0",
|
||||
"eslint-plugin-unicorn": "26.0.1",
|
||||
"eslint_d": "10.0.0",
|
||||
"get-port": "5.1.1",
|
||||
"husky": "5.1.2",
|
||||
"jest": "27.0.0-next.5",
|
||||
"lerna": "4.0.0",
|
||||
@@ -179,6 +186,7 @@
|
||||
"stdout-stderr": "0.1.13",
|
||||
"strip-ansi": "6.0.0",
|
||||
"test-listen": "1.1.0",
|
||||
"tree-kill": "1.2.2",
|
||||
"ts-jest": "27.0.0-next.10",
|
||||
"tslib": "2.1.0",
|
||||
"typescript": "4.1.5",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
module.exports = {
|
||||
preset: '../../jest.config.js',
|
||||
preset: '../../jest-unit.config.js',
|
||||
testEnvironment: 'jest-environment-jsdom',
|
||||
};
|
||||
|
||||
@@ -4,7 +4,8 @@ import RewriteImports from './rewrite-imports';
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default function preset(_api: any, options = {}) {
|
||||
// const isTest = _api.env('test');
|
||||
const isRunningInJest = Boolean(process.env.JEST_WORKER_ID);
|
||||
const isRunningInJest =
|
||||
process.env.JEST_WORKER_ID && !process.env.__NEXT_TEST_MODE;
|
||||
|
||||
const config = {
|
||||
presets: [[require('next/babel'), options]],
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
module.exports = {
|
||||
preset: "../../jest.config.js",
|
||||
preset: "../../jest-unit.config.js",
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
module.exports = {
|
||||
preset: "../../jest.config.js",
|
||||
preset: "../../jest-unit.config.js",
|
||||
// collectCoverage: !!`Boolean(process.env.CI)`,
|
||||
modulePathIgnorePatterns: ["<rootDir>/tmp", "<rootDir>/lib", "<rootDir>/commands/.test"],
|
||||
testPathIgnorePatterns: ["src/commands/test.ts", "test/commands/.test"],
|
||||
|
||||
@@ -40,7 +40,10 @@ export class Dev extends Command {
|
||||
const {getConfig} = await import("@blitzjs/config")
|
||||
|
||||
const blitzConfig = getConfig()
|
||||
if (blitzConfig.cli?.clearConsoleOnBlitzDev !== false) {
|
||||
if (
|
||||
blitzConfig.cli?.clearConsoleOnBlitzDev !== false &&
|
||||
!process.env.BLITZ_TEST_ENVIRONMENT
|
||||
) {
|
||||
const {log} = await import("@blitzjs/display")
|
||||
log.clearConsole()
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {readJSON} from "fs-extra"
|
||||
import {existsSync, readJSON} from "fs-extra"
|
||||
import {resolve} from "path"
|
||||
import pkgDir from "pkg-dir"
|
||||
|
||||
@@ -54,6 +54,10 @@ export const isBlitzRoot = async (): Promise<{
|
||||
if (err.code === "ENOENT") {
|
||||
const out = await checkParent()
|
||||
|
||||
if (existsSync("./blitz.config.js") || existsSync("./blitz.config.ts")) {
|
||||
return {err: false}
|
||||
}
|
||||
|
||||
if (out === false) {
|
||||
return {
|
||||
err: true,
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
module.exports = {
|
||||
preset: "../../jest.config.js",
|
||||
preset: "../../jest-unit.config.js",
|
||||
}
|
||||
|
||||
@@ -5,7 +5,9 @@ import pkgDir from "pkg-dir"
|
||||
const debug = require("debug")("blitz:config")
|
||||
|
||||
export function getProjectRoot() {
|
||||
return pkgDir.sync() || process.cwd()
|
||||
return (
|
||||
path.dirname(path.resolve(process.cwd(), "blitz.config.js")) || pkgDir.sync() || process.cwd()
|
||||
)
|
||||
}
|
||||
|
||||
export interface BlitzConfig extends Record<string, unknown> {
|
||||
@@ -40,11 +42,16 @@ export const getConfig = (reload?: boolean): BlitzConfig => {
|
||||
|
||||
const {PHASE_DEVELOPMENT_SERVER, PHASE_PRODUCTION_SERVER} = require("next/constants")
|
||||
|
||||
const pkgJson = readJSONSync(join(getProjectRoot(), "package.json"))
|
||||
let pkgJson: any
|
||||
|
||||
const pkgJsonPath = join(getProjectRoot(), "package.json")
|
||||
if (existsSync(pkgJsonPath)) {
|
||||
pkgJson = readJSONSync(join(getProjectRoot(), "package.json"))
|
||||
}
|
||||
|
||||
let blitzConfig = {
|
||||
_meta: {
|
||||
packageName: pkgJson.name,
|
||||
packageName: pkgJson?.name,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -89,10 +96,10 @@ export const getConfig = (reload?: boolean): BlitzConfig => {
|
||||
...loadedNextConfig,
|
||||
...loadedBlitzConfig,
|
||||
}
|
||||
} catch {
|
||||
} catch (error) {
|
||||
// https://github.com/blitz-js/blitz/issues/2080
|
||||
if (!process.env.JEST_WORKER_ID) {
|
||||
console.error("Failed to load config in getConfig()")
|
||||
console.error("Failed to load config in getConfig()", error)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
module.exports = {
|
||||
preset: "../../jest.config.js",
|
||||
preset: "../../jest-unit.config.js",
|
||||
testEnvironment: "jest-environment-jsdom",
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
module.exports = {
|
||||
preset: "../../jest.config.js",
|
||||
preset: "../../jest-unit.config.js",
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
module.exports = {
|
||||
preset: "../../jest.config.js",
|
||||
preset: "../../jest-unit.config.js",
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
module.exports = {
|
||||
preset: "../../jest.config.js",
|
||||
preset: "../../jest-unit.config.js",
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
module.exports = {
|
||||
preset: "../../jest.config.js",
|
||||
preset: "../../jest-unit.config.js",
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
module.exports = {
|
||||
preset: "../../jest.config.js",
|
||||
preset: "../../jest-unit.config.js",
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
module.exports = {
|
||||
preset: "../../jest.config.js",
|
||||
preset: "../../jest-unit.config.js",
|
||||
}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
diff --git a/node_modules/@preconstruct/cli/cli/dist/cli.cjs.dev.js b/node_modules/@preconstruct/cli/cli/dist/cli.cjs.dev.js
|
||||
index 7e40755..fde60cc 100644
|
||||
--- a/node_modules/@preconstruct/cli/cli/dist/cli.cjs.dev.js
|
||||
+++ b/node_modules/@preconstruct/cli/cli/dist/cli.cjs.dev.js
|
||||
@@ -1770,6 +1770,8 @@ let getRollupConfig = (pkg, entrypoints, aliases, type, reportTransformedFile) =
|
||||
external.push(...builtInModules);
|
||||
}
|
||||
|
||||
+ external.push('next', 'react', '@babel/core', 'prettier')
|
||||
+
|
||||
let input = {};
|
||||
entrypoints.forEach(entrypoint => {
|
||||
input[path__default.relative(pkg.directory, path__default.join(entrypoint.directory, "dist", getNameForDistForEntrypoint(entrypoint)))] = entrypoint.source;
|
||||
22
patches/@preconstruct+cli+2.0.7.patch
Normal file
22
patches/@preconstruct+cli+2.0.7.patch
Normal file
@@ -0,0 +1,22 @@
|
||||
diff --git a/node_modules/@preconstruct/cli/cli/dist/cli.cjs.dev.js b/node_modules/@preconstruct/cli/cli/dist/cli.cjs.dev.js
|
||||
index 972d57b..e6bc64b 100644
|
||||
--- a/node_modules/@preconstruct/cli/cli/dist/cli.cjs.dev.js
|
||||
+++ b/node_modules/@preconstruct/cli/cli/dist/cli.cjs.dev.js
|
||||
@@ -166,7 +166,7 @@ function format(args, messageType, scope) {
|
||||
info: chalk.cyan("info"),
|
||||
none: ""
|
||||
}[messageType];
|
||||
- let fullPrefix = "🎁 " + prefix + (scope === undefined ? "" : " " + chalk.cyan(scope));
|
||||
+ let fullPrefix = prefix + (scope === undefined ? "" : " " + chalk.cyan(scope));
|
||||
return fullPrefix + util.format("", ...args).split("\n").reduce((str, line) => {
|
||||
const prefixed = `${str}\n${fullPrefix}`;
|
||||
return line ? `${prefixed} ${line}` : prefixed;
|
||||
@@ -1917,6 +1917,8 @@ let getRollupConfig = (pkg, entrypoints, aliases, type, reportTransformedFile) =
|
||||
external.push(...builtInModules);
|
||||
}
|
||||
|
||||
+ external.push('next', 'react', '@babel/core', 'prettier', '.blitz')
|
||||
+
|
||||
let input = {};
|
||||
entrypoints.forEach(entrypoint => {
|
||||
input[path__default.relative(pkg.directory, path__default.join(entrypoint.directory, "dist", getNameForDistForEntrypoint(entrypoint)))] = entrypoint.source;
|
||||
4
test/integration/queries/babel.config.js
Normal file
4
test/integration/queries/babel.config.js
Normal file
@@ -0,0 +1,4 @@
|
||||
module.exports = {
|
||||
presets: ["blitz/babel"],
|
||||
plugins: [],
|
||||
}
|
||||
11
test/integration/queries/blitz.config.js
Normal file
11
test/integration/queries/blitz.config.js
Normal file
@@ -0,0 +1,11 @@
|
||||
module.exports = {
|
||||
// replace me
|
||||
async rewrites() {
|
||||
return [
|
||||
{
|
||||
source: "/blog/post/:pid",
|
||||
destination: "/blog/:pid",
|
||||
},
|
||||
]
|
||||
},
|
||||
}
|
||||
16
test/integration/queries/pages/blog/[post].js
Normal file
16
test/integration/queries/pages/blog/[post].js
Normal file
@@ -0,0 +1,16 @@
|
||||
import {useRouter} from "blitz"
|
||||
import React from "react"
|
||||
|
||||
const Post = () => {
|
||||
const router = useRouter()
|
||||
|
||||
return (
|
||||
<>
|
||||
<div id="as-path">{router.asPath}</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
Post.getInitialProps = () => ({hello: "hi"})
|
||||
|
||||
export default Post
|
||||
3
test/integration/queries/pages/index.js
Normal file
3
test/integration/queries/pages/index.js
Normal file
@@ -0,0 +1,3 @@
|
||||
const page = () => "hello from sub id"
|
||||
page.getInitialProps = () => ({hello: "hi"})
|
||||
export default page
|
||||
1
test/integration/queries/pages/normal.js
Normal file
1
test/integration/queries/pages/normal.js
Normal file
@@ -0,0 +1 @@
|
||||
export default () => <p id="normal-text">a normal page</p>
|
||||
81
test/integration/queries/test/index.test.ts
Normal file
81
test/integration/queries/test/index.test.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
import {
|
||||
blitzBuild,
|
||||
blitzStart,
|
||||
File,
|
||||
findPort,
|
||||
killApp,
|
||||
launchApp,
|
||||
renderViaHTTP,
|
||||
} from "blitz-test-utils"
|
||||
import cheerio from "cheerio"
|
||||
import {join} from "path"
|
||||
|
||||
jest.setTimeout(1000 * 60 * 5)
|
||||
let app: any
|
||||
let appPort: number
|
||||
const appDir = join(__dirname, "..")
|
||||
const blitzConfig = new File(join(appDir, "blitz.config.js"))
|
||||
|
||||
const runTests = () => {
|
||||
it("should have gip in __NEXT_DATA__", async () => {
|
||||
const html = await renderViaHTTP(appPort, "/")
|
||||
const $ = cheerio.load(html)
|
||||
expect(JSON.parse($("#__NEXT_DATA__").text()).gip).toBe(true)
|
||||
})
|
||||
|
||||
it("should not have gip in __NEXT_DATA__ for non-GIP page", async () => {
|
||||
const html = await renderViaHTTP(appPort, "/normal")
|
||||
const $ = cheerio.load(html)
|
||||
expect("gip" in JSON.parse($("#__NEXT_DATA__").text())).toBe(false)
|
||||
})
|
||||
|
||||
it("should have correct router.asPath for direct visit dynamic page", async () => {
|
||||
const html = await renderViaHTTP(appPort, "/blog/1")
|
||||
const $ = cheerio.load(html)
|
||||
expect($("#as-path").text()).toBe("/blog/1")
|
||||
})
|
||||
|
||||
it("should have correct router.asPath for direct visit dynamic page rewrite direct", async () => {
|
||||
const html = await renderViaHTTP(appPort, "/blog/post/1")
|
||||
const $ = cheerio.load(html)
|
||||
expect($("#as-path").text()).toBe("/blog/post/1")
|
||||
})
|
||||
}
|
||||
|
||||
describe("getInitialProps", () => {
|
||||
describe("dev mode", () => {
|
||||
beforeAll(async () => {
|
||||
appPort = await findPort()
|
||||
app = await launchApp(appDir, appPort)
|
||||
})
|
||||
afterAll(() => killApp(app))
|
||||
|
||||
runTests()
|
||||
})
|
||||
|
||||
describe("serverless mode", () => {
|
||||
beforeAll(async () => {
|
||||
blitzConfig.replace("// replace me", `target: 'serverless', `)
|
||||
await blitzBuild(appDir)
|
||||
appPort = await findPort()
|
||||
app = await blitzStart(appDir, appPort)
|
||||
})
|
||||
afterAll(async () => {
|
||||
await killApp(app)
|
||||
blitzConfig.restore()
|
||||
})
|
||||
|
||||
runTests()
|
||||
})
|
||||
|
||||
describe("production mode", () => {
|
||||
beforeAll(async () => {
|
||||
await blitzBuild(appDir)
|
||||
appPort = await findPort()
|
||||
app = await blitzStart(appDir, appPort)
|
||||
})
|
||||
afterAll(() => killApp(app))
|
||||
|
||||
runTests()
|
||||
})
|
||||
})
|
||||
89
test/jest-environment.js
Normal file
89
test/jest-environment.js
Normal file
@@ -0,0 +1,89 @@
|
||||
// my-custom-environment
|
||||
const http = require("http")
|
||||
const getPort = require("get-port")
|
||||
const seleniumServer = require("selenium-standalone")
|
||||
const NodeEnvironment = require("jest-environment-node")
|
||||
|
||||
const {BROWSER_NAME: browserName = "chrome", SKIP_LOCAL_SELENIUM_SERVER} = process.env
|
||||
|
||||
const newTabPg = `
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>new tab</title>
|
||||
</head>
|
||||
<body>
|
||||
<a href="about:blank" target="_blank" id="new">Click me</a>
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
|
||||
class CustomEnvironment extends NodeEnvironment {
|
||||
async setup() {
|
||||
await super.setup()
|
||||
// Since ie11 doesn't like dataURIs we have to spin up a
|
||||
// server to handle the new tab page
|
||||
this.server = http.createServer((req, res) => {
|
||||
res.statusCode = 200
|
||||
res.end(newTabPg)
|
||||
})
|
||||
const newTabPort = await getPort()
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
this.server.listen(newTabPort, (err) => {
|
||||
if (err) return reject(err)
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
|
||||
let seleniumServerPort
|
||||
|
||||
if (browserName !== "chrome" && SKIP_LOCAL_SELENIUM_SERVER !== "true") {
|
||||
console.log("Installing selenium server")
|
||||
await new Promise((resolve, reject) => {
|
||||
seleniumServer.install((err) => {
|
||||
if (err) return reject(err)
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
|
||||
console.log("Starting selenium server")
|
||||
await new Promise((resolve, reject) => {
|
||||
seleniumServer.start((err, child) => {
|
||||
if (err) return reject(err)
|
||||
this.seleniumServer = child
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
console.log("Started selenium server")
|
||||
seleniumServerPort = 4444
|
||||
}
|
||||
|
||||
this.global.wd = null
|
||||
this.global._newTabPort = newTabPort
|
||||
this.global.browserName = browserName
|
||||
this.global.seleniumServerPort = seleniumServerPort
|
||||
this.global.browserStackLocalId = global.browserStackLocalId
|
||||
}
|
||||
|
||||
async teardown() {
|
||||
await super.teardown()
|
||||
|
||||
if (this.server) {
|
||||
this.server.close()
|
||||
}
|
||||
if (this.global.wd) {
|
||||
try {
|
||||
await this.global.wd.quit()
|
||||
} catch (err) {
|
||||
console.log(`Failed to quit webdriver instance`, err)
|
||||
}
|
||||
}
|
||||
// must come after wd.quit()
|
||||
if (this.seleniumServer) {
|
||||
this.seleniumServer.kill()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = CustomEnvironment
|
||||
24
test/jest-global-setup.js
Normal file
24
test/jest-global-setup.js
Normal file
@@ -0,0 +1,24 @@
|
||||
let globalSetup = () => {}
|
||||
|
||||
if (process.env.BROWSERSTACK) {
|
||||
const {Local} = require("browserstack-local")
|
||||
const browserStackLocal = new Local()
|
||||
const localBrowserStackOpts = {
|
||||
key: process.env.BROWSERSTACK_ACCESS_KEY,
|
||||
localIdentifier: new Date().getTime(), // Adding a unique local identifier to run parallel tests on BrowserStack
|
||||
}
|
||||
global.browserStackLocal = browserStackLocal
|
||||
global.browserStackLocalId = localBrowserStackOpts.localIdentifier
|
||||
|
||||
globalSetup = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
browserStackLocal.start(localBrowserStackOpts, (err) => {
|
||||
if (err) return reject(err)
|
||||
console.log("Started BrowserStackLocal", browserStackLocal.isRunning())
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = globalSetup
|
||||
9
test/jest-global-teardown.js
Normal file
9
test/jest-global-teardown.js
Normal file
@@ -0,0 +1,9 @@
|
||||
let globalTeardown = () => {}
|
||||
|
||||
if (process.env.BROWSERSTACK) {
|
||||
globalTeardown = () => global.browserStackLocal.killAllProcesses(() => {})
|
||||
}
|
||||
|
||||
module.exports = async () => {
|
||||
await globalTeardown()
|
||||
}
|
||||
9
test/jest-setup-after-env.js
Normal file
9
test/jest-setup-after-env.js
Normal file
@@ -0,0 +1,9 @@
|
||||
/* eslint-env jest */
|
||||
|
||||
process.env.BLITZ_TEST_ENVIRONMENT = true
|
||||
|
||||
if (process.env.JEST_RETRY_TIMES) {
|
||||
const retries = Number(process.env.JEST_RETRY_TIMES)
|
||||
console.log(`Configuring jest retries: ${retries}`)
|
||||
jest.retryTimes(retries)
|
||||
}
|
||||
640
test/lib/blitz-test-utils.ts
Normal file
640
test/lib/blitz-test-utils.ts
Normal file
@@ -0,0 +1,640 @@
|
||||
import {ChildProcess} from "child_process"
|
||||
import spawn from "cross-spawn"
|
||||
import express from "express"
|
||||
import {existsSync, readFileSync, unlinkSync, writeFileSync} from "fs"
|
||||
import {writeFile} from "fs-extra"
|
||||
import getPort from "get-port"
|
||||
import http from "http"
|
||||
// `next` here is the symlink in `test/node_modules/next` which points to the root directory.
|
||||
// This is done so that requiring from `next` works.
|
||||
// The reason we don't import the relative path `../../dist/<etc>` is that it would lead to inconsistent module singletons
|
||||
// import server from "next/dist/server/next"
|
||||
import _pkg from "next/package.json"
|
||||
import fetch from "node-fetch"
|
||||
import path from "path"
|
||||
import qs from "querystring"
|
||||
import treeKill from "tree-kill"
|
||||
|
||||
// export const nextServer = server
|
||||
export const pkg = _pkg
|
||||
|
||||
// polyfill Object.fromEntries for the test/integration/relay-analytics tests
|
||||
// on node 10, this can be removed after we no longer support node 10
|
||||
if (!Object.fromEntries) {
|
||||
Object.fromEntries = require("core-js/features/object/from-entries")
|
||||
}
|
||||
|
||||
export function initBlitzServerScript(
|
||||
scriptPath: string,
|
||||
successRegexp: RegExp,
|
||||
env: Record<any, any>,
|
||||
failRegexp: RegExp,
|
||||
opts?: {
|
||||
onStdout?: (stdout: string) => void
|
||||
onStderr?: (stderr: string) => void
|
||||
},
|
||||
) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const instance = spawn("node", ["--no-deprecation", scriptPath], {env})
|
||||
|
||||
function handleStdout(data: Buffer) {
|
||||
const message = data.toString()
|
||||
if (successRegexp.test(message)) {
|
||||
resolve(instance)
|
||||
}
|
||||
process.stdout.write(message)
|
||||
|
||||
if (opts && opts.onStdout) {
|
||||
opts.onStdout(message.toString())
|
||||
}
|
||||
}
|
||||
|
||||
function handleStderr(data: Buffer) {
|
||||
const message = data.toString()
|
||||
if (failRegexp && failRegexp.test(message)) {
|
||||
instance.kill()
|
||||
return reject(new Error("received failRegexp"))
|
||||
}
|
||||
process.stderr.write(message)
|
||||
|
||||
if (opts && opts.onStderr) {
|
||||
opts.onStderr(message.toString())
|
||||
}
|
||||
}
|
||||
|
||||
instance.stdout.on("data", handleStdout)
|
||||
instance.stderr.on("data", handleStderr)
|
||||
|
||||
instance.on("close", () => {
|
||||
instance.stdout.removeListener("data", handleStdout)
|
||||
instance.stderr.removeListener("data", handleStderr)
|
||||
})
|
||||
|
||||
instance.on("error", (err) => {
|
||||
reject(err)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export function renderViaAPI(app: any, pathname: string, query: Record<any, any>) {
|
||||
const url = `${pathname}${query ? `?${qs.stringify(query)}` : ""}`
|
||||
return app.renderToHTML({url}, {}, pathname, query)
|
||||
}
|
||||
|
||||
export async function renderViaHTTP(
|
||||
appPort: number,
|
||||
pathname: string,
|
||||
query?: Record<any, any>,
|
||||
opts?: Record<any, any>,
|
||||
) {
|
||||
return fetchViaHTTP(appPort, pathname, query, opts).then((res) => res.text())
|
||||
}
|
||||
|
||||
export function fetchViaHTTP(
|
||||
appPort: number,
|
||||
pathname: string,
|
||||
query?: Record<any, any>,
|
||||
opts?: Record<any, any>,
|
||||
) {
|
||||
const url = `http://localhost:${appPort}${pathname}${query ? `?${qs.stringify(query)}` : ""}`
|
||||
return fetch(url, opts)
|
||||
}
|
||||
|
||||
export function findPort() {
|
||||
return getPort()
|
||||
}
|
||||
|
||||
interface RunBlitzCommandOptions {
|
||||
cwd?: string
|
||||
env?: Record<any, any>
|
||||
spawnOptions?: any
|
||||
instance?: any
|
||||
stderr?: boolean
|
||||
stdout?: boolean
|
||||
ignoreFail?: boolean
|
||||
}
|
||||
|
||||
export function runBlitzCommand(argv: any[], options: RunBlitzCommandOptions = {}) {
|
||||
const blitzDir = path.dirname(require.resolve("blitz/package"))
|
||||
const blitzBin = path.join(blitzDir, "bin/blitz")
|
||||
const cwd = options.cwd || blitzDir
|
||||
// Let Next.js decide the environment
|
||||
const env = {
|
||||
...process.env,
|
||||
...options.env,
|
||||
NODE_ENV: "",
|
||||
__NEXT_TEST_MODE: "true",
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
console.log(`Running command "blitz ${argv.join(" ")}"`)
|
||||
const instance = spawn("node", ["--no-deprecation", blitzBin, ...argv], {
|
||||
...options.spawnOptions,
|
||||
cwd,
|
||||
env,
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
})
|
||||
|
||||
if (typeof options.instance === "function") {
|
||||
options.instance(instance)
|
||||
}
|
||||
|
||||
let stderrOutput = ""
|
||||
if (options.stderr) {
|
||||
instance.stderr.on("data", function (chunk) {
|
||||
stderrOutput += chunk
|
||||
})
|
||||
}
|
||||
|
||||
let stdoutOutput = ""
|
||||
if (options.stdout) {
|
||||
instance.stdout.on("data", function (chunk) {
|
||||
stdoutOutput += chunk
|
||||
})
|
||||
}
|
||||
|
||||
instance.on("close", (code, signal) => {
|
||||
if (!options.stderr && !options.stdout && !options.ignoreFail && code !== 0) {
|
||||
console.log(stderrOutput)
|
||||
return reject(new Error(`command failed with code ${code}`))
|
||||
}
|
||||
|
||||
resolve({
|
||||
code,
|
||||
signal,
|
||||
stdout: stdoutOutput,
|
||||
stderr: stderrOutput,
|
||||
})
|
||||
})
|
||||
|
||||
instance.on("error", (err: any) => {
|
||||
console.log(stderrOutput)
|
||||
err.stdout = stdoutOutput
|
||||
err.stderr = stderrOutput
|
||||
reject(err)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
interface RunBlitzLaunchOptions {
|
||||
cwd?: string
|
||||
env?: Record<any, any>
|
||||
onStdout?: (stdout: string) => void
|
||||
onStderr?: (stderr: string) => void
|
||||
stderr?: boolean
|
||||
stdout?: boolean
|
||||
blitzStart?: boolean
|
||||
}
|
||||
|
||||
export function runBlitzLaunchCommand(
|
||||
argv: any[],
|
||||
stdOut: unknown,
|
||||
opts: RunBlitzLaunchOptions = {},
|
||||
) {
|
||||
const blitzDir = path.dirname(require.resolve("blitz/package"))
|
||||
const blitzBin = path.join(blitzDir, "bin/blitz")
|
||||
const cwd = opts.cwd ?? path.dirname(require.resolve("blitz/package"))
|
||||
console.log(cwd)
|
||||
const env = {
|
||||
...process.env,
|
||||
NODE_ENV: undefined,
|
||||
__NEXT_TEST_MODE: "true",
|
||||
...opts.env,
|
||||
}
|
||||
|
||||
return new Promise<void | string | ChildProcess>((resolve, reject) => {
|
||||
const instance = spawn(
|
||||
"node",
|
||||
["--no-deprecation", blitzBin, opts.blitzStart ? "start" : "dev", ...argv],
|
||||
{cwd, env},
|
||||
)
|
||||
let didResolve = false
|
||||
|
||||
function handleStdout(data: Buffer) {
|
||||
const message = data.toString()
|
||||
const bootupMarkers = {
|
||||
dev: /compiled successfully/i,
|
||||
start: /started server/i,
|
||||
}
|
||||
if (bootupMarkers[opts.blitzStart || stdOut ? "start" : "dev"].test(message)) {
|
||||
if (!didResolve) {
|
||||
didResolve = true
|
||||
resolve(stdOut ? message : instance)
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof opts.onStdout === "function") {
|
||||
opts.onStdout(message)
|
||||
}
|
||||
|
||||
if (opts.stdout !== false) {
|
||||
process.stdout.write(message)
|
||||
}
|
||||
}
|
||||
|
||||
function handleStderr(data: Buffer) {
|
||||
const message = data.toString()
|
||||
if (typeof opts.onStderr === "function") {
|
||||
opts.onStderr(message)
|
||||
}
|
||||
|
||||
if (opts.stderr !== false) {
|
||||
process.stderr.write(message)
|
||||
}
|
||||
}
|
||||
|
||||
instance.stdout.on("data", handleStdout)
|
||||
instance.stderr.on("data", handleStderr)
|
||||
|
||||
instance.on("close", () => {
|
||||
instance.stdout.removeListener("data", handleStdout)
|
||||
instance.stderr.removeListener("data", handleStderr)
|
||||
if (!didResolve) {
|
||||
didResolve = true
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
|
||||
instance.on("error", (err) => {
|
||||
reject(err)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Launch the app in dev mode.
|
||||
export function launchApp(dir: string, port: number, opts: RunBlitzLaunchOptions = {}) {
|
||||
return runBlitzLaunchCommand(["-p", port], undefined, {cwd: dir, ...opts})
|
||||
}
|
||||
|
||||
export function blitzBuild(dir: string, args = [], opts: RunBlitzCommandOptions = {}) {
|
||||
return runBlitzCommand(["build", ...args], {cwd: dir, ...opts})
|
||||
}
|
||||
|
||||
export function blitzExport(dir: string, {outdir}, opts: RunBlitzCommandOptions = {}) {
|
||||
return runBlitzCommand(["export", "--outdir", outdir], {cwd: dir, ...opts})
|
||||
}
|
||||
|
||||
export function blitzExportDefault(dir: string, opts: RunBlitzCommandOptions = {}) {
|
||||
return runBlitzCommand(["export"], {cwd: dir, ...opts})
|
||||
}
|
||||
|
||||
export function blitzStart(dir: string, port: number, opts: RunBlitzLaunchOptions = {}) {
|
||||
return runBlitzLaunchCommand(["-p", port], undefined, {
|
||||
cwd: dir,
|
||||
...opts,
|
||||
blitzStart: true,
|
||||
})
|
||||
}
|
||||
|
||||
export function buildTS(args = [], cwd: string, env = {}) {
|
||||
cwd = cwd || path.dirname(require.resolve("@blitzjs/cli/package"))
|
||||
env = {...process.env, NODE_ENV: undefined, ...env}
|
||||
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
const instance = spawn(
|
||||
"node",
|
||||
["--no-deprecation", require.resolve("typescript/lib/tsc"), ...args],
|
||||
{cwd, env},
|
||||
)
|
||||
let output = ""
|
||||
|
||||
const handleData = (chunk) => {
|
||||
output += chunk.toString()
|
||||
}
|
||||
|
||||
instance.stdout.on("data", handleData)
|
||||
instance.stderr.on("data", handleData)
|
||||
|
||||
instance.on("exit", (code) => {
|
||||
if (code) {
|
||||
return reject(new Error("exited with code: " + code + "\n" + output))
|
||||
}
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Kill a launched app
|
||||
export async function killApp(instance) {
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
treeKill(instance.pid, (err) => {
|
||||
if (err) {
|
||||
if (
|
||||
process.platform === "win32" &&
|
||||
typeof err.message === "string" &&
|
||||
(err.message.includes(`no running instance of the task`) ||
|
||||
err.message.includes(`not found`))
|
||||
) {
|
||||
// Windows throws an error if the process is already dead
|
||||
//
|
||||
// Command failed: taskkill /pid 6924 /T /F
|
||||
// ERROR: The process with PID 6924 (child process of PID 6736) could not be terminated.
|
||||
// Reason: There is no running instance of the task.
|
||||
return resolve()
|
||||
}
|
||||
return reject(err)
|
||||
}
|
||||
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export async function startApp(app: any) {
|
||||
await app.prepare()
|
||||
const handler = app.getRequestHandler()
|
||||
const server = http.createServer(handler)
|
||||
;(server as any).__app = app
|
||||
|
||||
await promiseCall(server, "listen")
|
||||
return server
|
||||
}
|
||||
|
||||
export async function stopApp(server: any) {
|
||||
if (server.__app) {
|
||||
await server.__app.close()
|
||||
}
|
||||
await promiseCall(server, "close")
|
||||
}
|
||||
|
||||
export function promiseCall(obj: any, method: any, ...args: any[]) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const newArgs = [
|
||||
...args,
|
||||
function (err: any, res: any) {
|
||||
if (err) return reject(err)
|
||||
resolve(res)
|
||||
},
|
||||
]
|
||||
|
||||
obj[method](...newArgs)
|
||||
})
|
||||
}
|
||||
|
||||
export function waitFor(millis: number) {
|
||||
return new Promise((resolve) => setTimeout(resolve, millis))
|
||||
}
|
||||
|
||||
export async function startStaticServer(dir: string) {
|
||||
const app = express()
|
||||
const server = http.createServer(app)
|
||||
app.use(express.static(dir))
|
||||
|
||||
await promiseCall(server, "listen")
|
||||
return server
|
||||
}
|
||||
|
||||
export async function startCleanStaticServer(dir: string) {
|
||||
const app = express()
|
||||
const server = http.createServer(app)
|
||||
app.use(express.static(dir, {extensions: ["html"]}))
|
||||
|
||||
await promiseCall(server, "listen")
|
||||
return server
|
||||
}
|
||||
|
||||
// check for content in 1 second intervals timing out after
|
||||
// 30 seconds
|
||||
export async function check(contentFn: Function, regex: RegExp, hardError = true) {
|
||||
let content: any
|
||||
let lastErr: any
|
||||
|
||||
for (let tries = 0; tries < 30; tries++) {
|
||||
try {
|
||||
content = await contentFn()
|
||||
if (typeof regex === "string") {
|
||||
if (regex === content) {
|
||||
return true
|
||||
}
|
||||
} else if (regex.test(content)) {
|
||||
// found the content
|
||||
return true
|
||||
}
|
||||
await waitFor(1000)
|
||||
} catch (err) {
|
||||
await waitFor(1000)
|
||||
lastErr = err
|
||||
}
|
||||
}
|
||||
console.error("TIMED OUT CHECK: ", {regex, content, lastErr})
|
||||
|
||||
if (hardError) {
|
||||
throw new Error("TIMED OUT: " + regex + "\n\n" + content)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
export class File {
|
||||
path: string
|
||||
originalContent: any
|
||||
constructor(path: string) {
|
||||
this.path = path
|
||||
this.originalContent = existsSync(this.path) ? readFileSync(this.path, "utf8") : null
|
||||
}
|
||||
|
||||
write(content: any) {
|
||||
if (!this.originalContent) {
|
||||
this.originalContent = content
|
||||
}
|
||||
writeFileSync(this.path, content, "utf8")
|
||||
}
|
||||
|
||||
replace(pattern: any, newValue: any) {
|
||||
const currentContent = readFileSync(this.path, "utf8")
|
||||
if (pattern instanceof RegExp) {
|
||||
if (!pattern.test(currentContent)) {
|
||||
throw new Error(
|
||||
`Failed to replace content.\n\nPattern: ${pattern.toString()}\n\nContent: ${currentContent}`,
|
||||
)
|
||||
}
|
||||
} else if (typeof pattern === "string") {
|
||||
if (!currentContent.includes(pattern)) {
|
||||
throw new Error(
|
||||
`Failed to replace content.\n\nPattern: ${pattern}\n\nContent: ${currentContent}`,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
throw new Error(`Unknown replacement attempt type: ${pattern}`)
|
||||
}
|
||||
|
||||
const newContent = currentContent.replace(pattern, newValue)
|
||||
this.write(newContent)
|
||||
}
|
||||
|
||||
delete() {
|
||||
unlinkSync(this.path)
|
||||
}
|
||||
|
||||
restore() {
|
||||
this.write(this.originalContent)
|
||||
}
|
||||
}
|
||||
|
||||
export async function evaluate(browser: any, input: any) {
|
||||
if (typeof input === "function") {
|
||||
const result = await browser.executeScript(input)
|
||||
await new Promise((resolve) => setTimeout(resolve, 30))
|
||||
return result
|
||||
} else {
|
||||
throw new Error(`You must pass a function to be evaluated in the browser.`)
|
||||
}
|
||||
}
|
||||
|
||||
export async function retry(fn: Function, duration = 3000, interval = 500, description: string) {
|
||||
if (duration % interval !== 0) {
|
||||
throw new Error(
|
||||
`invalid duration ${duration} and interval ${interval} mix, duration must be evenly divisible by interval`,
|
||||
)
|
||||
}
|
||||
|
||||
for (let i = duration; i >= 0; i -= interval) {
|
||||
try {
|
||||
return await fn()
|
||||
} catch (err) {
|
||||
if (i === 0) {
|
||||
console.error(`Failed to retry${description ? ` ${description}` : ""} within ${duration}ms`)
|
||||
throw err
|
||||
}
|
||||
console.warn(`Retrying${description ? ` ${description}` : ""} in ${interval}ms`)
|
||||
await waitFor(interval)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function hasRedbox(browser: any, expected = true) {
|
||||
let attempts = 30
|
||||
do {
|
||||
const has = await evaluate(browser, () => {
|
||||
return Boolean(
|
||||
[].slice
|
||||
.call(document.querySelectorAll("nextjs-portal"))
|
||||
.find((p: any) =>
|
||||
p.shadowRoot.querySelector(
|
||||
"#nextjs__container_errors_label, #nextjs__container_build_error_label",
|
||||
),
|
||||
),
|
||||
)
|
||||
})
|
||||
if (has) {
|
||||
return true
|
||||
}
|
||||
if (--attempts < 0) {
|
||||
break
|
||||
}
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000))
|
||||
} while (expected)
|
||||
return false
|
||||
}
|
||||
|
||||
export async function getRedboxHeader(browser: any) {
|
||||
return retry(
|
||||
() =>
|
||||
evaluate(browser, () => {
|
||||
const portal = [].slice
|
||||
.call(document.querySelectorAll("nextjs-portal"))
|
||||
.find((p: any) => p.shadowRoot.querySelector("[data-nextjs-dialog-header"))
|
||||
const root = portal.shadowRoot
|
||||
return root
|
||||
.querySelector("[data-nextjs-dialog-header]")
|
||||
.innerText.replace(/__WEBPACK_DEFAULT_EXPORT__/, "Unknown")
|
||||
}),
|
||||
3000,
|
||||
500,
|
||||
"getRedboxHeader",
|
||||
)
|
||||
}
|
||||
|
||||
export async function getRedboxSource(browser: any) {
|
||||
return retry(
|
||||
() =>
|
||||
evaluate(browser, () => {
|
||||
const portal = [].slice
|
||||
.call(document.querySelectorAll("nextjs-portal"))
|
||||
.find((p: any) =>
|
||||
p.shadowRoot.querySelector(
|
||||
"#nextjs__container_errors_label, #nextjs__container_build_error_label",
|
||||
),
|
||||
)
|
||||
const root = portal.shadowRoot
|
||||
return root
|
||||
.querySelector("[data-nextjs-codeframe], [data-nextjs-terminal]")
|
||||
.innerText.replace(/__WEBPACK_DEFAULT_EXPORT__/, "Unknown")
|
||||
}),
|
||||
3000,
|
||||
500,
|
||||
"getRedboxSource",
|
||||
)
|
||||
}
|
||||
|
||||
export function getBrowserBodyText(browser: any) {
|
||||
return browser.eval('document.getElementsByTagName("body")[0].innerText')
|
||||
}
|
||||
|
||||
export function normalizeRegEx(src: string) {
|
||||
return new RegExp(src).source.replace(/\^\//g, "^\\/")
|
||||
}
|
||||
|
||||
function readJson(path: string) {
|
||||
return JSON.parse(readFileSync(path) as any)
|
||||
}
|
||||
|
||||
export function getBuildManifest(dir: string) {
|
||||
return readJson(path.join(dir, ".next/build-manifest.json"))
|
||||
}
|
||||
|
||||
export function getPageFileFromBuildManifest(dir: string, page: string) {
|
||||
const buildManifest = getBuildManifest(dir)
|
||||
const pageFiles = buildManifest.pages[page]
|
||||
if (!pageFiles) {
|
||||
throw new Error(`No files for page ${page}`)
|
||||
}
|
||||
|
||||
const pageFile = pageFiles.find(
|
||||
(file: string) =>
|
||||
file.endsWith(".js") && file.includes(`pages${page === "" ? "/index" : page}`),
|
||||
)
|
||||
if (!pageFile) {
|
||||
throw new Error(`No page file for page ${page}`)
|
||||
}
|
||||
|
||||
return pageFile
|
||||
}
|
||||
|
||||
export function readBlitzBuildClientPageFile(appDir: string, page: string) {
|
||||
const pageFile = getPageFileFromBuildManifest(appDir, page)
|
||||
return readFileSync(path.join(appDir, ".next", pageFile), "utf8")
|
||||
}
|
||||
|
||||
export function getPagesManifest(dir: string) {
|
||||
const serverFile = path.join(dir, ".next/server/pages-manifest.json")
|
||||
|
||||
if (existsSync(serverFile)) {
|
||||
return readJson(serverFile)
|
||||
}
|
||||
return readJson(path.join(dir, ".next/serverless/pages-manifest.json"))
|
||||
}
|
||||
|
||||
export function updatePagesManifest(dir: string, content: any) {
|
||||
const serverFile = path.join(dir, ".next/server/pages-manifest.json")
|
||||
|
||||
if (existsSync(serverFile)) {
|
||||
return writeFile(serverFile, content)
|
||||
}
|
||||
return writeFile(path.join(dir, ".next/serverless/pages-manifest.json"), content)
|
||||
}
|
||||
|
||||
export function getPageFileFromPagesManifest(dir: string, page: string) {
|
||||
const pagesManifest = getPagesManifest(dir)
|
||||
const pageFile = pagesManifest[page]
|
||||
if (!pageFile) {
|
||||
throw new Error(`No file for page ${page}`)
|
||||
}
|
||||
|
||||
return pageFile
|
||||
}
|
||||
|
||||
export function readBlitzBuildServerPageFile(appDir: string, page: string) {
|
||||
const pageFile = getPageFileFromPagesManifest(appDir, page)
|
||||
return readFileSync(path.join(appDir, ".next", "server", pageFile), "utf8")
|
||||
}
|
||||
11
test/tsconfig.json
Normal file
11
test/tsconfig.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
/* "module": "esnext", */
|
||||
/* "target": "esnext", */
|
||||
"allowJs": true,
|
||||
"baseUrl": "./lib",
|
||||
"resolveJsonModule": true,
|
||||
"noEmit": true
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"include": ["types/**/*", "packages/**/*", "recipes/**/*"],
|
||||
"include": ["types/**/*", "packages/**/*", "recipes/**/*", "test/**/*"],
|
||||
"exclude": []
|
||||
}
|
||||
|
||||
24
yarn.lock
24
yarn.lock
@@ -3131,10 +3131,10 @@
|
||||
resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.11.tgz#aeb16f50649a91af79dbe36574b66d0f9e4d9f71"
|
||||
integrity sha512-3NsZsJIA/22P3QUyrEDNA2D133H4j224twJrdipXN38dpnIOzAbUDtOwkcJ5pXmn75w7LSQDjA4tO9dm1XlqlA==
|
||||
|
||||
"@preconstruct/cli@2.0.5":
|
||||
version "2.0.5"
|
||||
resolved "https://registry.yarnpkg.com/@preconstruct/cli/-/cli-2.0.5.tgz#bd952ecae0fbe5cba37040d7f05c41cab3ac4250"
|
||||
integrity sha512-yWB0GwqZi9tTpSmsEkcVYxnieCT89L9938XR7s9ffjKf59GFMwy8O0G8wMA8Rm6jNjf3ob5zuVWvCaoHppUS8Q==
|
||||
"@preconstruct/cli@2.0.7":
|
||||
version "2.0.7"
|
||||
resolved "https://registry.yarnpkg.com/@preconstruct/cli/-/cli-2.0.7.tgz#368b0313bc3e04da2442e0133d7bdc3a076a3a55"
|
||||
integrity sha512-xXKbIZa5k39fLs3ufLo2/PgZjQK/ZBzUeK0nFt+t6xE3i++e6y/RN8GNNzGxOgwgM6+m+OL7rB54ruwB/HVWqw==
|
||||
dependencies:
|
||||
"@babel/code-frame" "^7.5.5"
|
||||
"@babel/core" "^7.7.7"
|
||||
@@ -3184,13 +3184,6 @@
|
||||
pirates "^4.0.1"
|
||||
source-map-support "^0.5.16"
|
||||
|
||||
"@preconstruct/next@2.0.0":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@preconstruct/next/-/next-2.0.0.tgz#050042c13dde0c671bee0681acb49c31e80ab415"
|
||||
integrity sha512-jpNffjgVKSilBCi3tNs2MEqqGdQBOo5n97B9OCfMDqO9SoiH7MyCmQ+tHCYQvY5gmD6Bf3Fas79N7Rzj6vJBsQ==
|
||||
dependencies:
|
||||
resolve "^1.17.0"
|
||||
|
||||
"@prisma/client@2.19.0":
|
||||
version "2.19.0"
|
||||
resolved "https://registry.yarnpkg.com/@prisma/client/-/client-2.19.0.tgz#a45f17a59fd109e95b61bf4b56d4a7642169ec0e"
|
||||
@@ -3879,6 +3872,13 @@
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/get-port@4.2.0":
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/get-port/-/get-port-4.2.0.tgz#4fc44616c737d37d3ee7926d86fa975d0afba5e4"
|
||||
integrity sha512-Iv2FAb5RnIk/eFO2CTu8k+0VMmIR15pKbcqRWi+s3ydW+aKXlN2yemP92SrO++ERyJx+p6Ie1ggbLBMbU1SjiQ==
|
||||
dependencies:
|
||||
get-port "*"
|
||||
|
||||
"@types/glob-stream@*":
|
||||
version "6.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/glob-stream/-/glob-stream-6.1.0.tgz#7ede8a33e59140534f8d8adfb8ac9edfb31897bc"
|
||||
@@ -10665,7 +10665,7 @@ get-pkg-repo@^1.0.0:
|
||||
parse-github-repo-url "^1.3.0"
|
||||
through2 "^2.0.0"
|
||||
|
||||
get-port@5.1.1, get-port@^5.1.1:
|
||||
get-port@*, get-port@5.1.1, get-port@^5.1.1:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.yarnpkg.com/get-port/-/get-port-5.1.1.tgz#0469ed07563479de6efb986baf053dcd7d4e3193"
|
||||
integrity sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==
|
||||
|
||||
Reference in New Issue
Block a user