1
0
mirror of synced 2026-02-03 18:01:02 -05:00

Compare commits

...

5 Commits

Author SHA1 Message Date
Brandon Bayer
4998c98372 wip to remove server babel transform 2021-02-04 12:05:45 -05:00
Brandon Bayer
f6873ceb78 add local middleware support to server transform 2021-02-02 18:46:06 -05:00
Brandon Bayer
74a662c804 working server rpc transform 2021-02-02 18:19:12 -05:00
Brandon Bayer
7f3eb3eb2b keep query/mutation original contents in place 2021-01-31 20:25:15 -05:00
Brandon Bayer
958eb00b69 add some debug stuff to auth example 2021-01-31 20:17:07 -05:00
13 changed files with 791 additions and 105 deletions

View File

@@ -1,5 +1,6 @@
import {resolver, SecurePassword, AuthenticationError} from "blitz"
import db from "db"
// import {logger} from "utils/logger"
import * as z from "zod"
export const authenticateUser = async (email: string, password: string) => {
@@ -24,8 +25,10 @@ export const LoginInput = z.object({
})
export default resolver.pipe(resolver.zod(LoginInput), async ({email, password}, {session}) => {
// logger.debug("Starting login...")
// This throws an error if credentials are invalid
const user = await authenticateUser(email, password)
// logger.debug("Authenticated user", {user})
await session.$create({userId: user.id, roles: [user.role]})

View File

@@ -0,0 +1,15 @@
import {Logger} from "tslog"
export const logger = new Logger({
type: "pretty",
dateTimePattern: "hour:minute:second.millisecond",
displayFunctionName: false,
displayFilePath: "hidden",
dateTimeTimezone:
process.env.NODE_ENV === "production"
? "utc"
: Intl.DateTimeFormat().resolvedOptions().timeZone,
prettyInspectHighlightStyles: {name: "yellow", number: "blue", bigint: "blue", boolean: "blue"},
maskValuesOfKeys: ["password", "passwordConfirmation"],
exposeErrorCodeFrame: process.env.NODE_ENV !== "production",
})

View File

@@ -36,6 +36,7 @@
}
],
"dependencies": {
"babel-plugin-superjson-next": "0.2.1"
"babel-plugin-superjson-next": "0.2.1",
"@babel/helper-module-imports": "7.12.5"
}
}

View File

@@ -1,5 +1,9 @@
// import RpcServerTranform from './rpc-server-transform';
// eslint-disable-next-line import/no-default-export
export default function preset(_api: any, options = {}) {
// const isServer = _api.caller((caller: any) => !!caller && caller.isServer);
// console.log('IS SERVER', isServer);
return {
presets: [[require('next/babel'), options]],
plugins: [require('babel-plugin-superjson-next')],

View File

@@ -0,0 +1,454 @@
import { PluginObj, types as BabelTypes } from '@babel/core';
import { NodePath } from '@babel/traverse';
export const STATIC_PROPS_ID = '__N_SSG';
export const SERVER_PROPS_ID = '__N_SSP';
export const EXPORT_NAME_GET_STATIC_PROPS = 'getStaticProps';
export const EXPORT_NAME_GET_STATIC_PATHS = 'getStaticPaths';
export const EXPORT_NAME_GET_SERVER_PROPS = 'getServerSideProps';
const ssgExports = new Set([
EXPORT_NAME_GET_STATIC_PROPS,
EXPORT_NAME_GET_STATIC_PATHS,
EXPORT_NAME_GET_SERVER_PROPS,
// legacy methods added so build doesn't fail from importing
// server-side only methods
`unstable_getStaticProps`,
`unstable_getStaticPaths`,
`unstable_getServerProps`,
`unstable_getServerSideProps`,
]);
type PluginState = {
refs: Set<NodePath<BabelTypes.Identifier>>;
isPrerender: boolean;
isServerProps: boolean;
done: boolean;
};
function decorateSsgExport(
t: typeof BabelTypes,
path: NodePath<BabelTypes.Program>,
state: PluginState
): void {
const gsspName = state.isPrerender ? STATIC_PROPS_ID : SERVER_PROPS_ID;
const gsspId = t.identifier(gsspName);
const addGsspExport = (
exportPath:
| NodePath<BabelTypes.ExportDefaultDeclaration>
| NodePath<BabelTypes.ExportNamedDeclaration>
): void => {
if (state.done) {
return;
}
state.done = true;
const [pageCompPath] = exportPath.replaceWithMultiple([
t.exportNamedDeclaration(
t.variableDeclaration(
// We use 'var' instead of 'let' or 'const' for ES5 support. Since
// this runs in `Program#exit`, no ES2015 transforms (preset env)
// will be ran against this code.
'var',
[t.variableDeclarator(gsspId, t.booleanLiteral(true))]
),
[t.exportSpecifier(gsspId, gsspId)]
),
exportPath.node,
]);
exportPath.scope.registerDeclaration(
pageCompPath as NodePath<BabelTypes.Node>
);
};
path.traverse({
ExportDefaultDeclaration(exportDefaultPath) {
addGsspExport(exportDefaultPath);
},
ExportNamedDeclaration(exportNamedPath) {
addGsspExport(exportNamedPath);
},
});
}
const isDataIdentifier = (name: string, state: PluginState): boolean => {
if (ssgExports.has(name)) {
if (name === EXPORT_NAME_GET_SERVER_PROPS) {
if (state.isPrerender) {
throw new Error('SERVER_PROPS_SSG_CONFLICT');
}
state.isServerProps = true;
} else {
if (state.isServerProps) {
throw new Error('SERVER_PROPS_SSG_CONFLICT');
}
state.isPrerender = true;
}
return true;
}
return false;
};
// eslint-disable-next-line import/no-default-export
export default function nextTransformSsg({
types: t,
}: {
types: typeof BabelTypes;
}): PluginObj<PluginState> {
function getIdentifier(
path:
| NodePath<BabelTypes.FunctionDeclaration>
| NodePath<BabelTypes.FunctionExpression>
| NodePath<BabelTypes.ArrowFunctionExpression>
): NodePath<BabelTypes.Identifier> | null {
const parentPath = path.parentPath;
if (parentPath.type === 'VariableDeclarator') {
const pp = parentPath as NodePath<BabelTypes.VariableDeclarator>;
const name = pp.get('id');
return name.node.type === 'Identifier'
? (name as NodePath<BabelTypes.Identifier>)
: null;
}
if (parentPath.type === 'AssignmentExpression') {
const pp = parentPath as NodePath<BabelTypes.AssignmentExpression>;
const name = pp.get('left');
return name.node.type === 'Identifier'
? (name as NodePath<BabelTypes.Identifier>)
: null;
}
if (path.node.type === 'ArrowFunctionExpression') {
return null;
}
return path.node.id && path.node.id.type === 'Identifier'
? (path.get('id') as NodePath<BabelTypes.Identifier>)
: null;
}
function isIdentifierReferenced(
ident: NodePath<BabelTypes.Identifier>
): boolean {
const b = ident.scope.getBinding(ident.node.name);
if (b?.referenced) {
// Functions can reference themselves, so we need to check if there's a
// binding outside the function scope or not.
if (b.path.type === 'FunctionDeclaration') {
return !b.constantViolations
.concat(b.referencePaths)
// Check that every reference is contained within the function:
.every((ref) => ref.findParent((p) => p === b.path));
}
return true;
}
return false;
}
function markFunction(
path:
| NodePath<BabelTypes.FunctionDeclaration>
| NodePath<BabelTypes.FunctionExpression>
| NodePath<BabelTypes.ArrowFunctionExpression>,
state: PluginState
): void {
const ident = getIdentifier(path);
if (ident?.node && isIdentifierReferenced(ident)) {
state.refs.add(ident);
}
}
function markImport(
path:
| NodePath<BabelTypes.ImportSpecifier>
| NodePath<BabelTypes.ImportDefaultSpecifier>
| NodePath<BabelTypes.ImportNamespaceSpecifier>,
state: PluginState
): void {
const local = path.get('local') as NodePath<BabelTypes.Identifier>;
if (isIdentifierReferenced(local)) {
state.refs.add(local);
}
}
return {
visitor: {
Program: {
enter(path, state) {
state.refs = new Set<NodePath<BabelTypes.Identifier>>();
state.isPrerender = false;
state.isServerProps = false;
state.done = false;
path.traverse(
{
VariableDeclarator(variablePath, variableState) {
if (variablePath.node.id.type === 'Identifier') {
const local = variablePath.get(
'id'
) as NodePath<BabelTypes.Identifier>;
if (isIdentifierReferenced(local)) {
variableState.refs.add(local);
}
} else if (variablePath.node.id.type === 'ObjectPattern') {
const pattern = variablePath.get(
'id'
) as NodePath<BabelTypes.ObjectPattern>;
const properties = pattern.get('properties');
properties.forEach((p) => {
const local = p.get(
p.node.type === 'ObjectProperty'
? 'value'
: p.node.type === 'RestElement'
? 'argument'
: (function () {
throw new Error('invariant');
})()
) as NodePath<BabelTypes.Identifier>;
if (isIdentifierReferenced(local)) {
variableState.refs.add(local);
}
});
} else if (variablePath.node.id.type === 'ArrayPattern') {
const pattern = variablePath.get(
'id'
) as NodePath<BabelTypes.ArrayPattern>;
const elements = pattern.get('elements');
elements.forEach((e) => {
let local: NodePath<BabelTypes.Identifier>;
if (e.node?.type === 'Identifier') {
local = e as NodePath<BabelTypes.Identifier>;
} else if (e.node?.type === 'RestElement') {
local = e.get(
'argument'
) as NodePath<BabelTypes.Identifier>;
} else {
return;
}
if (isIdentifierReferenced(local)) {
variableState.refs.add(local);
}
});
}
},
FunctionDeclaration: markFunction,
FunctionExpression: markFunction,
ArrowFunctionExpression: markFunction,
ImportSpecifier: markImport,
ImportDefaultSpecifier: markImport,
ImportNamespaceSpecifier: markImport,
ExportNamedDeclaration(exportNamedPath, exportNamedState) {
const specifiers = exportNamedPath.get('specifiers');
if (specifiers.length) {
specifiers.forEach((s) => {
if (
isDataIdentifier(
t.isIdentifier(s.node.exported)
? s.node.exported.name
: s.node.exported.value,
exportNamedState
)
) {
s.remove();
}
});
if (exportNamedPath.node.specifiers.length < 1) {
exportNamedPath.remove();
}
return;
}
const decl = exportNamedPath.get('declaration') as NodePath<
| BabelTypes.FunctionDeclaration
| BabelTypes.VariableDeclaration
>;
if (decl == null || decl.node == null) {
return;
}
switch (decl.node.type) {
case 'FunctionDeclaration': {
const name = decl.node.id!.name;
if (isDataIdentifier(name, exportNamedState)) {
exportNamedPath.remove();
}
break;
}
case 'VariableDeclaration': {
const inner = decl.get(
'declarations'
) as NodePath<BabelTypes.VariableDeclarator>[];
inner.forEach((d) => {
if (d.node.id.type !== 'Identifier') {
return;
}
const name = d.node.id.name;
if (isDataIdentifier(name, exportNamedState)) {
d.remove();
}
});
break;
}
default: {
break;
}
}
},
},
state
);
// if (!state.isPrerender && !state.isServerProps) {
// return;
// }
const refs = state.refs;
let count: number;
function sweepFunction(
sweepPath:
| NodePath<BabelTypes.FunctionDeclaration>
| NodePath<BabelTypes.FunctionExpression>
| NodePath<BabelTypes.ArrowFunctionExpression>
): void {
const ident = getIdentifier(sweepPath);
if (
ident?.node &&
refs.has(ident) &&
!isIdentifierReferenced(ident)
) {
++count;
if (
t.isAssignmentExpression(sweepPath.parentPath) ||
t.isVariableDeclarator(sweepPath.parentPath)
) {
sweepPath.parentPath.remove();
} else {
sweepPath.remove();
}
}
}
function sweepImport(
sweepPath:
| NodePath<BabelTypes.ImportSpecifier>
| NodePath<BabelTypes.ImportDefaultSpecifier>
| NodePath<BabelTypes.ImportNamespaceSpecifier>
): void {
const local = sweepPath.get(
'local'
) as NodePath<BabelTypes.Identifier>;
if (refs.has(local) && !isIdentifierReferenced(local)) {
++count;
sweepPath.remove();
if (
(sweepPath.parent as BabelTypes.ImportDeclaration).specifiers
.length === 0
) {
sweepPath.parentPath.remove();
}
}
}
do {
(path.scope as any).crawl();
count = 0;
path.traverse({
// eslint-disable-next-line no-loop-func
VariableDeclarator(variablePath) {
if (variablePath.node.id.type === 'Identifier') {
const local = variablePath.get(
'id'
) as NodePath<BabelTypes.Identifier>;
if (refs.has(local) && !isIdentifierReferenced(local)) {
++count;
variablePath.remove();
}
} else if (variablePath.node.id.type === 'ObjectPattern') {
const pattern = variablePath.get(
'id'
) as NodePath<BabelTypes.ObjectPattern>;
const beforeCount = count;
const properties = pattern.get('properties');
properties.forEach((p) => {
const local = p.get(
p.node.type === 'ObjectProperty'
? 'value'
: p.node.type === 'RestElement'
? 'argument'
: (function () {
throw new Error('invariant');
})()
) as NodePath<BabelTypes.Identifier>;
if (refs.has(local) && !isIdentifierReferenced(local)) {
++count;
p.remove();
}
});
if (
beforeCount !== count &&
pattern.get('properties').length < 1
) {
variablePath.remove();
}
} else if (variablePath.node.id.type === 'ArrayPattern') {
const pattern = variablePath.get(
'id'
) as NodePath<BabelTypes.ArrayPattern>;
const beforeCount = count;
const elements = pattern.get('elements');
elements.forEach((e) => {
let local: NodePath<BabelTypes.Identifier>;
if (e.node?.type === 'Identifier') {
local = e as NodePath<BabelTypes.Identifier>;
} else if (e.node?.type === 'RestElement') {
local = e.get(
'argument'
) as NodePath<BabelTypes.Identifier>;
} else {
return;
}
if (refs.has(local) && !isIdentifierReferenced(local)) {
++count;
e.remove();
}
});
if (
beforeCount !== count &&
pattern.get('elements').length < 1
) {
variablePath.remove();
}
}
},
FunctionDeclaration: sweepFunction,
FunctionExpression: sweepFunction,
ArrowFunctionExpression: sweepFunction,
ImportSpecifier: sweepImport,
ImportDefaultSpecifier: sweepImport,
ImportNamespaceSpecifier: sweepImport,
});
} while (count);
decorateSsgExport(t, path, state);
},
},
},
};
}

View File

@@ -0,0 +1,150 @@
import { NodePath, PluginObj, PluginPass, types as t } from '@babel/core';
import { addNamed as addNamedImport } from '@babel/helper-module-imports';
import * as nodePath from 'path';
/*
* Probably out of date, but working AST Explorer playground with dependencies inlined:
* https://astexplorer.net/#/gist/77689180624bb32e93745eb371f989da/55763c62f802bd0a69531e6e979b3ac446f4b821
*/
function functionDeclarationToExpression(declaration: t.FunctionDeclaration) {
return t.functionExpression(
declaration.id,
declaration.params,
declaration.body,
declaration.generator,
declaration.async
);
}
function classDeclarationToExpression(declaration: t.ClassDeclaration) {
return t.classExpression(
declaration.id,
declaration.superClass,
declaration.body,
declaration.decorators
);
}
function getFileName(state: PluginPass) {
const { filename, cwd } = state;
if (!filename) {
return undefined;
}
if (cwd && filename.startsWith(cwd)) {
return filename.slice(cwd.length);
}
return filename;
}
function addWithSuperJSONPageImport(path: NodePath<any>) {
return addNamedImport(path, 'enhanceResolver', '@blitzjs/core');
}
function extractResolverMetadata(filePath: string) {
const [, typePlural, name] =
/(queries|mutations)\/(.*)$/.exec(filePath) || [];
const type /*: ResolverType*/ =
typePlural === 'mutations' ? 'mutation' : 'query';
return {
type,
name,
apiPath: filePath.replace(/^\/app/, 'pages/api'),
};
}
function wrapInHOF(
path: NodePath<any>,
expr: t.Expression,
filePath: string
): t.Expression {
const data = extractResolverMetadata(filePath);
const program = path.findParent(t.isProgram) as NodePath<t.Program>;
if (!program) throw new Error('Missing parent');
const hasMiddlewareExport = program.node.body.find((path) => {
if (!t.isExportNamedDeclaration(path)) return null;
if (!path.declaration) return null;
if (!t.isVariableDeclaration(path.declaration)) return null;
const variableDeclarator = path.declaration.declarations.find((path) =>
t.isVariableDeclarator(path)
);
if (!variableDeclarator) return null;
return (variableDeclarator.id as any).name === 'middleware';
});
const metadataProperties = [
t.objectProperty(t.identifier('name'), t.stringLiteral(data.name)),
t.objectProperty(t.identifier('type'), t.stringLiteral(data.type)),
t.objectProperty(t.identifier('filePath'), t.stringLiteral(filePath)),
];
if (hasMiddlewareExport) {
metadataProperties.push(
t.objectProperty(t.identifier('middleware'), t.identifier('middleware'))
);
}
return t.callExpression(addWithSuperJSONPageImport(path), [
expr,
t.objectExpression(metadataProperties),
]);
}
function wrapExportDefaultDeclaration(path: NodePath<any>, filePath: string) {
const { node } = path;
if (
t.isIdentifier(node.declaration) ||
t.isFunctionExpression(node.declaration) ||
t.isCallExpression(node.declaration)
) {
node.declaration = wrapInHOF(path, node.declaration, filePath);
} else if (
t.isFunctionDeclaration(node.declaration) ||
t.isClassDeclaration(node.declaration)
) {
if (node.declaration.id) {
path.insertBefore(node.declaration);
node.declaration = wrapInHOF(path, node.declaration.id, filePath);
} else {
if (t.isFunctionDeclaration(node.declaration)) {
node.declaration = wrapInHOF(
path,
functionDeclarationToExpression(node.declaration),
filePath
);
} else {
node.declaration = wrapInHOF(
path,
classDeclarationToExpression(node.declaration),
filePath
);
}
}
}
}
function RpcServerTranform(): PluginObj {
return {
name: 'RpcServerTranform',
visitor: {
ExportDefaultDeclaration(path, state) {
const filePath =
getFileName(state) ?? nodePath.join('pages', 'Default.js');
if (!filePath.match(/app.*\/(queries|mutations)\//)) {
return;
}
wrapExportDefaultDeclaration(path, filePath);
},
},
};
}
// eslint-disable-next-line import/no-default-export
export default RpcServerTranform;

9
packages/babel-preset/src/types.d.ts vendored Normal file
View File

@@ -0,0 +1,9 @@
declare module '@babel/helper-module-imports' {
import { NodePath, types } from '@babel/core';
function addNamed(
path: NodePath,
named: string,
source: string
): types.Identifier;
}

View File

@@ -12,7 +12,7 @@ export {withRouter, RouterContext, BlitzRouter} from "./with-router"
export {useRouter} from "./use-router"
export {useRouterQuery} from "./use-router-query"
export {passportAuth} from "./passport-adapter"
export {getIsomorphicEnhancedResolver} from "./rpc"
export {getIsomorphicEnhancedResolver, enhanceResolver} from "./rpc"
export {useMutation} from "./use-mutation"
export {invoke, invokeWithMiddleware} from "./invoke"
export {getBlitzRuntimeData} from "./blitz-data"

View File

@@ -15,6 +15,8 @@ import {
CancellablePromise,
EnhancedResolver,
EnhancedResolverRpcClient,
Middleware,
Resolver,
ResolverModule,
ResolverRpc,
ResolverType,
@@ -156,8 +158,8 @@ executeRpcCall.warm = (apiUrl: string) => {
return window.fetch(apiUrl, {method: "HEAD"})
}
const getApiUrlFromResolverFilePath = (resolverFilePath: string) =>
resolverFilePath.replace(/^app\/_resolvers/, "/api")
const getApiPathFromResolverFilePath = (resolverFilePath: string) =>
resolverFilePath.replace(/^\/app/, "/api")
type IsomorphicEnhancedResolverOptions = {
warmApiEndpoints?: boolean
@@ -203,24 +205,24 @@ export function getIsomorphicEnhancedResolver<TInput, TResult>(
target: "server" | "client" = isClient ? "client" : "server",
options: IsomorphicEnhancedResolverOptions = {},
): EnhancedResolver<TInput, TResult> | EnhancedResolverRpcClient<TInput, TResult> {
const apiUrl = getApiUrlFromResolverFilePath(resolverFilePath)
const apiPath = getApiPathFromResolverFilePath(resolverFilePath)
if (target === "client") {
const resolverRpc: ResolverRpc<TInput, TResult> = (params, opts) =>
executeRpcCall(apiUrl, params, opts)
executeRpcCall(apiPath, params, opts)
const enhancedResolverRpcClient = resolverRpc as EnhancedResolverRpcClient<TInput, TResult>
enhancedResolverRpcClient._meta = {
name: resolverName,
type: resolverType,
filePath: resolverFilePath,
apiUrl: apiUrl,
apiPath: apiPath,
}
// Warm the lambda
if (options.warmApiEndpoints) {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
executeRpcCall.warm(apiUrl)
executeRpcCall.warm(apiPath)
}
return enhancedResolverRpcClient
@@ -232,8 +234,35 @@ export function getIsomorphicEnhancedResolver<TInput, TResult>(
name: resolverName,
type: resolverType,
filePath: resolverFilePath,
apiUrl: apiUrl,
apiPath: apiPath,
}
return enhancedResolver
}
}
function extractResolverMetadata(filePath: string) {
const [, typePlural, name] = /(queries|mutations)\/(.*)$/.exec(filePath) || []
const type: ResolverType = typePlural === "mutations" ? "mutation" : "query"
return {
name,
type,
filePath,
apiPath: filePath.replace(/^app/, "pages/api"),
}
}
interface EnhancedResolverData {
filePath: string
middleware?: Middleware[]
}
export function enhanceResolver<TInput, TResult>(
resolver: Resolver<TInput, TResult>,
data: EnhancedResolverData,
) {
const enhancedResolver = resolver as EnhancedResolver<TInput, TResult>
enhancedResolver.middleware = data.middleware
enhancedResolver._meta = extractResolverMetadata(data.filePath)
return enhancedResolver
}

View File

@@ -126,7 +126,7 @@ export type VerifyCallbackResult = {
}
// The actual resolver source definition
export type Resolver<TInput, TResult> = (input: TInput, ctx?: any) => Promise<TResult>
export type Resolver<TInput, TResult> = (input: TInput, ctx?: any) => TResult | Promise<TResult>
// Resolver type when imported with require()
export type ResolverModule<TInput, TResult> = {
@@ -147,8 +147,8 @@ export type ResolverRpc<TInput, TResult> = (
) => CancellablePromise<TResult>
export interface ResolverRpcExecutor<TInput, TResult> {
(apiUrl: string, params: TInput, opts?: RpcOptions): CancellablePromise<TResult>
warm: (apiUrl: string) => undefined | Promise<unknown>
(apiPath: string, params: TInput, opts?: RpcOptions): CancellablePromise<TResult>
warm: (apiPath: string) => undefined | Promise<unknown>
}
export type ResolverType = "query" | "mutation"
@@ -158,7 +158,7 @@ export interface ResolverEnhancement {
name: string
type: ResolverType
filePath: string
apiUrl: string
apiPath: string
}
}

View File

@@ -1,5 +1,5 @@
import {getConfig} from "@blitzjs/config"
import {ResolverType} from "@blitzjs/core"
// import {getConfig} from "@blitzjs/config"
// import {ResolverType} from "@blitzjs/core"
import {Stage, transform} from "@blitzjs/file-pipeline"
import {relative} from "path"
import slash from "slash"
@@ -10,6 +10,7 @@ export function isResolverPath(filePath: string) {
return /(?:app[\\/])(?!_resolvers).*(?:queries|mutations)[\\/].+/.exec(filePath)
}
/*
const isomorhicHandlerTemplateClient = (
resolverFilePath: string,
resolverName: string,
@@ -50,13 +51,16 @@ export default getIsomorphicEnhancedResolver(
}
)
`
*/
// Clarification: try/catch around db is to prevent query errors when not using blitz's inbuilt database (See #572)
const apiHandlerTemplate = (originalPath: string, useTypes: boolean) => `
${useTypes ? "// @ts-nocheck" : ""}
// This imports the output of getIsomorphicEnhancedResolver()
import enhancedResolver from '${originalPath}'
import * as resolverModule from '${originalPath}'
import {getAllMiddlewareForModule} from '@blitzjs/core'
import {rpcApiHandler} from '@blitzjs/server'
import {enhanceResolver} from '@blitzjs/core'
import path from 'path'
// Ensure these files are not eliminated by trace-based tree-shaking (like Vercel)
@@ -65,23 +69,25 @@ path.resolve("blitz.config.js")
path.resolve(".next/__db.js")
// End anti-tree-shaking
let db${useTypes ? ": any" : ""}
let connect${useTypes ? ": any" : ""}
const enhancedResolver = enhanceResolver(resolverModule.default, {
filePath: '${originalPath}',
middleware: resolverModule.middleware,
})
let connectFn = () => {}
try {
db = require('db').default
if (require('db').connect) {
connect = require('db').connect
} else if (db?.$connect || db?.connect) {
connect = () => db.$connect ? db.$connect() : db.connect()
} else {
connect = () => {}
const dbModule = require('db')
if (dbModule?.connect) {
connectFn = dbModule.connect
} else if (dbModule?.default?.$connect) {
connectFn = () => dbModule.default.$connect()
}
} catch(_) {}
export default rpcApiHandler(
enhancedResolver,
getAllMiddlewareForModule(enhancedResolver),
() => db && connect?.(),
getAllMiddlewareForModule(enhancedResolver as any),
connectFn,
)
export const config = {
@@ -98,11 +104,11 @@ export const createStageRpc = (isTypeScript = true): Stage =>
function configure({config: {src}}) {
const fileTransformer = absolutePathTransform(src)
const getResolverPath = fileTransformer(resolverFilePath)
// const getResolverPath = fileTransformer(resolverFilePath)
const getApiHandlerPath = fileTransformer(apiHandlerPath)
const {target}: {target?: string} = getConfig()
const warmApiEndpoints = target?.includes("serverless") ?? false
// const {target}: {target?: string} = getConfig()
// const warmApiEndpoints = target?.includes("serverless") ?? false
const stream = transform.file((file, {next, push}) => {
if (!isResolverPath(file.path)) {
@@ -110,22 +116,25 @@ export const createStageRpc = (isTypeScript = true): Stage =>
}
const originalPath = resolutionPath(src, file.path)
const resolverImportPath = resolverFilePath(originalPath)
const {resolverType, resolverName} = extractTemplateVars(resolverImportPath)
// const resolverImportPath = resolverFilePath(originalPath)
// const {resolverType, resolverName} = extractTemplateVars(resolverImportPath)
// Original function -> _resolvers path
push(
new File({
path: getResolverPath(file.path),
contents: file.contents,
// Appending a new file to the output of this particular stream
// We don't want to reprocess this file but simply add it to the output
// of the stream here we provide a hash with some information for how
// this file came to be here
hash: [file.hash, "rpc", "resolver"].join("|"),
event: "add",
}),
)
// push(
// new File({
// path: getResolverPath(file.path),
// contents: file.contents,
// // Appending a new file to the output of this particular stream
// // We don't want to reprocess this file but simply add it to the output
// // of the stream here we provide a hash with some information for how
// // this file came to be here
// hash: [file.hash, "rpc", "resolver"].join("|"),
// event: "add",
// }),
// )
// Original file
push(file)
// File API route handler
push(
@@ -144,31 +153,31 @@ export const createStageRpc = (isTypeScript = true): Stage =>
)
// Isomorphic client
const isomorphicHandlerFile = file.clone()
isomorphicHandlerFile.contents = Buffer.from(
isomorhicHandlerTemplateServer(
resolverImportPath,
resolverName,
resolverType,
warmApiEndpoints,
),
)
push(isomorphicHandlerFile)
// const isomorphicHandlerFile = file.clone()
// isomorphicHandlerFile.contents = Buffer.from(
// isomorhicHandlerTemplateServer(
// resolverImportPath,
// resolverName,
// resolverType,
// warmApiEndpoints,
// ),
// )
// push(isomorphicHandlerFile)
// Isomorphic client with export
const isomorphicHandlerFileWithExport = file.clone()
isomorphicHandlerFileWithExport.basename = clientResolverBasename(
isomorphicHandlerFileWithExport.basename,
)
isomorphicHandlerFileWithExport.contents = Buffer.from(
isomorhicHandlerTemplateClient(
resolverImportPath,
resolverName,
resolverType,
warmApiEndpoints,
),
)
push(isomorphicHandlerFileWithExport)
// const isomorphicHandlerFileWithExport = file.clone()
// isomorphicHandlerFileWithExport.basename = clientResolverBasename(
// isomorphicHandlerFileWithExport.basename,
// )
// isomorphicHandlerFileWithExport.contents = Buffer.from(
// isomorhicHandlerTemplateClient(
// resolverImportPath,
// resolverName,
// resolverType,
// warmApiEndpoints,
// ),
// )
// push(isomorphicHandlerFileWithExport)
return next()
})
@@ -184,6 +193,7 @@ function resolutionPath(srcPath: string, filePath: string) {
return removeExt(slash(relative(srcPath, filePath)))
}
/*
function extractTemplateVars(resolverImportPath: string) {
const [, resolverTypePlural, resolverName] =
/(queries|mutations)\/(.*)$/.exec(resolverImportPath) || []
@@ -196,10 +206,13 @@ function extractTemplateVars(resolverImportPath: string) {
resolverName,
}
}
*/
/*
function resolverFilePath(path: string) {
return path.replace(/^app/, "app/_resolvers")
}
*/
function apiHandlerPath(path: string) {
return path.replace(/^app/, "pages/api")
@@ -208,10 +221,10 @@ function apiHandlerPath(path: string) {
/**
* "query.ts" => "query.client.ts"
*/
function clientResolverBasename(basename: string) {
const parts = basename.split(".")
parts.splice(parts.length - 1, 0, "client")
return parts.join(".")
}
// function clientResolverBasename(basename: string) {
// const parts = basename.split(".")
//
// parts.splice(parts.length - 1, 0, "client")
//
// return parts.join(".")
// }

View File

@@ -40,36 +40,44 @@ export function withBlitz(nextConfig: any) {
config.module.rules.push({test: excluded, use: {loader: "null-loader"}})
})
if (normalizedConfig.experimental?.isomorphicResolverImports) {
config.plugins.push(
new options.webpack.NormalModuleReplacementPlugin(
/[/\\]?(mutations|queries)[/\\]/,
(resource: any) => {
const request = resource.request as string
if (request.includes("_resolvers")) {
return
}
if (
request.endsWith(".js") ||
request.endsWith(".ts") ||
request.endsWith(".jsx") ||
request.endsWith(".tsx")
) {
return
}
resource.request = resource.request + ".client"
},
),
)
} else {
config.module.rules.push({
issuer: /(mutations|queries)(?!.*\.client)/,
resource: /_resolvers/,
use: {loader: "null-loader"},
})
}
// if (normalizedConfig.experimental?.isomorphicResolverImports) {
// config.plugins.push(
// new options.webpack.NormalModuleReplacementPlugin(
// /[/\\]?(mutations|queries)[/\\]/,
// (resource: any) => {
// const request = resource.request as string
// if (request.includes("_resolvers")) {
// return
// }
//
// if (
// request.endsWith(".js") ||
// request.endsWith(".ts") ||
// request.endsWith(".jsx") ||
// request.endsWith(".tsx")
// ) {
// return
// }
//
// resource.request = resource.request + ".client"
// },
// ),
// )
// // config.module.rules.push({
// // // resource: /(mutations|queries)/,
// // // test: /(mutations|queries)/,
// // test: /.*/,
// // // resource: /_resolvers/,
// // // use: {loader: "null-loader"},
// // sideEffects: false,
// // })
// } else {
// config.module.rules.push({
// issuer: /(mutations|queries)(?!.*\.client)/,
// resource: /_resolvers/,
// use: {loader: "null-loader"},
// })
// }
}
if (typeof normalizedConfig.webpack === "function") {

View File

@@ -199,7 +199,7 @@
dependencies:
"@babel/types" "^7.12.7"
"@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.10.4", "@babel/helper-module-imports@^7.12.1", "@babel/helper-module-imports@^7.12.5":
"@babel/helper-module-imports@7.12.5", "@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.10.4", "@babel/helper-module-imports@^7.12.1", "@babel/helper-module-imports@^7.12.5":
version "7.12.5"
resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz#1bfc0229f794988f76ed0a4d4e90860850b54dfb"
integrity sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA==