Next.js: Les routes (et pages) de l'API Next.js doivent prendre en charge la lecture de fichiers

Créé le 5 août 2019  ·  87Commentaires  ·  Source: vercel/next.js

Demande de fonctionnalité

Votre demande de fonctionnalité est liée à un problème ? Décrivez s'il vous plait.

Il n'est actuellement pas possible de lire des fichiers à partir de routes ou de pages API.

Décrivez la solution que vous souhaitez

Je veux pouvoir appeler fs.readFile avec un chemin __dirname et le faire "fonctionner".

Cela devrait fonctionner en mode Développement et Production.

Décrivez les alternatives que vous avez envisagées

Cela peut nécessiter une intégration avec @zeit/webpack-asset-relocator-loader dans une certaine mesure. Ce plugin gère ces types d'exigences.

Cependant, ce n'est pas une nécessité. Je serais d'accord avec quelque chose qui fonctionne _uniquement_ avec __dirname et __filename (pas de chemins relatifs ou basés sur cwd).

Contexte supplémentaire

Exemple:

// pages/api/test.js
import fs from 'fs'
import path from 'path'

export default (req, res) => {
  const fileContent = fs.readFileSync(
    path.join(__dirname, '..', '..', 'package.json'), 
    'utf8'
  )
  // ...
}

Remarque : je sais que vous pouvez tromper l'exemple ci-dessus ☝️ avec require , mais ce n'est pas le sujet. ??

story feature request

Commentaire le plus utile

Solution de contournement que j'utilise :

# next.config.js
module.exports = {
    serverRuntimeConfig: {
        PROJECT_ROOT: __dirname
    }
}

et à l'endroit où vous avez besoin du chemin

import fs from 'fs'
import path from 'path'
import getConfig from 'next/config'
const { serverRuntimeConfig } = getConfig()

fs.readFile(path.join(serverRuntimeConfig.PROJECT_ROOT, './path/to/file.json'))

Je sais que cela ne résout pas le besoin de référencer des fichiers avec des chemins relatifs au fichier actuel, mais cela résout mon cas d'utilisation très connexe (lecture de fichiers image à partir d'un dossier /public/images ).

Tous les 87 commentaires

Je voulais juste appuyer cela, en essayant de mettre en œuvre le téléchargement de fichiers à l'aide de routes API. Je peux télécharger le fichier, mais je dois ensuite pouvoir y accéder à nouveau pour le télécharger dans le compartiment S3.

Je seconde ça ! De plus, être capable de lire les répertoires est très important pour l'utilisation de mon entreprise car nous conservons nos données comme les membres de l'équipe et les articles de blog dans un répertoire de contenu. Nous cherchons donc un moyen d'exiger tous les fichiers du répertoire.

Le PR ci-dessus va résoudre ce problème ! ☝️ 🙏

Que diriez-vous de fs.writeFile est-ce possible ? Par exemple, créez et enregistrez un fichier JSON basé sur un webhook qui a été publié sur un /api/route

Salut @marlonmarcello , ça va être possible. Restez connectés

C'est déjà résolu ?

Pas encore, vous pouvez vous abonner au #8334

@huv1k Merci beaucoup !

Y a-t-il un moyen d'aider cela à avancer plus rapidement?

A noter : si vous utilisez TypeScript, vous pouvez déjà importer un fichier JSON en tant que module directement (assurez-vous que resolveJsonModule est bien true dans tsconfig.json ). Par exemple:

import myJson from '../../../some/path/my.json';

La forme de l'objet JSON est également automatiquement utilisée comme type, donc la saisie semi-automatique est vraiment agréable.

Solution de contournement que j'utilise :

# next.config.js
module.exports = {
    serverRuntimeConfig: {
        PROJECT_ROOT: __dirname
    }
}

et à l'endroit où vous avez besoin du chemin

import fs from 'fs'
import path from 'path'
import getConfig from 'next/config'
const { serverRuntimeConfig } = getConfig()

fs.readFile(path.join(serverRuntimeConfig.PROJECT_ROOT, './path/to/file.json'))

Je sais que cela ne résout pas le besoin de référencer des fichiers avec des chemins relatifs au fichier actuel, mais cela résout mon cas d'utilisation très connexe (lecture de fichiers image à partir d'un dossier /public/images ).

Vu dans les relations publiques, cela a un peu changé - une mise à jour sur les plans actuels (ou non) ? On dirait qu'il y a certaines stratégies que vous ne voulez pas poursuivre, n'oubliez pas de les énumérer + pourquoi les contributeurs peuvent-ils essayer ?

Cela bloque l'utilisation de Nexus avec Next.js. Ce serait formidable de revoir cette priorité.

Solution de contournement que j'utilise :

# next.config.js
module.exports = {
    serverRuntimeConfig: {
        PROJECT_ROOT: __dirname
    }
}

et à l'endroit où vous avez besoin du chemin

import fs from 'fs'
import path from 'path'
import getConfig from 'next/config'
const { serverRuntimeConfig } = getConfig()

fs.readFile(path.join(serverRuntimeConfig.PROJECT_ROOT, './path/to/file.json'))

Je sais que cela ne résout pas le besoin de référencer des fichiers avec des chemins relatifs au fichier actuel, mais cela résout mon cas d'utilisation très connexe (lecture de fichiers image à partir d'un dossier /public/images ).

Homme merveilleux. A travaillé pour moi.

J'ai utilisé la nouvelle méthode getStaticProps pour cela (dans #9524). La méthode est actuellement marquée comme instable, mais il semble y avoir un bon soutien de l'équipe Next.js pour l'envoyer officiellement.

par exemple:

export async function unstable_getStaticProps() {
  const siteData = await import("../data/pages/siteData.json");
  const home = await import("../data/pages/home.json");

  return {
    props: { siteData, home }
  };
}

@ ScottSmith95 Avez-vous un projet de source publique où vous l'utilisez? Curieux de savoir à quoi cela ressemblerait.

Le projet n'est pas encore open source, mais je suis heureux de partager plus de ma config si vous avez plus de questions.

@ScottSmith95 J'ai _toutes_ les questions 😛

  1. Où dans votre projet stockez-vous les fichiers de données ? (extérieur/intérieur src ?)
  2. À quoi ressemble un composant de page next.js qui les utilise ?
  3. S'agit-il uniquement de chemins codés en dur ou pouvez-vous charger un fichier en fonction des paramètres de chemin ?
  4. Comment fonctionne la compilation/le déploiement, surtout s'il ne s'agit pas de chemins codés en dur ?

@Svish Nous stockons des fichiers de données dans /data au sein de notre projet. (Les pages sont dans /pages, pas dans /src/prages.) Ce composant de page ressemble à ceci (les accessoires sont envoyés au composant Home qui est l'exportation par défaut) :

// /pages/index.js
const Home = ({ siteData, home }) => {
  return (
    <>
      <Head>
        <meta name="description" content={siteData.siteDescription} />
        <meta name="og:description" content={siteData.siteDescription} />
        <meta
          name="og:image"
          content={getAbsoluteUrl(siteData.siteImage, constants.siteMeta.url)}
        />
      </Head>
      <section className={`container--fluid ${styles.hero}`}>
        <SectionHeader section={home.hero} heading="1">
          <div className="col-xs-12">
            <PrimaryLink
              href={home.hero.action.path}
              className={styles.heroAction}
            >
              {home.hero.action.text}
            </PrimaryLink>
          </div>
        </SectionHeader>
        <div className={styles.imageGradientOverlay}>
          <img src={home.hero.image.src} alt={home.hero.image.alt} />
        </div>
      </section>
    </>
  );
};

Pour les pages plus avancées, celles avec des routes dynamiques, nous récupérons ces données comme ceci :

// /pages/studio/[member.js]
export async function unstable_getStaticProps({ params }) {
  const siteData = await import("../../data/pages/siteData.json");
  const member = await import(`../../data/team/${params.member}.json`);

  return {
    props: { siteData, member }
  };
}

Le déploiement se passe vraiment bien, avec des routes dynamiques, getStaticPaths() devient nécessaire. Je vous encourage à consulter la RFC pour la documentation à ce sujet, mais voici un exemple de la façon dont nous gérons cela en rassemblant toutes les données des membres de notre équipe et en les transmettant à Next.js.

// /pages/studio/[member.js]
export async function unstable_getStaticPaths() {
  const getSingleFileJson = async path => await import(`../../${path}`);

  // These utility functions come from `@asmallstudio/tinyutil` https://github.com/asmallstudio/tinyutil
  const directoryData = await getDirectory(
    "./data/team",
    ".json",
    getSingleFileJson,
    createSlugFromTitle
  );
  const directoryPaths = directoryData.reduce((pathsAccumulator, page) => {
    pathsAccumulator.push({
      params: {
        member: page.slug
      }
    });

    return pathsAccumulator;
  }, []);

  return directoryPaths;
}

@ ScottSmith95 semble prometteur ! Quelques questions complémentaires si vous avez le temps :

  1. Que faites-vous ici pour la génération de sites statiques ? C'est-à-dire lors de l'utilisation de next export ?
  2. Ai-je bien compris, que getStaticPaths renvoie une liste de paramètres de chemin, qui est ensuite (par la suite) alimentée, un par un, dans getStaticProps pour chaque rendu ?
  3. Pouvez-vous utiliser getStaticProps sans getStaticPaths , par exemple pour une page sans aucun paramètre ?
  4. Pouvez-vous utiliser getStaticProps dans _app ? Par exemple, si vous souhaitez charger une configuration à l'échelle du site ou quelque chose comme ça ?

Et les apis ?? Ces crochets sont pour les pages, mais qu'en est-il des apis ?

Je suis confus. J'ai pu définir le _dirname en tant que variable env dans la prochaine configuration. Par conséquent, j'ai pu accéder au système de fichiers à partir de l'API, mais cela ne fonctionnait que localement. Après l'avoir déployé jusqu'à maintenant, j'ai eu une erreur. Avez-vous une idée des raisons pour lesquelles cela ne fonctionnera pas après le déploiement ?

@josias-r le problème principal est généralement que les fichiers à lire ne sont pas inclus dans le déploiement, mais cela dépend de la façon dont vous les incluez et de leurs types de fichiers ( js / json est généralement bien, mais d'autres types de fichiers comme .jade nécessiteront d'autres moyens de traiter le sien, comme l'utilisation d'un @now/node lambda/déploiement séparé pour la lecture/la gestion de ces fichiers).

Si vous pouvez expliquer davantage l'erreur, peut-être que quelqu'un pourra vous aider.

@BrunoBernardino Il faisait en fait référence aux fichiers JSON dans mon dossier src. Mais c'est même la méthode fs.readdirSync(my_dirname_env_var) qui échoue déjà lors du déploiement. Ce répertoire ne semble donc plus du tout exister après le déploiement. Voici ce que j'obtiens lorsque j'essaie d'accéder au chemin complet vers le json via mon API :

ERROR   Error: ENOENT: no such file or directory, open '/zeit/3fc37db3/src/content/somejsonfilethatexists.json'

Et comme je l'ai mentionné, cela fonctionne localement lorsque je compile puis lance npm start .

@josias-r Merci ! Avez-vous essayé de faire le fs.readdirSync avec un chemin relatif (pas de variables) à la place (juste pour déboguer le déploiement) ? J'ai trouvé que cela fonctionnait généralement, et si c'est le cas, vous pouvez écrire ce morceau de code (juste en lisant le fichier, sans le stocker nulle part) quelque part dans un processus d'initialisation ( getInitialProps ou quelque chose), de sorte que le le processus de déploiement détecte qu'il a besoin de ce fichier, puis continue à le lire avec la var dans le code/la logique réel. Ce n'est pas soigné, mais cela fonctionne jusqu'à ce que cela soit pris en charge. Je crois que l'utilisation de __dirname fonctionne également dans certains cas.

@BrunoBernardino J'ai pu créer une arborescence de fichiers à partir du chemin relatif à la racine ./ . Ce que j'ai obtenu était le JSON suivant (sans les modules de nœud répertoriés):

{
  "path": "./",
  "name": ".",
  "type": "folder",
  "children": [
    {
      "path": ".//.next",
      "name": ".next",
      "type": "folder",
      "children": [
        {
          "path": ".//.next/serverless",
          "name": "serverless",
          "type": "folder",
          "children": [
            {
              "path": ".//.next/serverless/pages",
              "name": "pages",
              "type": "folder",
              "children": [
                {
                  "path": ".//.next/serverless/pages/api",
                  "name": "api",
                  "type": "folder",
                  "children": [
                    {
                      "path": ".//.next/serverless/pages/api/posts",
                      "name": "posts",
                      "type": "folder",
                      "children": [
                        {
                          "path": ".//.next/serverless/pages/api/posts/[...id].js",
                          "name": "[...id].js",
                          "type": "file"
                        }
                      ]
                    }
                  ]
                }
              ]
            }
          ]
        }
      ]
    },
    {
      "path": ".//node_modules",
      "name": "node_modules",
      "type": "folder",
      "children": ["alot of children here ofc"]
    },
    { "path": ".//now__bridge.js", "name": "now__bridge.js", "type": "file" },
    {
      "path": ".//now__launcher.js",
      "name": "now__launcher.js",
      "type": "file"
    }
  ]
}

Votre fichier JSON semble y manquer, avez-vous essayé de l'inclure via le code comme je l'ai suggéré ci-dessus ? Le principal problème est que les optimisations exécutées par le déploiement ne récupèrent pas toujours les chemins dynamiques, je crois, donc forcer un chemin statique a fonctionné pour moi dans le passé (pas nécessairement pour le code réel en cours d'exécution, mais pour s'assurer que les fichiers pertinents sont inclus). Cela a-t-il du sens ?

@BrunoBernardino Je suis passé à une solution sans API. Étant donné que je souhaite dynamiquement exiger des fichiers d'un dossier et que je n'ai besoin que du contenu de ces fichiers, je peux utiliser la méthode import() . Je ne voulais tout simplement pas le faire de cette façon, car cela semble bidon, mais cela fait essentiellement la même chose que mon point de terminaison d'API aurait fait.
... J'ai essayé de mettre le fichier dans le dossier statique mais cela n'a pas fonctionné non plus. Mais j'espère que l'accès au système de fichiers sera possible à l'avenir.

J'ai également dû recourir à des solutions piratées, mais j'espère que cela arrivera bientôt et que de plus en plus de gens commenceront à voir Next comme étant prêt pour la production à mesure que ces cas d'utilisation seront pris en charge "comme prévu".

Solution de contournement que j'utilise :

# next.config.js
module.exports = {
    serverRuntimeConfig: {
        PROJECT_ROOT: __dirname
    }
}

et à l'endroit où vous avez besoin du chemin

import fs from 'fs'
import path from 'path'
import getConfig from 'next/config'
const { serverRuntimeConfig } = getConfig()

fs.readFile(path.join(serverRuntimeConfig.PROJECT_ROOT, './path/to/file.json'))

Je sais que cela ne résout pas le besoin de référencer des fichiers avec des chemins relatifs au fichier actuel, mais cela résout mon cas d'utilisation très connexe (lecture de fichiers image à partir d'un dossier /public/images ).

Homme merveilleux. A travaillé pour moi.

Cela fonctionne parfaitement sur le développement local, bien que cela ne semble pas fonctionner lors du déploiement vers now .

ENOENT: no such file or directory, open '/zeit/41c233e5/public/images/my-image.png'
    at Object.openSync (fs.js:440:3)
    at Object.readFileSync (fs.js:342:35)
    at getEmailImage (/var/task/.next/serverless/pages/api/contact/demo.js:123:52)
    at module.exports.7gUS.__webpack_exports__.default (/var/task/.next/serverless/pages/api/contact/demo.js:419:87)
    at processTicksAndRejections (internal/process/task_queues.js:93:5)
    at async apiResolver (/var/task/node_modules/next/dist/next-server/server/api-utils.js:42:9) {
  errno: -2,
  syscall: 'open',
  code: 'ENOENT',
  path: '/zeit/41c233e5/public/images/my-image.png'
}

Je comprends que le dossier public est déplacé vers la route, j'ai donc essayé de le forcer à rechercher dans le dossier de base lors de la production, mais j'ai toujours obtenu le même résultat :

ENOENT: no such file or directory, open '/zeit/5fed13e9/images/my-image.png'
    at Object.openSync (fs.js:440:3)
    at Object.readFileSync (fs.js:342:35)
    at getEmailImage (/var/task/.next/serverless/pages/api/contact/demo.js:124:52)
    at module.exports.7gUS.__webpack_exports__.default (/var/task/.next/serverless/pages/api/contact/demo.js:331:87)
    at processTicksAndRejections (internal/process/task_queues.js:93:5)
    at async apiResolver (/var/task/node_modules/next/dist/next-server/server/api-utils.js:42:9) {
  errno: -2,
  syscall: 'open',
  code: 'ENOENT',
  path: '/zeit/5fed13e9/images/my-image.png'
}

@PaulPCIO le problème que vous rencontrez là-bas est dû au fait que ce n'est pas un fichier .json , .js , ou .ts . Les fichiers sous /public sont "déployés" sur un CDN mais pas sur le lambda (AFAIK), donc pour ce cas vous avez besoin soit d'un déploiement lambda dédié ( @now/node ) avec includeFiles , ou, si vous n'avez besoin que de ce seul fichier, convertissez-le en base64 et utilisez-le comme var (dans un fichier dédié ou non).

Merci @BrunoBernardino attendu autant, j'utiliserai la méthode base64

C'est une résolution du __dirname dans l'environnement déployé ??

@NicolasHz pouvez-vous développer ? Je n'ai pas bien compris ta question.

@BrunoBernardino En regardant les derniers commentaires, y compris le mien, je suis presque sûr que le hack "map _dirname in the next config" ne fonctionne pas en déploiement. Même avec des fichiers js et JSON. Au moins pour le déploiement de now , cela ne compte probablement pas pour les déploiements personnalisés.

@BrunoBernardino Je ne suis pas en mesure d'utiliser certaines variables pointant vers le chemin local sur l'environnement déployé. __dirname il n'est pas défini une fois déployé et je ne parviens pas à lire un fichier à partir de mes scripts apis.

Je l'ai compris @NicolasHz . Oui, vous devrez recourir à l'une des solutions ci-dessus, en fonction du type de fichier que vous devez lire/accéder.

Juste pour confirmer, le config.js ne fonctionne pas sur les déploiements.

Solution de contournement que j'utilise :

# next.config.js
module.exports = {
  env: {
    PROJECT_DIRNAME: __dirname,
  },
}

et dans la définition de l'api où j'ai besoin du chemin (le dossier allPosts contient tous les blogs au format markdown et il se trouve à la racine du projet)

import fs from 'fs'
import { join } from 'path'

const postsDirectory = join(process.env.PROJECT_DIRNAME, 'allPosts')

Il fonctionne parfaitement sur le développement local.
Mais il donne cette erreur lors du déploiement sur zeit maintenant.

[POST] /api/postsApi
11:00:13:67
Status:
500
Duration:
8.1ms
Memory Used:
76 MB
ID:
kxq8t-1585546213659-5c3393750f30
User Agent:
axios/0.19.2
{
  fields: [ 'title', 'date', 'slug', 'author', 'coverImage', 'excerpt' ],
  page: 1
}
2020-03-30T05:30:13.688Z    572075eb-4a7a-47de-be16-072a9f7005f7    ERROR   Error: ENOENT: no such file or directory, scandir '/zeit/1cc63678/allPosts'
    at Object.readdirSync (fs.js:871:3)
    at getPostSlugs (/var/task/.next/serverless/pages/api/postsApi.js:306:52)
    at module.exports.fZHd.__webpack_exports__.default (/var/task/.next/serverless/pages/api/postsApi.js:253:86)
    at apiResolver (/var/task/node_modules/next/dist/next-server/server/api-utils.js:48:15)
    at processTicksAndRejections (internal/process/task_queues.js:97:5) {
  errno: -2,
  syscall: 'scandir',
  code: 'ENOENT',
  path: '/zeit/1cc63678/allPosts'
}

@sjcodebook comme l' a dit @now/node distinct pour que les lambdas accèdent au système de fichiers et appelle ce fichier via une demande de l'application elle-même (ou génère le résultat statique dont j'ai besoin avant le déploiement). Un peu fou, mais ça marche.

Salut @BrunoBernardino... Voulez-vous dire un projet séparé avec un serveur de nœud personnalisé ?

Cependant je ne comprends pas pourquoi il y a un paramètre "

@valse cela peut être sur le même projet. Voici un extrait de mon now.json :

{
  "builds": [
    {
      "src": "next.config.js",
      "use": "@now/next"
    },
    {
      "src": "lambdas/**/*.ts",
      "use": "@now/node",
      "config": {
        "includeFiles": ["email-templates/**"]
      }
    }
  ],
  "routes": [
    {
      "src": "/lambdas/(.+)",
      "dest": "/lambdas/$1.ts"
    }
  ]
}

De cette façon, je peux les appeler via quelque chose comme:

await ky.post(`${hostUrl}/lambdas/email?token=${someToken}`);

à partir d'une page api suivante, en supposant que j'ai un fichier lambdas/email.ts qui gère l'envoi d'e-mails et la lecture à partir de fichiers modèles tels que pug .

J'espère que ça aide !

De plus, "includeFiles" ne fonctionne que pour @now/node (peut-être d'autres, mais pas @now/next )

@BrunoBernardino ressemble à s'il utilise des fonctions node , il ne peut plus lire ESM !

voici ce qui se passe lorsque j'essaye d'importer une liste de pages mdx :

code

import { NextApiRequest, NextApiResponse } from 'next'
import { promises as fs } from 'fs'
import { join } from 'path'
const { readdir } = fs

export default async (req: NextApiRequest, res: NextApiResponse) => {
  const postFiles = await readdir(join(process.cwd(), 'pages', 'blog'))

  const postNames: string[] = postFiles.filter((page: string) => page !== 'index.tsx')

  const posts = []

  for (const post of postNames) {
    const mod = await import(`../pages/blog/${post}`)

    posts.push({ ...mod, link: post.slice(0, post.indexOf('.')) })
  }

  res.status(200).json([])
}

l'erreur que j'obtiens :

export const title = 'My new website!'
^^^^^^

SyntaxError: Unexpected token 'export'

@talentlessguy Je ne fais pas partie de l'équipe Zeit/Vercel, juste un client satisfait. On dirait que cela pourrait mieux convenir à leur support client, car je vois quelques problèmes potentiels juste à partir de cet extrait :

  1. Vous voudrez peut-être n'utiliser rien ou __dirname au lieu de process.cwd() pour le chemin de base. Je n'ai pas utilisé ce dernier dans les lambdas, mais les autres, donc je ne sais pas si c'est un problème ou non
  2. Vous importez NextApiRequest et NextApiResponse tant que types, mais cela devrait fonctionner à partir de @now/node" , n'est-ce pas ? Ainsi, les types doivent être importés comme :
import { NowRequest, NowResponse } from '@now/node';
  1. Vous importez/lisez à partir de pages/... mais les incluez-vous via includeFiles ? À quoi ressemble votre now.json ?

@BrunoBernardino

Je ne peux pas utiliser __dirname car c'est toujours / , process.cwd() place, montre le vrai chemin

J'ai accepté vos correctifs et cela a fonctionné :

lambdas/posts.ts

import { NowResponse, NowRequest } from '@now/node'
import { promises as fs } from 'fs'
import { join } from 'path'
const { readdir } = fs

export default async (req: NowRequest, res: NowResponse) => {
  const postFiles = await readdir(join(process.cwd(), 'pages', 'blog'))

  const postNames: string[] = postFiles.filter((page: string) => page !== 'index.tsx')

  const posts = []

  for (const post of postNames) {
    const mod = await import(`../pages/blog/${post}`)

    posts.push({ ...mod, link: post.slice(0, post.indexOf('.')) })
  }

  res.status(200).json([])
}

maintenant.json

{
  "builds": [
    {
      "src": "next.config.js",
      "use": "@now/next"
    },
    {
      "src": "lambdas/**/*.ts",
      "use": "@now/node",
      "config": {
        "includeFiles": ["pages/blog/*.mdx"]
      }
    }
  ],
  "routes": [
    {
      "src": "/lambdas/(.+)",
      "dest": "/lambdas/$1.ts"
    }
  ]
}

erreur que j'obtiens :

import Meta from '../../components/Article/Meta.tsx'
^^^^^^

SyntaxError: Cannot use import statement outside a module

On dirait que la fonction de nœud dactylographié ne peut pas traiter .mdx comme un module :(

Bon, il semble que vous ayez trouvé le problème. Essayez de lire le contenu du fichier et de l'analyser au lieu de l'importer directement. Je n'ai jamais vu une importation comme celle-ci fonctionner, et cela semble être quelque chose qui ne fonctionnerait qu'avec de la magie de Babel, que vous pouvez également utiliser à la place du simple TS.

@BrunoBernardino tu as raison, mais ce n'est pas simple... J'ai la cible définie sur esnext et module sur esnext également, il devrait pouvoir tout importer... mais d'une manière ou d'une autre, ce n'est pas le cas

de toute façon, ce n'est pas lié au problème, je vais le chercher quelque part sur Google

Pas de soucis. Quelques conseils pourraient être dans https://mdxjs.com/advanced/typescript et https://mdxjs.com/getting-started/webpack qui pourraient faire en sorte que le déploiement de @now/node doit être modifié pour utilise le. Quoi qu'il en soit, leur soutien devrait être utile.

aucun mouvement à ce sujet ? Ce serait formidable de pouvoir inclure des modèles d'e-mails html à utiliser dans les routes API. En ce moment, je les inclus dans des fichiers JS mais je ne suis pas particulièrement fan de ce hack.

Un autre hack consiste à utiliser webpack raw-loader pour les intégrer dans js.

yarn add --dev raw-loader
const templates = {
    verify: require("raw-loader!../template/email/verify.hbs").default,
};

Ensuite, utilisez templates.verify comme chaîne.

Il y a un problème avec next-i18next qui semble être lié à celui-ci ( vercel/vercel#4271 ) . Fondamentalement, now ne place pas les fichiers .json situés à l'intérieur de /public/static/locales/ dans la fonction sans serveur. Quelqu'un peut-il fournir une solution de contournement jusqu'à ce que la fonctionnalité discutée ici soit ajoutée à la suivante ?

@borispoehland avez-vous essayé les solutions de contournement d'importation/requis ci-dessus ? Cela devrait fonctionner.

@borispoehland avez-vous essayé les solutions de contournement d'importation/requis ci-dessus ? Cela devrait fonctionner.

@BrunoBernardino Je ne sais pas de quel commentaire vous parlez exactement.

Pouvez-vous me donner un exemple d'importation de tous les fichiers .json contenus dans public/static/locales dans la fonction sans serveur ? Et faire ça (dans quel fichier) ?

J'utilise next (comme vous l'avez dit plus tôt, includeFiles n'est pas compatible avec @now/next , idk si cela a un impact sur mon problème).

De plus, parce que next-i18next est une sorte de boîte noire pour moi (je ne veux donc pas importer les fichiers à partir de là), je cherche un moyen de les importer entièrement afin que next-i18next puisse directement y accéder (dans d'autres commentaires ci-dessus, parfois seul le PROJECT_DIRNAME était défini à l'intérieur du next.config.json et l'import devait être fait manuellement. Ce n'est pas ce que j'essaie d'atteindre). Comme dans vercel/vercel#4271 , je veux juste que now prenne mes fichiers .json dans la fonction sans serveur d'une manière ou d'une autre.

@borispoehland dans _any_ file à l'intérieur de pages/api (ou qui est appelé par un là-bas), faites quelque chose comme https://github.com/vercel/next.js/issues/8251#issuecomment -544008976

Vous n'avez rien à faire avec l'importation. Le fait est que l'exécution de webpack vercel verra alors que ces fichiers doivent être inclus, et cela devrait fonctionner.

J'espère que cela à du sens.

@borispoehland dans _any_ file à l'intérieur de pages/api (ou qui est appelé par un là-bas), faites quelque chose comme #8251 (commentaire)

Vous n'avez rien à faire avec l'importation. Le fait est que l'exécution de webpack vercel verra alors que ces fichiers doivent être inclus, et cela devrait fonctionner.

J'espère que cela à du sens.

@BrunoBernardino le problème avec cette approche est que j'ai beaucoup de fichiers json. Faire l'importation manuellement pour chaque fichier est un peu fastidieux. Existe-t-il un moyen plus simple de dire à now : "Hé, veuillez récupérer tous les fichiers json dans ce répertoire de manière récursive" ? Merci d'avance

Edit: Même l'importation manuelle json fichiers

J'ai ouvert un nouveau problème pour mon problème , au cas où quelqu'un serait intéressé à se joindre à la discussion. Merci pour le moment, @BrunoBernardino !

Une autre option / solution de contournement pour activer la possibilité d'utiliser __dirname comme vous vous attendez normalement à ce qu'il se comporte consiste à ajuster la configuration du pack Web.

Par défaut, webpack alias divers Node globals avec des polyfills, sauf si vous lui dites de ne pas le faire :
https://webpack.js.org/configuration/node/
Et les paramètres par défaut du webpack sont de laisser __dirname et __filename seuls, c'est-à-dire de ne pas les polyremplir et de laisser le nœud les gérer normalement.

Cependant, la configuration du webpack Next.js n'utilise pas / ne reflète pas les valeurs par défaut du webpack https://github.com/vercel/next.js/blob/bb6ae2648ddfb65a810edf6ff90a86201d52320c/packages/next/build/webpack-config.ts#L661 -L663

Cela dit, j'ai utilisé le plugin de configuration Next personnalisé ci-dessous pour ajuster la configuration du pack Web.

IMPORTANT: cela fonctionne pour mon cas d'utilisation. Il n'a pas été testé dans une large gamme d'environnements/configurations et n'a pas été testé contre tous les tests unitaires/d'intégration Next.js. Son utilisation peut avoir des effets secondaires inattendus dans votre environnement.
De plus, Next peut avoir des raisons spécifiques de ne pas utiliser les paramètres par défaut du pack Web pour __dirname et __filename . Encore une fois, le code ci-dessous peut avoir des effets secondaires inattendus et doit être utilisé avec prudence.

De plus, le plugin ci-dessous a été conçu pour être utilisé avec le package next-compose-plugins : https://github.com/cyrilwanner/next-compose-plugins

Mais devrait également fonctionner comme un plugin normal : https://nextjs.org/docs/api-reference/next.config.js/custom-webpack-config

const withCustomWebpack = (nextCfg) => {
  return Object.assign({}, nextCfg, {
    webpack(webpackConfig, options) {
      // We only want to change the `server` webpack config.
      if (options.isServer) {
        // set `__dirname: false` and/or `__filename: false` here to align with webpack defaults:
        // https://webpack.js.org/configuration/node/
        Object.assign(webpackConfig.node, { __dirname: false });
      }

      if (typeof nextCfg.webpack === 'function') {
        return nextCfg.webpack(webpackConfig, options);
      }

      return webpackConfig;
    },
  });
};

J'ai implémenté la solution par @jkjustjoshing , et bien que cela fonctionne très bien localement, cela ne fonctionne pas lorsque je déploie l'application sur Vercel.

J'obtiens l'erreur suivante :

Error: GraphQL error: ENOENT: no such file or directory, open '/vercel/37166432/public/ts-data.csv'

Mon code :

const content = await fs.readFile(
  path.join(serverRuntimeConfig.PROJECT_ROOT, "./public/ts-data.csv")
);

Voici un lien vers le fichier : https://github.com/bengrunfeld/trend-viewer/blob/master/pages/api/graphql-data.js

@bengrunfeld oui, votre solution ne fonctionne que localement.

J'ai eu un problème similaire récemment (je voulais lire un fichier dans une route API) et la solution a été plus simple que prévu.

Essayez path.resolve('./public/ts-data.csv')

@borispoehland Merci beaucoup !! Votre solution a fonctionné à merveille !

@bengrunfeld pas de problème, je l'ai aussi découvert par hasard ( @BrunoBernardino ;)). C'est la solution au problème de tout le monde ici, je suppose.

Veuillez noter que vous devez toujours définir next.config.js . J'ai supprimé le fichier après avoir vu que la solution de @borispoehland fonctionnait et j'ai reçu une erreur similaire.

Ensuite, je l'ai réinitialisé à la solution de

# next.config.js
module.exports = {
    serverRuntimeConfig: {
        PROJECT_ROOT: __dirname
    }
}

Veuillez noter que vous devez toujours définir next.config.js . Je l'ai supprimé après avoir vu que la solution de @borispoehland fonctionnait et j'ai reçu une erreur similaire.

Je l'ai réinitialisé à la solution de

# next.config.js
module.exports = {
    serverRuntimeConfig: {
        PROJECT_ROOT: __dirname
    }
}

@bengrunfeld Vraiment ? Peut-être que vous utilisez toujours l'approche PROJECT_ROOT à un autre endroit du code, car dans mon projet, cela fonctionne sans. A quoi ressemble l'erreur ?

Lors du déploiement sur Vercel, comment écrire un readFile dans une Page qui fonctionnera à la fois en mode SSG et SSR/Aperçu ?

Dépôt de démonstration de l'endroit où cela ne fonctionne https://github.com/mathdroid/blog-fs-demo

https://blog-fs-demo.vercel.app/

@mathdroid essayez de déplacer readFile et readdir à l'intérieur des fonctions getStaticProps et getStaticPaths , sinon le code pourrait s'exécuter dans le navigateur.

Cependant, l'importation de fs devrait suffire.

Faites-nous savoir comment cela fonctionne.

@borispoehland Merci pour la merveilleuse solution. Je ne pensais pas que path.resolve() à /public fonctionnerait à la fois localement et sur Vercel :eyes:! Tu es mon sauveur pour la journée. :+1:

@borispoehland j'ai essayé votre solution dans une fonction sans serveur mais j'obtiens toujours:
ENOENT : aucun fichier ou répertoire de ce type, ouvrez '/var/task/public/posts.json'

const postsFile = resolve('./public/posts.json');

const updateCache = async (posts: IPost[]): Promise<IPost[]> => {
    postCache = posts;
    fs.writeFileSync(postsFile, JSON.stringify(postCache)); // <====
    return postCache;
}

j'ai essayé avec notre sans next.config.js

module.exports = {
    serverRuntimeConfig: {
        PROJECT_ROOT: __dirname
    }
}

Peut-être que votre solution ne fonctionne pas sur les fonctions sans serveur ?

@borispoehland j'ai essayé votre solution dans une fonction sans serveur mais j'obtiens toujours:
ENOENT : aucun fichier ou répertoire de ce type, ouvrez '/var/task/public/posts.json'

const postsFile = resolve('./public/posts.json');

const updateCache = async (posts: IPost[]): Promise<IPost[]> => {
    postCache = posts;
    fs.writeFileSync(postsFile, JSON.stringify(postCache)); // <====
    return postCache;
}

j'ai essayé avec notre sans next.config.js

module.exports = {
    serverRuntimeConfig: {
        PROJECT_ROOT: __dirname
    }
}

Peut-être que votre solution ne fonctionne pas sur les fonctions sans serveur ?

Je ne sais pas pourquoi ça ne marche pas de ton côté... Désolé

Ok, je l'ai fait fonctionner pour la lecture en utilisant le code @bengrunfeld mais malheureusement, vous ne pouvez pas écrire :
[Erreur : EROFS : système de fichiers en lecture seule, ouvrez '/var/task/public/posts.json']
Donc pas moyen de mettre à jour un cache pour éviter trop d'appels à la base de données :(

@neckaros avez-vous essayé d'utiliser mon approche pour lire un fichier autre que .json , par exemple un fichier .jpg ?

Ok, je l'ai fait fonctionner pour la lecture en utilisant le code @bengrunfeld mais malheureusement, vous ne pouvez pas écrire :

[Erreur : EROFS : système de fichiers en lecture seule, ouvrez '/var/task/public/posts.json']

Donc pas moyen de mettre à jour un cache pour éviter trop d'appels à la base de données :(

@neckaros, vous devriez pouvoir écrire et lire à partir de S3 (ou d'un autre système de fichiers externe), mais j'utilise généralement redis pour des choses rapides et mises en cache qui peuvent être volatiles. https://redislabs.com le maintient "sans serveur", et j'ai des exemples de code prêts pour la production dans https://nextjs-boilerplates.brn.sh si vous le souhaitez.

@borispoehland je pouvais lire mais pas écrire à partir d'une fonction sans service. Mais j'ai fini par le faire fonctionner en actualisant mon cache dans les versions incrémentielles (revalider) au lieu d'ajouter un nouveau contenu. Ce qui, je suppose, n'est pas un mauvais modèle. Merci pour ton aide!

@BrunoBernardino merci je vais regarder. Vous cherchez vraiment une solution d'amateur entièrement gratuite qui ne casse pas une fois que vous avez quelques utilisateurs :)

Vous cherchez vraiment une solution d'amateur entièrement gratuite qui ne casse pas une fois que vous avez quelques utilisateurs :)

Bien reçu. RedisLabs et Vercel l'ont fait pour moi. ??

Après quelques recherches, j'ai commencé à écrire des fichiers fonctionnant avec le package OS étendu ...

import { tmpdir } from "os";
const doc = new PDFDocument()
const pdfPath = path.join(tmpdir(), `${store.id}${moment().format('YYYYMMDD')}.pdf`)
const writeStream = doc.pipe(fs.createWriteStream(pdfPath)

lire un fichier fonctionne avec la solution @subwaymatch
const logoPath = path.resolve('./public/logo.png')

Après quelques recherches, j'ai commencé à écrire des fichiers fonctionnant avec le package OS étendu ...

import { tmpdir } from "os";
const doc = new PDFDocument()
const pdfPath = path.join(tmpdir(), `${store.id}${moment().format('YYYYMMDD')}.pdf`)
const writeStream = doc.pipe(fs.createWriteStream(pdfPath)

lire un fichier fonctionne avec la solution @subwaymatch
const logoPath = path.resolve('./public/logo.png')

Bien, êtes-vous capable de relire le contenu de ce fichier ? L'annuaire est-il accessible et permanent ?

@marklundin Avec une fonction nommée tmpdir je doute que ce soit permanent, mais si cela fonctionne, il serait bon de savoir à quel point tmpdir est temporaire, ouais... 🤔

Des mises à jour à ce sujet ? Je me demande pourquoi cela fonctionne dans getInitialProps, mais pas dans les routes API 🤷‍♂️

Ma solution de contournement actuelle

const data = await import(`../../../../data/de/my-nice-file.json`);
res.json(data.default);

ayant actuellement ce problème dans les routes API aussi

ayant actuellement ce problème dans les routes API aussi

Il existe quelques solutions de travail ici, quel problème rencontrez-vous en particulier ?

J'ai du mal à faire fonctionner cela même avec les suggestions de ce fil. Mon cas d'utilisation est que j'écris un guide et que je souhaite afficher le code source du composant à côté du composant lui-même. Ma méthode pour ce faire consiste à utiliser fs pour charger le fichier jsx du composant dans getServerSideProps et à transmettre la valeur de chaîne du contenu du fichier en tant qu'accessoire.

Je me sentais ravi de le faire fonctionner localement, mais quand je suis allé le déployer, la joie est partie :(

Veuillez consulter : https://github.com/ElGoorf/i18next-guide/blob/fix-example-components/pages/plurals.jsx

@ElGoorf votre problème est que les fichiers public sont sur un bord et que les fonctions sont sur un lambda. Maintenant, @vercel/next ne permet toujours pas includeFiles , donc le moyen le plus simple de le faire fonctionner serait d'utiliser une fonction lambda avec.

Voici un exemple de code qui a aidé les autres ici : https://github.com/vercel/next.js/issues/8251#issuecomment -614220305

Merci @BrunoBernardino Je n'avais pas réalisé que j'avais raté le "x éléments cachés charger plus..." et je pensais que je

Malheureusement, j'ai eu du mal avec votre solution, car c'est la première fois que j'entends parler d'Edge/Lambda, cependant, j'ai trouvé que la solution de https :/ /github.com/vercel/next.js/issues/8251#issuecomment -634829189

Super! L'avez-vous fait fonctionner ? Ou avez-vous toujours des problèmes?

Je ne suis pas sûr que Vercel utilise même cette terminologie, mais par Edge, je veux dire CDN, d'où les fichiers statiques sont servis, et par lambda, j'entends les fonctions "backend" qui sont appelées à partir des routes API, qui sont isolées comme les fonctions AWS Lambda .

Hey,

Une mise à jour sur l'écriture dans des fichiers à l'aide de next.js sur vercel ? Je peux lire sans problème. Utiliser le const logoPath = path.resolve('./public/logo.png')

J'essaie d'écraser le fichier public/sitemap.xml (en raison des limites de taille sur vercel), je ne peux que le renvoyer sans erreur en tant que fichier statique dans le dossier public. J'ai déjà implémenté le plan du site avec zlib et diffusé la réponse, mais il semble attendre la fin du flux, puis le renvoyer. Cela n'atteint pas l'erreur de limitation de taille, mais malheureusement, c'est très lent. Je suis ouvert à toutes les suggestions que les gens pourraient avoir. Le plan du site est construit à partir d'un appel d'API vers un backend séparé et doit être mis à jour régulièrement.

Ce que j'ai tenté :

  • Zipper et diffuser le xml - fonctionne mais très lentement.
  • Construire et renvoyer le plan du site à partir d'une fonction api, malheureusement, cela atteint la limite de taille.
  • Lecture d'un fichier xml statique à partir d'un dossier public (fonctionne) quelle que soit sa taille, mais non modifiable.
  • Tester l'écriture dans ce fichier, ne fonctionne pas. Tester l'écriture dans n'importe quel fichier/dossier ne fonctionne pas
  • Testez le retour du fichier xml statique à partir de la fonction "api", erreur de taille. Cela fonctionne localement.
  • Testez le retour du fichier xml statique à partir de l'erreur de taille getServerSideProp "page". Cela fonctionne localement.
  • Apprécierait des idées du tout ?

Hey @emomooney , je n'imagine pas que Vercel permette jamais d'écrire des fichiers dans une fonction (même pour la mise en cache), car le principal "avantage" du serverless est son apatride, et cela lui ajouterait de l'état, donc je pense que vous allez besoin d'utiliser le bord/cdn pour cela.

J'ai déjà implémenté le plan du site avec zlib et diffusé la réponse, mais il semble attendre la fin du flux, puis le renvoyer.

Je suis curieux de savoir si vous n'aviez que cette lenteur pour les appels suivants, ou juste le premier, pour le démarrage à froid ? J'imagine qu'il s'agissait d'un appel API à Vercel via une fonction api next.js, ou un lambda dédié, similaire à ce que je fais ici .

Si c'était le cas et que c'était encore trop lent, votre "backend séparé" est-il en dehors de Vercel ? Si c'est le cas, vous pouvez potentiellement l'utiliser pour créer un fichier sitemap.xml et vercel --prod dans un domaine, en "cachant" essentiellement le fichier pour qu'il soit lisible et accessible, et vous auriez juste besoin de mettre à jour le robots.txt pour lier le plan du site à un autre domaine/sous-domaine.

Cette page vous a été utile?
0 / 5 - 0 notes