Next.js: يجب أن تدعم مسارات Next.js API (والصفحات) ملفات القراءة

تم إنشاؤها على ٥ أغسطس ٢٠١٩  ·  87تعليقات  ·  مصدر: vercel/next.js

طلب المواصفات

هل طلب الميزة الخاص بك متعلق بمشكلة؟ يرجى الوصف.

لا يمكن حاليًا قراءة الملفات من مسارات أو صفحات واجهة برمجة التطبيقات.

صِف الحل الذي تريده

أريد أن أكون قادرًا على الاتصال بـ fs.readFile __dirname بمسار

يجب أن يعمل هذا في وضع التطوير والإنتاج.

صِف البدائل التي فكرت فيها

قد يحتاج هذا إلى التكامل مع @zeit/webpack-asset-relocator-loader في بعض السعة. هذا البرنامج المساعد يعالج هذه الأنواع من المتطلبات.

ومع ذلك ، فهي ليست ضرورة. سأكون موافقًا على شيء يعمل _only_ مع __dirname و __filename (لا توجد مسارات نسبية أو قائمة على cwd).

سياق إضافي

مثال:

// 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'
  )
  // ...
}

ملاحظة: أعلم أنه يمكنك خداع المثال أعلاه ☝️ بـ require ، لكن هذا ليس هو الهدف. 😄

story feature request

التعليق الأكثر فائدة

الحل الذي أستخدمه:

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

وفي الموقع تحتاج المسار

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'))

أعلم أن هذا لا يحل الحاجة إلى الرجوع إلى الملفات ذات المسارات المتعلقة بالملف الحالي ، ولكن هذا يحل حالة الاستخدام ذات الصلة (قراءة ملفات الصور من مجلد /public/images ).

ال 87 كومينتر

أردت فقط أن أثني على ذلك ، في محاولة لتنفيذ تحميل الملفات باستخدام مسارات API. يمكنني تحميل الملف ولكن بعد ذلك أحتاج إلى الوصول إليه مرة أخرى لتحميله إلى دلو S3.

أنا أوافق على هذا! أيضًا ، تعد القدرة على قراءة الدلائل أمرًا مهمًا جدًا لاستخدام شركتي حيث نحتفظ ببياناتنا مثل أعضاء الفريق ومنشورات المدونة في دليل المحتوى ، لذلك نحن نبحث عن طريقة لطلب جميع الملفات في الدليل.

العلاقات العامة أعلاه سوف تصلح هذا! ☝️ 🙏

ماذا عن fs.writeFile هل هذا ممكن؟ على سبيل المثال ، قم بإنشاء وحفظ ملف JSON بناءً على خطاف ويب تم نشره على /api/route

مرحبًا marlonmarcello ، سيكون هذا ممكنًا. ترقبوا

هل هذا قد تم حله بالفعل؟

ليس بعد ، يمكنك الاشتراك برقم # 8334

@ huv1k جزيل الشكر!

هل هناك طريقة للمساعدة في المضي قدمًا بشكل أسرع؟

جدير بالذكر: إذا كنت تستخدم TypeScript ، فيمكنك بالفعل استيراد ملف JSON كوحدة نمطية مباشرة (تأكد من أن resolveJsonModule هو true في tsconfig.json ). على سبيل المثال:

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

يتم أيضًا استخدام شكل كائن JSON تلقائيًا كنوع ، لذا فإن الإكمال التلقائي رائع حقًا.

الحل الذي أستخدمه:

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

وفي الموقع تحتاج المسار

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'))

أعلم أن هذا لا يحل الحاجة إلى الرجوع إلى الملفات ذات المسارات المتعلقة بالملف الحالي ، ولكن هذا يحل حالة الاستخدام ذات الصلة (قراءة ملفات الصور من مجلد /public/images ).

رأيت في العلاقات العامة أن هذا قد تغير قليلاً - أي تحديث لما هي الخطط الحالية (أو لا)؟ يبدو أن هناك بعض الاستراتيجيات التي لا تريد اتباعها ، ضع قائمة بها ذهنك + لماذا يمكن للمساهمين إعطاء هذه اللقطة؟

هذا يحظر استخدام nexus مع Next.js. سيكون من الرائع رؤية هذا الأمر يحظى بالأولوية مرة أخرى.

الحل الذي أستخدمه:

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

وفي الموقع تحتاج المسار

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'))

أعلم أن هذا لا يحل الحاجة إلى الرجوع إلى الملفات ذات المسارات المتعلقة بالملف الحالي ، ولكن هذا يحل حالة الاستخدام ذات الصلة (قراءة ملفات الصور من مجلد /public/images ).

رجل رائع. عملت من أجلي.

لقد كنت أستخدم طريقة getStaticProps لهذا (في 9524 #). تم وضع علامة على الطريقة حاليًا على أنها غير مستقرة ولكن يبدو أن هناك دعمًا جيدًا من فريق Next.js لشحنها رسميًا.

على سبيل المثال:

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 هل لديك مشروع مصدر عام تستخدم فيه هذا؟ فضولي حول الشكل الذي سيبدو عليه.

المشروع ليس مفتوح المصدر ، ولكن يسعدني مشاركة المزيد من التكوين الخاص بي إذا كان لديك المزيد من الأسئلة.

@ ScottSmith95 لدي _all_ الأسئلة 😛

  1. أين تخزن ملفات البيانات في مشروعك؟ (خارج / داخل src ؟)
  2. كيف يبدو مكون الصفحة next.js الذي يستخدمهم؟
  3. هل هي مسارات مشفرة فقط ، أم يمكنك تحميل ملف بناءً على معلمات المسار؟
  4. كيف يعمل البناء / النشر ، خاصةً إذا لم تكن مسارات مشفرة؟

Svish نقوم بتخزين ملفات البيانات في / بيانات داخل مشروعنا. (الصفحات موجودة في / pages ، وليست / src / prages.) يبدو مكون الصفحة هذا على هذا النحو (يتم إرسال الدعائم إلى مكوِّن الصفحة الرئيسية وهو التصدير الافتراضي):

// /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>
    </>
  );
};

بالنسبة للصفحات الأكثر تقدمًا ، تلك التي تحتوي على مسارات ديناميكية ، فإننا نحصل على هذه البيانات مثل:

// /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 }
  };
}

يتم النشر بسلاسة فعلية ، مع المسارات الديناميكية ، يصبح getStaticPaths() ضروريًا. أشجعك على التحقق من RFC للحصول على الوثائق المتعلقة بذلك ، ولكن إليك مثال على كيفية تعاملنا مع ذلك من خلال جمع جميع بيانات أعضاء فريقنا وتمريرها إلى 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 تبدو واعدة! زوجان من أسئلة المتابعة إذا كان لديك وقت:

  1. ما تفعله هنا هو إنشاء موقع ثابت؟ أي عند استخدام next export ؟
  2. هل فهمت الأمر بشكل صحيح ، أن getStaticPaths يعرض قائمة بمعلمات المسار ، والتي يتم بعد ذلك (حسب التالي) تغذيتها ، واحدة تلو الأخرى ، إلى getStaticProps لكل عملية تصيير؟
  3. هل يمكنك استخدام getStaticProps بدون getStaticPaths ، على سبيل المثال لصفحة بدون أي معلمات؟
  4. هل يمكنك استخدام getStaticProps في _app ؟ على سبيل المثال ، إذا كان لديك بعض التكوين على مستوى الموقع الذي ترغب في تحميله أو شيء من هذا القبيل؟

ماذا عن apis ؟؟ هذه الخطافات مخصصة للصفحات ، لكن ماذا عن apis؟

انا مرتبك. تمكنت من تعيين _dirname كمتغير env في التكوين التالي. لذلك تمكنت من الوصول إلى نظام الملفات من واجهة برمجة التطبيقات ، لكنها عملت محليًا فقط. بعد نشره حتى الآن ، حصلت على خطأ. هل لديك أي أفكار عن سبب عدم نجاحها بعد النشر؟

@ josias-r تكمن المشكلة الرئيسية عادةً في أن الملفات المراد قراءتها لا يتم تضمينها في النشر ، ولكن ذلك يعتمد على كيفية تضمينها وأنواع الملفات الموجودة فيها ( js / json عادةً ما يكون جيدًا ، ولكن أنواع الملفات الأخرى مثل .jade ستتطلب طرقًا بديلة للتعامل مع ملفه ، مثل استخدام @now/node lambda / نشر منفصل لقراءة / التعامل مع هذه الملفات).

إذا كان بإمكانك شرح المزيد عن الخطأ ، فربما يمكن لشخص ما مساعدتك.

BrunoBernardino كان يشير في الواقع إلى ملفات JSON داخل مجلد src الخاص بي. لكنها في الواقع طريقة fs.readdirSync(my_dirname_env_var) التي فشلت بالفعل في النشر. لذلك لا يبدو أن دير موجود على الإطلاق بعد النشر. هذا ما أحصل عليه عندما أحاول الوصول إلى المسار الكامل إلى json vis API الخاص بي:

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

وكما ذكرت ، يعمل هذا محليًا عندما أقوم بإنشاء ثم تشغيل npm start .

@ josias-r شكرا! هل حاولت إجراء fs.readdirSync بمسار نسبي (بدون متغيرات) بدلاً من ذلك (فقط لتصحيح أخطاء النشر)؟ لقد وجدت أنه يعمل عادةً ، وإذا كان الأمر كذلك ، فيمكنك كتابة هذا الجزء من الكود (فقط قراءة الملف ، وليس تخزينه في أي مكان) في مكان ما في عملية التهيئة ( getInitialProps أو شيء من هذا القبيل) ، بحيث تلتقط عملية النشر أنها تحتاج إلى هذا الملف ، ثم تستمر في قراءته باستخدام var في الكود / المنطق الفعلي. إنه ليس أنيقًا ، لكنه يعمل حتى يتم دعمه. أعتقد أن استخدام __dirname يعمل أيضًا في بعض الحالات.

BrunoBernardino لقد تمكنت من إنشاء شجرة ملفات بدءًا من المسار النسبي للجذر ./ . ما حصلت عليه هو JSON التالي (بدون إدراج وحدات العقدة):

{
  "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"
    }
  ]
}

يبدو أن ملف JSON مفقود هناك ، هل حاولت تضمينه عبر الكود كما اقترحت أعلاه؟ المشكلة الرئيسية هي أن التحسينات التي يتم تشغيلها لا تلتقط دائمًا المسارات الديناميكية ، على ما أعتقد ، لذا فإن فرض مسار ثابت قد نجح معي في الماضي (ليس بالضرورة لتشغيل الكود الفعلي ، ولكن للتأكد من الملفات ذات الصلة مدرجة). هل هذا منطقي؟

BrunoBernardino لقد قمت بالتبديل إلى حل غير API. نظرًا لأنني أرغب ديناميكيًا في طلب الملفات من مجلد وأحتاج فقط إلى محتوى هذه الملفات ، فأنا قادر على استخدام طريقة import() . لم أرغب في فعل ذلك بهذه الطريقة ، لأنه يبدو متطفلًا ، لكنه في الأساس يقوم بنفس الشيء الذي كانت ستفعله نقطة نهاية API الخاصة بي.
... حاولت وضع الملف في المجلد الثابت ولكن هذا لم ينجح أيضًا. لكنني آمل أن يكون الوصول إلى نظام الملفات ممكنًا في المستقبل.

لقد اضطررت أيضًا إلى اللجوء إلى حلول الاختراق ، ولكن آمل أن يصل هذا قريبًا وسيبدأ المزيد من الأشخاص في رؤية التالي باعتباره جاهزًا للإنتاج حيث يتم دعم حالات الاستخدام هذه "كما هو متوقع".

الحل الذي أستخدمه:

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

وفي الموقع تحتاج المسار

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'))

أعلم أن هذا لا يحل الحاجة إلى الرجوع إلى الملفات ذات المسارات المتعلقة بالملف الحالي ، ولكن هذا يحل حالة الاستخدام ذات الصلة (قراءة ملفات الصور من مجلد /public/images ).

رجل رائع. عملت من أجلي.

إنه يعمل بشكل مثالي على التنمية المحلية ، على الرغم من أنه لا يبدو أنه يعمل عند النشر إلى 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'
}

أتفهم أنه يتم نقل المجلد العام إلى المسار ، لذا حاولت إجباره على البحث في المجلد الأساسي عند الإنتاج ولكن لا يزال لدي نفس النتيجة:

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 المشكلة التي تواجهها هناك لأنها ليست ملف .json أو .js أو .ts الملف. يتم "نشر" الملفات الموجودة ضمن /public إلى CDN ولكن ليس إلى lambda (AFAIK) ، لذلك في هذه الحالة تحتاج إما إلى نشر lambda ( @now/node ) مع includeFiles ، أو ، إذا كنت تحتاج فقط إلى هذا الملف الفردي ، فقم بتحويله إلى base64 واستخدمه كملف var (في ملف مخصص أم لا).

شكرًا لك BrunoBernardino الذي توقعته كثيرًا ، base64

إنه بعض الدقة لـ __dirname في بيئة النشر ؟؟

NicolasHz هل يمكنك أن تشرح بالتفصيل؟ لم أفهم سؤالك تمامًا.

BrunoBernardino بالنظر إلى التعليقات الأخيرة ، بما في ذلك التعليقات الخاصة بي ، فأنا متأكد تمامًا من أن اختراق "الخريطة _dirname في التكوين التالي" لا يعمل في النشر. حتى w / js و JSON. على الأقل بالنسبة لنشر now ، قد لا يتم احتساب ذلك لعمليات النشر المخصصة.

BrunoBernardino لست قادرًا على استخدام بعض المتغيرات التي تشير إلى المسار المحلي على البيئة المنتشرة. __dirname لم يتم تحديده بمجرد نشره ، ولا يمكنني قراءة ملف من البرامج النصية لـ apis الخاصة بي.

فهمت @ NicolasHz . نعم ، ستحتاج إلى اللجوء إلى أحد الحلول المذكورة أعلاه ، اعتمادًا على نوع الملف الذي تحتاج إلى قراءته / الوصول إليه.

فقط للتأكيد ، لا يعمل config.js على عمليات النشر.

الحل الذي أستخدمه:

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

وفي تعريف api حيث أحتاج إلى المسار (يحتوي مجلد allPosts على جميع المدونات بتنسيق markdown وهو موجود في جذر المشروع)

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

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

إنها تعمل بشكل مثالي على التنمية المحلية.
لكنها تعطي هذا الخطأ عند نشرها في zeit الآن.

[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 مثل BrunoQuaresma قال ، هذا الحل يعمل محليًا فقط. ما زلت أستخدم نشر @now/node منفصل لـ lambdas للوصول إلى نظام الملفات ، واستدعاء هذا الملف عبر طلب من التطبيق نفسه (أو إنشاء أي نتيجة ثابتة أحتاجها قبل النشر). كندة مجنونة ، لكنها تعمل.

مرحبًاBrunoBernardino ... هل تقصد مشروعًا منفصلاً بخادم عقدة مخصص؟

ومع ذلك ، لا أفهم سبب وجود إعداد " includeFiles " إذا كان من المستحيل الوصول إليها 🤔

valse يمكن أن يكون في نفس المشروع. هذا مقتطف من 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"
    }
  ]
}

بهذه الطريقة يمكنني الاتصال بهم عبر شيء مثل:

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

من داخل صفحة api التالية ، بافتراض أن لدي ملف lambdas/email.ts يتعامل مع إرسال رسائل البريد الإلكتروني والقراءة من ملفات القوالب مثل pug .

أتمنى أن يساعد ذلك!

أيضًا ، "includeFiles" تعمل فقط مقابل @now/node (ربما أخرى ، لكن ليس @now/next )

يبدو أن BrunoBernardino إذا كنت تستخدم وظائف node ، فلا يمكنها الآن قراءة ESM!

هذا ما يحدث عندما أحاول استيراد قائمة بصفحات mdx:

الشفرة

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([])
}

الخطأ الذي أحصل عليه:

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

SyntaxError: Unexpected token 'export'

talentlessguy لست عضوًا في فريق Zeit / Vercel ، مجرد عميل سعيد. يبدو أن هذا قد يكون أكثر ملاءمة لدعم العملاء ، حيث أرى بعض المشكلات المحتملة من هذا المقتطف فقط:

  1. قد ترغب في استخدام لا شيء أو استخدام __dirname بدلاً من process.cwd() للمسار الأساسي. لم أستخدم الأخير في lambdas ، لكن الآخرين ، لذلك لست متأكدًا مما إذا كانت هذه مشكلة أم لا
  2. أنت تستورد NextApiRequest و NextApiResponse كأنواع ، ولكن هذا يجب أن يتم تشغيله من @now/node" ، أليس كذلك؟ لذلك يجب استيراد الأنواع مثل:
import { NowRequest, NowResponse } from '@now/node';
  1. أنت تستورد / تقرأ من pages/... لكن هل تقوم بتضمينها عبر includeFiles ؟ كيف تبدو now.json ؟

تضمين التغريدة

لا يمكنني استخدام __dirname لأنه دائمًا / ، بدلاً من ذلك process.cwd() ، يظهر المسار الحقيقي

قبلت إصلاحاتك ونجحت:

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([])
}

الآن. 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"
    }
  ]
}

خطأ أحصل عليه:

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

SyntaxError: Cannot use import statement outside a module

يبدو أن وظيفة العقدة التي يتم طباعتها لا يمكنها التعامل مع .mdx كوحدة: :(

حسنًا ، يبدو أنك وجدت المشكلة. حاول قراءة محتويات الملف وتحليلها بدلاً من الاستيراد مباشرة. لم أر قط استيرادًا مثل هذا العمل ، ويبدو أنه شيء من شأنه أن يعمل فقط مع بعض سحر Babel ، والذي يمكنك أيضًا استخدامه بدلاً من TS العادي.

BrunoBernardino أنت على حق ، لكن الأمر ليس عاديًا ... لدي الهدف المحدد على esnext والوحدة النمطية إلى esnext أيضًا ، يجب أن يكون قادرًا على استيراد كل شيء ... ولكن بطريقة ما لا

على أي حال ، لا يتعلق الأمر بالمشكلة ، فقم بالبحث عنه في موقع Google في مكان ما

لا داعى للقلق. قد تكون بعض النصائح في https://mdxjs.com/advanced/typescript و https://mdxjs.com/getting-started/webpack مما قد يجعل الأمر يتطلب تعديل نشر @now/node إلى استخدمه. على أي حال ، يجب أن يكون دعمهم مفيدًا.

أي حركة على هذا؟ سيكون من الرائع أن تكون قادرًا على تضمين قوالب بريد إلكتروني بتنسيق html لاستخدامها في مسارات واجهة برمجة التطبيقات. في الوقت الحالي ، أقوم بتضمينهم في ملفات JS ولكني لست من المعجبين بهذا الاختراق.

الاختراق الآخر هو استخدام أداة تحميل خام webpack لتضمينها في js.

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

ثم استخدم templates.verify كسلسلة.

هناك مشكلة تحدث مع next-i18next ويبدو أنها مرتبطة بهذه المشكلة ( now لا يضع ملفات .json الموجودة داخل /public/static/locales/ في وظيفة بدون خادم. هل يمكن لأي شخص توفير حل بديل حتى تتم إضافة الميزة التي تمت مناقشتها هنا إلى التالي؟

borispoehland هل جربت الاستيراد / تتطلب الحلول البديلة من الأعلى؟ التي يجب أن تعمل.

borispoehland هل جربت الاستيراد / تتطلب الحلول البديلة من الأعلى؟ التي يجب أن تعمل.

BrunoBernardino لا أعرف بالضبط ما الذي تعنيه التعليق.

هل يمكن أن تعطيني مثالاً على استيراد جميع ملفات .json داخل public/static/locales إلى دالة بدون خادم؟ وأين تفعل هذا (في أي ملف)؟

أنا أستخدم التالي (كما ذكرت سابقًا ، includeFiles غير متوافق مع @now/next ، idk إذا كان لهذا أي تأثير على مشكلتي).

بالإضافة إلى ذلك ، نظرًا لأن next-i18next هو نوع من الصندوق الأسود بالنسبة لي (وبالتالي لا أريد استيراد الملفات من هناك) ، فأنا أبحث عن طريقة لاستيرادها بالكامل حتى يتمكن next-i18next القيام بذلك مباشرة الوصول إليها (في التعليقات الأخرى أعلاه ، في بعض الأحيان تم تحديد PROJECT_DIRNAME داخل next.config.json وكان يجب أن يتم الاستيراد يدويًا. هذا ليس ما أحاول الوصول إليه). كما هو الحال في vercel / vercel # 4271 ، أريد فقط now لأخذ ملفات .json إلى وظيفة بدون خادم بطريقة ما.

borispoehland في ملف _any_ داخل pages/api (أو يتم استدعاؤه من قبل أحد هناك) ، افعل شيئًا مثل https://github.com/vercel/next.js/issues/8251#issuecomment -544008976

لا تحتاج إلى فعل أي شيء مع الاستيراد. النقطة المهمة هي أن Webpack vercel الذي يتم تشغيله سيشاهد بعد ذلك تلك الملفات التي يجب تضمينها ، ويجب أن تعمل.

وآمل أن يجعل الشعور.

borispoehland في ملف _any_ داخل pages/api (أو يتم استدعاؤه من قبل أحد هناك) ، افعل شيئًا مثل # 8251 (تعليق)

لا تحتاج إلى فعل أي شيء مع الاستيراد. النقطة المهمة هي أن Webpack vercel الذي يتم تشغيله سيشاهد بعد ذلك تلك الملفات التي يجب تضمينها ، ويجب أن تعمل.

وآمل أن يجعل الشعور.

BrunoBernardino المشكلة في هذا النهج هي أن لدي الكثير من ملفات json. يعد القيام بالاستيراد يدويًا لكل ملف أمرًا مرهقًا. هل هناك طريقة أسهل لإخبار now : "مرحبًا ، يُرجى التقاط جميع ملفات json داخل هذا الدليل بشكل متكرر"؟ شكرا لك مقدما

تحرير: حتى استيراد ملفات json يدويًا ينتج عنه نفس الخطأ من ذي قبل. سأفتح عددًا جديدًا لهذا ، على ما أعتقد

فتحت عددًا جديدًا لمشكلتي ، في حال كان أحدهم مهتمًا بالانضمام إلى المناقشة. شكرًا الآن ، BrunoBernardino !

خيار / حل بديل آخر لتمكين القدرة على استخدام __dirname كما تتوقع عادةً هو تعديل تهيئة حزمة الويب.

بشكل افتراضي ، ستقوم حزمة الويب بتسمية العديد من Node globals مع polyfills ما لم تخبرها بعدم القيام بما يلي:
https://webpack.js.org/configuration/node/
والإعدادات الافتراضية لحزمة الويب هي ترك __dirname و __filename بمفردهما ، أي عدم ملءهما وترك العقدة تتعامل معها كالمعتاد.

ومع ذلك ، لا يستخدم تكوين حزمة الويب Next.js / يعكس إعدادات حزمة الويب الافتراضية https://github.com/vercel/next.js/blob/bb6ae2648ddfb65a810edf6ff90a86201d52320c/packages/next/build/webpack-config.ts#L661 -L663

كل ما قيل ، لقد استخدمت المكون الإضافي التالي المخصص أدناه لضبط تهيئة حزمة الويب.

هام: هذا يعمل مع حالة الاستخدام الخاصة بي. لم يتم اختباره في مجموعة واسعة من البيئات / التكوينات ولم يتم اختباره مقابل جميع اختبارات الوحدة / التكامل Next.js. قد يكون لاستخدامه آثار جانبية غير مقصودة في بيئتك.
أيضًا ، قد يكون لـ Next أسباب محددة لعدم استخدام إعدادات webpack الافتراضية لـ __dirname و __filename . مرة أخرى ، قد يكون للرمز أدناه آثار جانبية غير مقصودة ويجب استخدامه بحذر.

أيضًا ، تم تصميم المكون الإضافي أدناه للاستخدام مع حزمة next-compose-plugins : https://github.com/cyrilwanner/next-compose-plugins

ولكن يجب أن تعمل كمكوِّن إضافي عادي أيضًا: 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;
    },
  });
};

لقد نفذت الحل بواسطة jkjustjoshing ، وعلى الرغم من أنه يعمل بشكل رائع محليًا ، إلا أنه لا يعمل عندما أنشر التطبيق على Vercel.

أحصل على الخطأ التالية:

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

رمز بلدي:

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

إليك رابط الملف: https://github.com/bengrunfeld/trend-viewer/blob/master/pages/api/graphql-data.js

bengrunfeld نعم ، الحل الخاص بك يعمل محليًا فقط.

واجهت مشكلة مماثلة مؤخرًا (أردت قراءة ملف في مسار واجهة برمجة التطبيقات) وكان الحل أسهل من المتوقع.

جرب path.resolve('./public/ts-data.csv')

borispoehland شكرا جزيلا لك !! الحل الخاص بك يعمل بشكل جميل!

bengrunfeld لا توجد مشكلة ، لقد اكتشفت ذلك أيضًا بالصدفة ( BrunoBernardino ؛)). أعتقد أن هذا هو الحل لمشكلة الجميع هنا.

يرجى ملاحظة أنك لا تزال بحاجة إلى تعيين next.config.js . لقد حذفت الملف بعد أن رأيت أن حل borispoehland يعمل ، وتلقيت خطأً مماثلاً.

ثم أعدت تعيينه إلى حل

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

يرجى ملاحظة أنك لا تزال بحاجة إلى تعيين next.config.js . أزلته بعد أن رأيت أن حل borispoehland يعمل ، وتلقيت خطأً مماثلاً.

أعدت تعيينه إلى حل

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

bengrunfeld حقًا؟ ربما لا تزال تستخدم طريقة PROJECT_ROOT في نقطة أخرى في الكود ، لأنها تعمل بدونها في مشروعي. كيف يبدو الخطأ؟

عند النشر إلى Vercel ، كيف أكتب readFile في صفحة تعمل في وضع SSG و SSR / Preview؟

المستودع التجريبي الذي لا يعمل فيه: https://github.com/mathdroid/blog-fs-demo

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

mathdroid حاول نقل readFile و readdir داخل الدالتين getStaticProps و getStaticPaths ، على التوالي ، وإلا فقد يتم تشغيل الكود في المتصفح.

يجب أن يكون استيراد fs جيدًا في المقدمة.

دعنا نعرف كيف يعمل ذلك.

borispoehland شكرا على الحل الرائع. لم أكن أتوقع أن path.resolve() إلى /public سيعمل محليًا وعلى Vercel: eyes :! أنت منقذي لهذا اليوم. : +1:

borispoehland لقد جربت الحل الخاص بك داخل وظيفة لا
موجود: لا يوجد مثل هذا الملف أو الدليل ، افتح "/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;
}

لقد حاولت مع ملف next.config.js الخاص بنا

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

ربما لا يعمل الحل الخاص بك على وظائف بدون خادم؟

borispoehland لقد جربت الحل الخاص بك داخل وظيفة لا
موجود: لا يوجد مثل هذا الملف أو الدليل ، افتح "/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;
}

لقد حاولت مع ملف next.config.js الخاص بنا

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

ربما لا يعمل الحل الخاص بك على وظائف بدون خادم؟

لا أعرف لماذا لا يعمل من جانبك ... آسف

حسنًا ، لقد نجحت في القراءة باستخدام كود
[خطأ: EROFS: نظام ملفات للقراءة فقط ، افتح '/var/task/public/posts.json']
لذلك لا توجد طريقة لتحديث ذاكرة التخزين المؤقت لتجنب الكثير من استدعاءات قاعدة البيانات :(

neckaros هل حاولت استخدام أسلوبي لقراءة ملف غير .json ، على سبيل المثال ملف .jpg ؟

حسنًا ، لقد نجحت في القراءة باستخدام كود

[خطأ: EROFS: نظام ملفات للقراءة فقط ، افتح '/var/task/public/posts.json']

لذلك لا توجد طريقة لتحديث ذاكرة التخزين المؤقت لتجنب الكثير من استدعاءات قاعدة البيانات :(

neckaros ، يجب أن تكون قادرًا على الكتابة والقراءة من S3 (أو نظام ملفات خارجي آخر) ، لكنني عادةً ما أستخدم redis للأشياء السريعة المخبأة التي يمكن أن تكون متقلبة. https://redislabs.com يبقيه "بدون خادم" ، ولدي أمثلة تعليمات برمجية جاهزة للإنتاج في https://nextjs-boilerplates.brn.sh إذا كنت تريد.

borispoehland استطعت أن أقرأ ولكن لا أكتب من وظيفة خاملة. لكن انتهى بي الأمر إلى أنه يعمل عن طريق تحديث ذاكرة التخزين المؤقت الخاصة بي في الإنشاءات المتزايدة (إعادة التحقق) بدلاً من إضافة محتوى جديد. الذي أعتقد أنه ليس نمطًا سيئًا. شكرا لمساعدتك!

BrunoBernardino شكرا

أتطلع حقًا إلى حل هواة مجاني تمامًا لا ينكسر بمجرد أن يكون لديك عدد قليل من المستخدمين :)

انسخ هذا. فعل RedisLabs و Vercel ذلك من أجلي. 💯

بعد بعض الحفر حصلت على كتابة ملفات تعمل مع حزمة نظام التشغيل الموسعة ...

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)

قراءة ملف يعمل مع حل subwaymatch
const logoPath = path.resolve('./public/logo.png')

بعد بعض الحفر حصلت على كتابة ملفات تعمل مع حزمة نظام التشغيل الموسعة ...

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)

قراءة ملف يعمل مع حل subwaymatch
const logoPath = path.resolve('./public/logo.png')

رائع ، هل تستطيع إعادة قراءة محتويات هذا الملف؟ هل الدليل سهل الوصول إليه ودائم؟

marklundin مع وظيفة تسمى tmpdir أشك في أنها دائمة ، ولكن إذا نجح ذلك ، فسيكون من الجيد معرفة مدى المؤقت الفعلي لـ tmpdir ، نعم ... 🤔

أي تحديثات على هذا؟ أتساءل لماذا تعمل في getInitialProps ، لكن ليس في مسارات API 🤷‍♂️

الحل الحالي الخاص بي

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

تواجه حاليًا هذه المشكلة في مسارات API أيضًا

تواجه حاليًا هذه المشكلة في مسارات API أيضًا

توجد بعض الحلول العملية هنا ، ما المشكلة التي تواجهها على وجه التحديد؟

أنا أكافح من أجل الحصول على هذا العمل حتى مع الاقتراحات من هذا الموضوع. حالة الاستخدام الخاصة بي هي أنني أكتب دليلاً وأريد إظهار الكود المصدري للمكون جنبًا إلى جنب مع المكون نفسه. أسلوبي للقيام بذلك هو استخدام fs لتحميل ملف jsx الخاص بالمكون داخل getServerSideProps وتمرير قيمة السلسلة لمحتويات الملف كخاصية.

كنت أشعر بسعادة غامرة لأنني أعملها محليًا ، ولكن بعد ذلك عندما ذهبت لنشرها ، ذهبت الفرحة :(

يرجى الاطلاع على: https://github.com/ElGoorf/i18next-guide/blob/fix-example-components/pages/plurals.jsx

ElGoorf مشكلتك هي أن ملفات public موجودة على الحافة ، والوظائف موجودة على lambda. الآن ، لا يزال @vercel/next لا يسمح بـ includeFiles ، لذا فإن أسهل طريقة لتشغيلها هي استخدام دالة lambda معها.

إليك بعض نماذج التعليمات البرمجية التي ساعدت الآخرين هنا: https://github.com/vercel/next.js/issues/8251#issuecomment -614220305

شكرًا BrunoBernardino لم أدرك أنني فاتني "x من العناصر المخفية يتم تحميل المزيد ..." واعتقدت أنني أصبت بالجنون من الخيط فقد المعنى!

لسوء الحظ ، لقد كافحت مع الحل الخاص بك ، حيث إنها المرة الأولى التي أسمع فيها عن Edge / Lambda ، ومع ذلك ، وجدت أن حل balthild كان أقرب إلى ما كنت أتبعه في الأصل قبل تجربة طريقة node.fs: https: / /github.com/vercel/next.js/issues/8251#issuecomment -634829189

رائعة! هل وجدتها تعمل؟ أو هل ما زلت تواجه مشاكل؟

لست متأكدًا من أن Vercel يستخدم حتى هذه المصطلحات ، ولكن أعني من خلال Edge CDN ، حيث يتم تقديم الملفات الثابتة من ، وأعني بواسطة lambda وظائف "الواجهة الخلفية" التي يتم استدعاؤها من مسارات API ، والتي يتم عزلها مثل وظائف AWS Lambda .

مهلا،

أي تحديث في الكتابة إلى الملفات باستخدام next.js على vercel؟ لا أستطيع أن أقرأ أي مشكلة. باستخدام const logoPath = path.resolve('./public/logo.png')

أحاول الكتابة فوق الملف public / sitemap.xml (بسبب حدود الحجم على vercel) لا يمكنني إعادته إلا بدون أخطاء كملف ثابت في المجلد العام. لقد قمت سابقًا بتنفيذ خريطة الموقع باستخدام zlib وتدفق الاستجابة ولكن يبدو أنني انتظر حتى ينتهي البث ثم أعيده. هذا لا يصل إلى خطأ تحديد الحجم ، لكنه للأسف بطيء جدًا. أنا منفتح على أي اقتراحات قد تكون لدى الناس. تم إنشاء خريطة الموقع من استدعاء واجهة برمجة التطبيقات (API) إلى خلفية منفصلة ويجب تحديثها بانتظام.

الأشياء التي حاولت:

  • ضغط وتدفق ملف xml - يعمل ولكن ببطء شديد.
  • إنشاء خريطة موقع وإعادتها من وظيفة api ، للأسف هذا يصل إلى حد الحجم.
  • قراءة ملف xml ثابت من المجلد العام (يعمل) بغض النظر عن الحجم ، ولكن غير قابل للتحديث.
  • اختبار الكتابة لهذا الملف ، لا يعمل. اختبار الكتابة إلى أي ملف / مجلد لا يعمل
  • اختبار إرجاع ملف xml ثابت من وظيفة "api" ، خطأ في الحجم. هذا يعمل محليًا.
  • اختبار إرجاع ملف xml ثابت من خطأ حجم getServerSideProp "الصفحة". هذا يعمل محليًا.
  • هل نقدر أي أفكار على الإطلاق؟

مرحبًا emomooney ، لا أتخيل أن Vercel أبدًا سمح لكتابة الملفات في وظيفة (حتى للتخزين المؤقت) ، نظرًا لأن "الميزة" الرئيسية لعدم وجود خادم هي انعدام الحالة ، وهذا من شأنه أن يضيف حالة إلى ذلك ، لذلك أعتقد أنك بحاجة إلى استخدام حافة / cdn لذلك.

لقد قمت سابقًا بتنفيذ خريطة الموقع باستخدام zlib وتدفق الاستجابة ولكن يبدو أنني انتظر حتى ينتهي البث ثم أعيده.

أشعر بالفضول إذا كنت تعاني للتو من هذا البطء في المكالمات اللاحقة ، أو فقط الأولى ، من أجل البداية الباردة؟ أتخيل أن هذا كان استدعاء API لـ Vercel عبر دالة next.js api ، أو lambda مخصصة ، على غرار ما أفعله هنا .

إذا كان الأمر كذلك ولا يزال بطيئًا جدًا ، فهل "الواجهة الخلفية المنفصلة" خارج Vercel؟ إذا كان الأمر كذلك ، فمن المحتمل أن تستخدمه لإنشاء ملف sitemap.xml و vercel --prod في مجال ، بشكل أساسي "التخزين المؤقت" للملف ليكون قابلاً للقراءة ويمكن الوصول إليه ، وستحتاج فقط إلى التحديث robots.txt لربط خريطة الموقع بمجال / مجال فرعي آخر.

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات