MDX syntax highlighting is not working in Next.js - reactjs

When importing the plugin #mapbox/rehype-prism, it doesn't seem to do anything for syntax highlighting as it is supposed to.
//next.config.js
const rehypePrism = require("#mapbox/rehype-prism");
const withMDX = require("#next/mdx")({
extension: /\.mdx?$/,
options: {
rehypePlugins: [rehypePrism],
},
});
module.exports = withMDX({
pageExtensions: ["js", "jsx", "ts", "tsx", "md", "mdx"],
});

Have you solve this problem yet?
It may not be done, so I will write the method :)
anyway import prism.css in _app.tsx (or js, jsx, ts).
prism.css can be downloaded from prismjs.com/download. Before download it, you can select theme and language you use.
This settings is memorized in top of prism.css as url params format, so you can reconfigure theme and language with accesing access with this url.
import React from 'react';
import xxx
import yyy
import '../styles/prism.css`
do you solve?
if no, Please try to solve with following my reference code.
import React from 'react';
import { GetStaticProps } from 'next';
import fs from 'fs';
import path from 'path';
import matter from 'gray-matter';
import { MDXRemote, MDXRemoteSerializeResult } from 'next-mdx-remote';
import { serialize } from 'next-mdx-remote/serialize';
import rehypePrism from '#mapbox/rehype-prism';
import Statics from './next-statics.config';
import MdxComponents from './mdx-components.config';
type MdxPrismProps = {
source: MDXRemoteSerializeResult;
};
const MdxPrism: React.FC<MdxPrismProps> = ({ source }) => {
return <MDXRemote {...source} components={MdxComponents} />;
};
export const getStaticProps: GetStaticProps = async ({ params }) => {
const postFilePath = path.join(Statics.path.posts, `${params.slug}.mdx`);
const source = fs.readFileSync(postFilePath);
const { content } = matter(source);
const mdxSource = await serialize(content, {
mdxOptions: {
rehypePlugins: [rehypePrism],
},
});
return {
props: {
source: mdxSource,
},
};
};
export default MdxPrism;
maybe you can solve to use following part of previous code.
const mdxSource = await serialize(content, {
mdxOptions: {
rehypePlugins: [rehypePrism],
},
});
If this still doesn't work, please find <pre> and <code> in html by developer tools (press F12).
and check these tags have class="language-xxx".

Related

TypeError: supabaseClient.auth.getSession is not a function. Error with NextJS and Supabse

I'm using supabase auth helper package to React and Next in my project. Immediately, when I start the page, I get an error message that says the following:
And it is that I am not calling that supabaseClient.auth.getSession() method. Which makes me believe that the error is internal. i'm just following the documentation: doc. And use the code that appears in the Basic Setup and in Server-side rendering (SSR)
Actually i have this:
`
// pages/_app.js
import { useState } from 'react'
import { useRouter } from 'next/router'
import '../styles/global.css'
import ProfileContext from '../utils/context/ProfileContext'
import useProfile from '../utils/hooks/useProfile'
import ProtectedRoutes from '../utils/constants/rutas/protectedRoutes'
const MyApp = ({ Component, pageProps }) => {
const [supabaseClient] = useState(() => createBrowserSupabaseClient())
console.log('supabaseClient', supabaseClient)
return (
<SessionContextProvider
supabaseClient={supabaseClient}
initialSession={pageProps.initialSession}
>
<ProfileContext.Provider value={useProfile()}>
<Component {...pageProps} />
</ProfileContext.Provider>
</SessionContextProvider>
)
}
export default MyApp
`
`
// pages/index.js
import { createServerSupabaseClient } from '#supabase/auth-helpers-nextjs'
import { useState, useEffect } from "react"
import Layout from "../layout/Layout"
import List from "../components/List"
import Empty from "../components/Empty"
import { supabase } from "../utils/supabaseClient"
const Index = ({ allData }) => {
const [session, setSession] = useState(null)
useEffect(() => {
setSession(supabase.auth.session())
}, [])
return (
<Layout>
{allData.length ? <List allData={allData} /> : <Empty />}
</Layout>
);
}
export default Index
export async function getServerSideProps(ctx) {
let { data: receptions, error } = await supabase
.from('receptions')
.select('*')
.order('id', { ascending: false })
if (error) throw error
return {
props: {
allData: receptions
},
};
}
`
As you will notice at no time I make use of the getSession() method. I hope I explained myself well.
Try this in to see if the user is not logged in to send the user to login.
This would be because you have an older version of the supabase-js library installed. Please install the latest 2.x version in order for this to work.
Run the following command to get the latest v2 version
npm install #supabase/supabase-js#2

Gatsby - Uncaught TypeError - Context/build problem

I was developing my application in Gatsby and got stuck. Everything is working fine with "gatsby develop", but when I run "gatsby build" I get an error:
"WebpackError: TypeError: Cannot destructure property 'cursorStyles' of 'Object(...)(...)' as it is undefined."
And yeah, cursorStyles are defined in the context, so everything should work perfectly or I am missing something. Tried to clean cache, but still the error occurs, which is very weird cuz didn't have any problems to work on this project locally.
EDIT - yes, I wrapped the gatsby application with the Global Provider as you can see below. I just don't understand why the build doesn't work, when I clearly have the access to the context... ;/
gatsby-browser.js
import React from "react"
import { GlobalProvider } from "./src/context/globalContext"
export const wrapRootElement = ({ element }) => {
return <GlobalProvider>{element}</GlobalProvider>
}
Context - values are defined in global provider const
import React, { createContext, useReducer, useContext } from "react"
//Define Context
const GlobalStateContext = createContext()
const GlobalDispatchContext = createContext()
//Reducer
const globalReducer = (state, action) => {
switch (action.type) {
case "TOGGLE_THEME": {
return {
...state,
currentTheme: action.theme,
}
}
case "CURSOR_TYPE": {
return {
...state,
cursorType: action.cursorType,
}
}
default: {
throw new Error(`Unhandled action type: ${action.type}`)
}
}
}
export const GlobalProvider = ({ children }) => {
const [state, dispatch] = useReducer(globalReducer, {
currentTheme: "dark",
cursorType: false,
cursorStyles: ["pointer", "hovered", "locked", "white"],
})
return (
<GlobalDispatchContext.Provider value={dispatch}>
<GlobalStateContext.Provider value={state}>
{children}
</GlobalStateContext.Provider>
</GlobalDispatchContext.Provider>
)
}
//custom hooks for when we want to use our global state
export const useGlobalStateContext = () => useContext(GlobalStateContext)
export const useGlobalDispatchContext = () => useContext(GlobalDispatchContext)
Layout.js - problem occurs when destructuring and using my custom hook
import React, {useState} from "react"
import PropTypes from "prop-types"
import { useStaticQuery, graphql } from "gatsby"
import { createGlobalStyle, ThemeProvider } from "styled-components"
import { normalize } from "styled-normalize"
import Header from './header'
import Cursor from './customCursor'
import Navigation from './navigation'
import { useGlobalStateContext, useGlobalDispatchContext } from '../context/globalContext'
const Layout = ({ children }) => {
const { cursorStyles, currentTheme } = useGlobalStateContext()
const dispatch = useGlobalDispatchContext()
const data = useStaticQuery(graphql`
query SiteTitleQuery {
site {
siteMetadata {
title
}
}
}
`)
const darkTheme = {
background: '#000',
text:'#fff',
red: '#ea291e'
}
const lightTheme = {
background: '#fff',
text:'#000',
red: '#ea291e'
}
const onCursor = cursorType => {
cursorType = (cursorStyles.includes(cursorType) && cursorType || false)
dispatch({ type: "CURSOR_TYPE", cursorType: cursorType})
}
const [toggleMenu, setToggleMenu] = useState(false)
return(
<ThemeProvider theme={currentTheme === "dark" ? darkTheme : lightTheme}>
<GlobalStyle/>
<Cursor toggleMenu={toggleMenu} />
<Header onCursor={onCursor} toggleMenu={toggleMenu} setToggleMenu={setToggleMenu} />
<Navigation onCursor={onCursor} toggleMenu={toggleMenu} setToggleMenu={setToggleMenu} />
<main>{children}</main>
{console.log(currentTheme)}
</ThemeProvider>
)
}
Layout.propTypes = {
children: PropTypes.node.isRequired,
}
export default Layout
Finally I solved it! I don't know why, but I guess it's just Gatsby who works in a mysterious way. I duplicated my code from gatsby-browser, and put exactly the same thing into the gatsby-ssr file and... surprisingly the weird errors about "object undefined" disappeared.
Unfortunately, due to the fact that gatsby build in that case works similar to the next.js' server-side rendering, I had to fix my code in different places - e.g "window" properties had to be rendered conditionally - but duplicating gatsby-browser's code to the gatsby-ssr fixed the building problem! So as I thought, it wasn't a problem with my code, but rather a problem with gatsby's config.
Very strange. This problem happens only in production. Duplicating the code from gatsby-browser inside gatsby-ssr - really solves it!
import React from 'react';
import CartProvider from './src/components/CartProvider';
export const wrapRootElement = ({ element }) => (
<CartProvider>{element}</CartProvider>
);

Using graphql-tools, apollo-link-schema, and react-hooks always returning undefined when mocking

I'm new to using GraphQL in React and have been moving a project from a REST API to a new GraphQL one. As part of this, I wanted to setup mock data to work on the application independent of the GQL API being completed. I've spent a bunch of time trying to follow the Apollo and GraphQL Tools docs but no matter what, I can't seem to get the mock resolvers to work properly. For context, I am using this in a NextJS/React app, and here's a minimum example of what I'm trying to do:
Setup App.js
import React from 'react';
import ApolloClient from 'apollo-client';
import { ApolloProvider } from 'react-apollo';
import { SchemaLink } from 'apollo-link-schema';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { makeExecutableSchema } from '#graphql-tools/schema';
import { addMocksToSchema } from '#graphql-tools/mock';
export default function App() {
const schema = makeExecutableSchema({typeDefs:`
type Query {
getPerson: Person!
}
type Person {
name: String!
}
`});
const mocks = {
Query: () => ({
getPerson: () => ({
name: () => "Name"
})
})
}
addMocksToSchema({ mocks, schema });
const link = new SchemaLink({ schema });
const client = new ApolloClient({
link,
cache: new InMemoryCache(),
connectToDevTools: true
});
return (
<ApolloProvider client={client}>
<Person />
</ApolloProvider>
)
}
Person.js
import React from 'react';
import { useQuery } from '#apollo/react-hooks';
import gql from 'graphql-tag';
export default function Person() {
const { loading, error, data } = useQuery(gql`
query PersonQuery {
getPerson {
name
}
}
`, {
errorPolicy: 'all'
});
console.log(data);
if (loading) return "Loading...";
if (error) console.log(error);
return (
<h1>{data.getPerson.name}<h1>
)
}
Looking at the console.log(error) result yields the error Cannot return null for non-nullable field Query.getPerson and making it a nullable field just returns { getPerson: null } of course. I've tried returning the resolver results as objects vs functions. Logging within the resolvers shows the Query part is being executed but nothing nested within that.
Am I setting something up incorrectly? I also tried not passing in custom mocks as suggested should be possible based on the graphql-tools docs, but to no avail. I also saw this issue from the apollo hooks GitHub that said the newest version of hooks broke the usage of addMocksToSchema, so I tried using the suggested 3.1.3 version but again no luck. Any help would be greatly appreciated!
You need to provide the mock to the client, not the plain schema.
const schemaWithMocks = addMocksToSchema({
schema,
mocks: {},
preserveResolvers: false,
});
const client = new ApolloClient({
// link: new SchemaLink({ schema }); < -- REPLACE THIS
link: (new SchemaLink({ schema: schemaWithMocks }) as unknown) as ApolloLink, // https://github.com/apollographql/apollo-link/issues/1258
cache: new InMemoryCache(),
connectToDevTools: true,
});
Now console.log(data) prints
{"getPerson": {"__typename": "Person", "name": "Name"}} 🎉

Recommended way to use GraphQL in Next.js app

In my apps, I am using following NPM modules to play with Strapi, GraphQL and Next.js:
react-apollo
next-apollo
graphql
gql
recompose
In the next step, I am creating Apollo config file, example below:
import { HttpLink } from "apollo-link-http";
import { withData } from "next-apollo";
const config = {
link: new HttpLink({
uri: "http://localhost:1337/graphql",
})
};
export default withData(config);
and then inside a class component, I am using a static method getInitialProps() to fetch data from the Strapi via GraphQL query.
Everything is fine but maybe there is another, better way via React hooks or any other?
I found one more nice hook solution for Next.js and GraphQL.
I want to share it with you. Let's start.
Note: I assume that you have Next.js application already installed. If not please follow this guide.
To build this solution we need:
#apollo/react-hooks
apollo-cache-inmemory
apollo-client
apollo-link-http
graphql
graphql-tag
isomorphic-unfetch
next-with-apollo
1. run npm command:
npm install --save #apollo/react-hooks apollo-cache-inmemory apollo-client apollo-link-http graphql graphql-tag isomorphic-unfetch next-with-apollo
2. create Appolo config file, eg. in folder ./config and call it appollo.js. File code below:
import { ApolloClient } from "apollo-client";
import { InMemoryCache } from "apollo-cache-inmemory";
import withApollo from "next-with-apollo";
import { createHttpLink } from "apollo-link-http";
import fetch from "isomorphic-unfetch";
const GRAPHQL_URL = process.env.BACKEND_URL || "https://api.graphql.url";
const link = createHttpLink({
fetch,
uri: GRAPHQL_URL
});
export default withApollo(
({ initialState }) =>
new ApolloClient({
link: link,
cache: new InMemoryCache()
.restore(initialState || {})
})
);
3. create _app.js file (kind of wrapper) in ./pages folder with below code:
import React from "react";
import Head from "next/head";
import { ApolloProvider } from "#apollo/react-hooks";
import withData from "../config/apollo";
const App = ({ Component, pageProps, apollo }) => {
return (
<ApolloProvider client={apollo}>
<Head>
<title>App Title</title>
</Head>
<Component {...pageProps} />
</ApolloProvider>
)
};
export default withData(App);
4. create reusable query component, eg. ./components/query.js
import React from "react";
import { useQuery } from "#apollo/react-hooks";
const Query = ({ children, query, id }) => {
const { data, loading, error } = useQuery(query, {
variables: { id: id }
});
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {JSON.stringify(error)}</p>;
return children({ data });
};
export default Query;
5. create a component for our data fetched via GraphQL
import React from "react";
import Query from "../components/query";
import GRAPHQL_TEST_QUERY from "../queries/test-query";
const Example = () => {
return (
<div>
<Query query={GRAPHQL_TEST_QUERY} id={null}>
{({ data: { graphqlData } }) => {
return (
<div>
{graphqlData.map((fetchedItem, i) => {
return (
<div key={fetchedItem.id}>
{fetchedItem.name}
</div>
);
})}
</div>
);
}}
</Query>
</div>
);
};
export default Example;
6. create our GraphQL query inside ./queries/test-query. Note: I assume that we have access to our example data and properties id and name via GraphQL
import gql from "graphql-tag";
const GRAPHQL_TEST_QUERY = gql`
query graphQLData {
exampleTypeOfData {
id
name
}
}
`;
export default GRAPHQL_TEST_QUERY;
7. to display our result create index.js file (homepage) in ./pages folder with below code:
import Example from './components/example';
const Index = () => <div><Example /></div>
export default Index;
That's all.. enjoy and extend this solution as you want..
I have found one more interestng solution with using apollo-server-micro and lodash
Quick guide:
create Next.js app (example name: next-app) and install required packages
npm i apollo-server-micro lodash
create required files in you Next.js app (next-app)
/next-app/pages/api/graphql/index.js
/next-app/pages/api/graphql/resolvers.js
/next-app/pages/api/graphql/typeDefs.js
add code to index.js
import { ApolloServer } from 'apollo-server-micro';
import resolvers from './resolvers';
import typeDefs from './TypeDef';
const apolloServer = new ApolloServer({
typeDefs,
resolvers,
});
export const config = {
api: {
bodyParser: false
}
};
export default apolloServer.createHandler({ path: '/api/graphql' });
add code to typeDefs.js
import { gql } from 'apollo-server-micro';
const typeDefs = gql`
type User {
id: Int!
name: String!
age: Int
active: Boolean!
}
type Query {
getUser(id: Int): User
}
`;
export default typeDefs;
add code to resolvers.js
import lodash from 'lodash/collection';
const users = [
{ id: 1, name: 'Mario', age: 38, active: true },
{ id: 2, name: 'Luigi', age: 40, active: true},
{ id: 3, name: 'Wario', age: 36, active: false }
];
const resolvers = {
Query: {
getUser: (_, { id }) => {
return lodash.find(users, { id });
}
}
};
export default resolvers;
test your Next.js app (next-app) by running below command and checking graphql URL http://localhost:3000/api/graphql
npm run dev

React-intl for non components

Currently I have the following code to expose react-intl to non-components, but it throws an error for intl as undefined.
I have created a separate component as 'CurrentLocale' and inject-intl to it. The exporting function t will use intl formatMessage from CurrentLocale context.
import React from 'react';
import {injectIntl} from 'react-intl';
import PropTypes from 'prop-types';
import { flow } from 'lodash';
class CurrentLocale extends React.Component {
constructor(props,context){
super();
console.log(context,props);
console.log(this.formatMessage);
const { intl } = this.context.intl;//this.props;
this.formatMessage = intl.formatMessage;
}
render() {
return false;
}
}
CurrentLocale.contextTypes={
intl:PropTypes.object,
};
injectIntl(CurrentLocale);
function intl() {
return new CurrentLocale();
}
function formatMessage(...args) {
return intl().formatMessage(...args);
}
const t = opts => {
const id = opts.id;
const type = opts.type;
const values = opts.values;
let t;
switch (type){
case 'message':
default:
t = formatMessage(id, values);
}
return t;
}
export default t;
t is called as in another plain javascript file as,
import t from './locale/t';
t( { type: 'message', id:'button.Next'});
Following is the error message.
Thanks in advance.
There's also another approach very simple I used for solving a similar problem: Provide access to the intl object for a non-component:
import { IntlProvider, addLocaleData } from 'react-intl';
import localeDataDE from 'react-intl/locale-data/de';
import localeDataEN from 'react-intl/locale-data/en';
import { formMessages } from '../../../store/i18n'; // I defined some messages here
import { Locale } from '../../../../utils'; //I set the locale fom here
addLocaleData([...localeDataEN, ...localeDataDE]);
const locale = Locale.setLocale(); //my own methods to retrieve locale
const messages = Locale.setMessages(); //getting messages from the json file.
const intlProvider = new IntlProvider({ locale, messages });
const { intl } = intlProvider.getChildContext();
export const SCHEMA = {
salutation: {
label: intl.formatMessage(formMessages.salutationLabel),
errormessages: {
required: intl.formatMessage(formMessages.salutationError),
},
},
academic_title_code: {
label: intl.formatMessage(formMessages.academicTitleLabel),
},
};
It's working like a charm!
UPDATE for v3.x
After migration to react-intl 3.x
import { createIntl, createIntlCache } from 'react-intl'
import { formMessages } from '../../../store/i18n'; // I defined some messages here
import { Locale } from '../../../../utils'; //I set the locale fom here
const locale = Locale.setLocale(); //my own methods to retrieve locale
const messages = Locale.setMessages(); //getting messages from the json file.
// This is optional but highly recommended
// since it prevents memory leak
const cache = createIntlCache();
const intl = createIntl({ locale, messages }, cache)
export const SCHEMA = {
salutation: {
label: intl.formatMessage(formMessages.salutationLabel),
errormessages: {
required: intl.formatMessage(formMessages.salutationError),
},
},
academic_title_code: {
label: intl.formatMessage(formMessages.academicTitleLabel),
},
};
There's a new way to do it pretty easily with createIntl, it returns an object that you can use outside React components. Here's an example from the documentation.
import {createIntl, createIntlCache, RawIntlProvider} from 'react-intl'
// This is optional but highly recommended
// since it prevents memory leak
const cache = createIntlCache()
const intl = createIntl({
locale: 'fr-FR',
messages: {}
}, cache)
// Call imperatively
intl.formatNumber(20)
// Pass it to IntlProvider
<RawIntlProvider value={intl}>{foo}</RawIntlProvider>
I personally store the intl object in Redux store so I can access it everywhere in my app.
This line: const { intl } = this.context.intl; should be const { intl } = this.context;
Here is a reference post of someone doing almost the exact same thing as you are: https://github.com/yahoo/react-intl/issues/983#issuecomment-342314143
In the above the author is creating essentially a singleton that is exported instead of creating a new instance each time like you have above. This might be something you want to consider as well.
There's also another way solving a similar problem to used react-intl formatMessage for non-components.
Create a LocaleStore.js store file.
import _formatMessage from "format-message";
export default class LocaleStore {
formatMessage = (id, values) => {
if (!(id in this.messages)) {
console.warn("Id not found in intl list: " + id);
return id;
}
return _formatMessage(this.messages[id], values);
};
}
import LocaleStore your CombinedStores.js
import LocaleStore from "./stores/LocaleStore";
import en from "./translations/en";
import de from "./translations/de";
import Global from "./stores/global"
const locale = new LocaleStore("en", {
en,
de
});
export default {
global:new Global(locale)
}
now you can use this in your GlobalStore.js
class GlobalStore {
constructor(locale) {
this.locale = locale;
}
formatMessage=(message_is,formatLanguage="en")=> {
return this.locale.formatMessage(message_id, formatLanguage);
}
}
react-intl decorates your React.Component with wrapped component which is injected internationalized message dynamically so that the locale data is able to be loaded dynamically.
import { injectIntl } from 'react-intl';
class MyComponent extends Component {
render() {
const intl = this.props;
const title = intl.formatMessage({ id: 'title' });
return (<div>{title}</div>);
}
};
export default injectIntl(MyComponent);
It can be applied only in view layer such as React.Component.
react-intl can't be used in Vanilla JS. For example,
export default const rules = {
noSpace(value) {
if (value.includes(' ')) {
return 'Space is not allowed.';
}
}
};
One of alternative is react-intl-universal. It can be used not only in Component but also in Vanilla JS.
For example:
import intl from 'react-intl-universal';
export default const rules = {
noSpace(value) {
if (value.includes(' ')) {
return intl.get('no_space');
}
}
};
See react-intl-universal online example
If you can accept to use a function component I prefer to use the useIntl hook
https://reactjs.org/docs/components-and-props.html#function-and-class-components
I can then get values like this:
import { useIntl } from "react-intl";
const intl = useIntl()
intl.formatMessage({ id: 'myId' }),
https://formatjs.io/docs/react-intl/api/#useintl-hook

Resources