Compare commits
5 Commits
use-author
...
new-resolv
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4998c98372 | ||
|
|
f6873ceb78 | ||
|
|
74a662c804 | ||
|
|
7f3eb3eb2b | ||
|
|
958eb00b69 |
@@ -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]})
|
||||
|
||||
|
||||
15
examples/auth/utils/logger.ts
Normal file
15
examples/auth/utils/logger.ts
Normal 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",
|
||||
})
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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')],
|
||||
|
||||
454
packages/babel-preset/src/resolver-code-elimination.ts
Normal file
454
packages/babel-preset/src/resolver-code-elimination.ts
Normal 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);
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
150
packages/babel-preset/src/rpc-server-transform.ts
Normal file
150
packages/babel-preset/src/rpc-server-transform.ts
Normal 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
9
packages/babel-preset/src/types.d.ts
vendored
Normal 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;
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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(".")
|
||||
// }
|
||||
|
||||
@@ -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") {
|
||||
|
||||
@@ -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==
|
||||
|
||||
Reference in New Issue
Block a user