В настоящее время невозможно читать файлы из маршрутов или страниц API.
Я хочу иметь возможность вызывать 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
, но дело не в этом. 😄
Просто хотел поддержать это, пытаясь реализовать загрузку файлов с использованием маршрутов API. Я могу получить файл для загрузки, но затем мне потребуется снова получить к нему доступ, чтобы загрузить его в корзину S3.
Я поддерживаю это! Кроме того, возможность читать каталоги очень важна для использования моей компанией, поскольку мы храним наши данные, такие как члены команды и сообщения в блогах, в каталоге содержимого, поэтому мы ищем способ потребовать все файлы в каталоге.
Вышеупомянутый PR исправит это! ☝️ 🙏
Как насчет 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
).
Видел, что в PR это немного изменилось - есть ли обновления о текущих планах (или нет)? Похоже, есть некоторые стратегии, которых вы не хотите придерживаться, не забудьте перечислить их + почему участники могут попробовать?
Это блокирует использование нексуса с 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 У меня _все_ вопросы 😛
src
?)@Svish Мы храним файлы данных в / data внутри нашего проекта. (Страницы находятся в / pages, а не в / src / prages.) Этот компонент страницы выглядит следующим образом (реквизиты отправляются в компонент Home, который является экспортом по умолчанию):
// /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 Выглядит многообещающе! Если у вас есть время, задайте пару дополнительных вопросов:
next export
?getStaticPaths
возвращает список параметров пути, который затем (к следующему) передается, один за другим, в getStaticProps
для каждого рендеринга?getStaticProps
без getStaticPaths
, например, для страницы без каких-либо параметров?getStaticProps
в _app
? Например, если у вас есть конфигурация для всего сайта, которую вы хотите загрузить, или что-то в этом роде?А как насчет API ?? Эти хуки предназначены для страниц, но как насчет API?
Я запутался. Мне удалось установить _dirname как переменную env в следующей конфигурации. Поэтому я смог получить доступ к файловой системе из API, но он работал только локально. После развертывания до настоящего момента я получил ошибку. Есть идеи, почему он не работает после развертывания?
@ josias-r основная проблема обычно заключается в том, что файлы для чтения не включены в развертывание, но это зависит от того, как вы их включаете и к каким типам файлов они относятся ( js
/ json
обычно нормально, но для других типов файлов, таких как .jade
, потребуются альтернативные способы работы с ним, например, использование отдельного @now/node
lambda / deployment для чтения / обработки этих файлов).
Если вы можете подробнее объяснить ошибку, возможно, кто-то сможет вам помочь.
@BrunoBernardino На самом деле речь шла о файлах JSON внутри моей папки src. Но на самом деле даже метод fs.readdirSync(my_dirname_env_var)
уже терпит неудачу при развертывании. Так что после развертывания этот каталог, похоже, вообще не существует. Вот что я получаю, когда пытаюсь получить доступ к полному пути к json через мой 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 как готовую к производству, поскольку эти варианты использования будут поддерживаться «как ожидалось».
Обходной путь, который я использую:
# 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, но не в лямбда-выражении (AFAIK), поэтому в этом случае вам потребуется либо выделенное развертывание лямбда-выражения ( @now/node
) с includeFiles
, или, если вам нужен только этот единственный файл, преобразуйте его в base64
и используйте его как переменную (в выделенном файле или нет).
Спасибо, @BrunoBernardino этого и ожидал, я буду использовать метод base64
Это какое-то разрешение __dirname в развернутой среде ??
@NicolasHz, ты можешь уточнить? Я не совсем понял ваш вопрос.
@BrunoBernardino Глядя на последние комментарии, включая мой, я почти уверен, что хак "map _dirname
в следующей конфигурации" не работает при развертывании. Даже с файлами j / js и JSON. По крайней мере, для развертывания now
, которое, вероятно, не учитывается для пользовательских развертываний.
@BrunoBernardino Я не могу использовать некоторые переменные, указывающие на локальный путь в развернутом окружении. __dirname он не определен после развертывания, и я не могу прочитать файл из моих сценариев apis.
Понял @NicolasHz . Да, вам нужно прибегнуть к одному из вышеперечисленных решений, в зависимости от того, к какому типу файла вам нужно прочитать / получить доступ.
Просто подтверждаю, config.js не работает с развертываниями.
Обходной путь, который я использую:
# next.config.js
module.exports = {
env: {
PROJECT_DIRNAME: __dirname,
},
}
и в определении api, где мне нужен путь (папка allPosts содержит все блоги в формате уценки и находится в корне проекта)
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
для лямбда-выражений для доступа к файловой системе и вызываю этот файл по запросу из самого приложения (или генерирую любой статический результат, который мне нужен перед развертыванием). Какое-то безумие, но это работает.
Привет, @BrunoBernardino ... Вы имеете в виду отдельный проект с настраиваемым сервером узлов?
Однако я не понимаю, зачем нужен параметр "
@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, я просто счастливый клиент. Похоже, это лучше подходит для их поддержки клиентов, поскольку я вижу несколько потенциальных проблем только из этого фрагмента:
__dirname
вместо process.cwd()
в качестве базового пути. Я не использовал последний в лямбдах, но другие, поэтому я не уверен, проблема в этом или нет.NextApiRequest
и NextApiResponse
как типы, но это должно быть запущено из @now/node"
, верно? Таким образом, типы должны быть импортированы как:import { NowRequest, NowResponse } from '@now/node';
pages/...
но включаете ли вы их через includeFiles
? Как выглядит ваш now.json
?@BrunoBernardino
Я не могу использовать __dirname
потому что вместо этого всегда /
, process.cwd()
, показывает реальный путь
Я принял исправления ur, и это сработало:
лямбды / 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([])
}
now.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, вы правы, но это не просто ts ... У меня есть цель, установленная на esnext, и модуль на esnext, он должен иметь возможность импортировать все ... но почему-то это не так
в любом случае это не связано с проблемой, собираюсь где-нибудь погуглить
Не стоит беспокоиться. Несколько советов могут быть в https://mdxjs.com/advanced/typescript и https://mdxjs.com/getting-started/webpack, которые могут сделать так, чтобы развертывание @now/node
нужно было настроить, чтобы используй это. В любом случае их поддержка должна помочь.
какие-нибудь движения по этому поводу? Было бы здорово иметь возможность включать HTML-шаблоны электронной почты для использования в API Routes. Прямо сейчас я включаю их в файлы JS, но я не особо фанат этого хака.
Еще один прием - использовать raw-loader webpack для встраивания их в js.
yarn add --dev raw-loader
const templates = {
verify: require("raw-loader!../template/email/verify.hbs").default,
};
Затем используйте templates.verify
как строку.
Возникла проблема с next-i18next, которая, похоже, связана с этим ( vercel / vercel # 4271 ). Обычно 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 в pages/api
(или который вызывается одним из них), сделайте что-то вроде https://github.com/vercel/next.js/issues/8251#issuecomment -544008976
С импортом ничего делать не нужно. Дело в том, что запускаемая версия веб-пакета затем увидит, что эти файлы необходимо включить, и он должен работать.
Я надеюсь, что в этом есть смысл.
@borispoehland в
pages/api
(или тот, который там вызывается ), сделайте что-нибудь вроде # 8251 (комментарий)С импортом ничего делать не нужно. Дело в том, что запускаемая версия веб-пакета затем увидит, что эти файлы необходимо включить, и он должен работать.
Я надеюсь, что в этом есть смысл.
@BrunoBernardino проблема с этим подходом в том, что у меня много файлов json. Выполнение импорта вручную для каждого файла довольно обременительно. Есть ли более простой способ сказать now
: «Эй, пожалуйста, рекурсивно извлеките все файлы json из этого каталога»? заранее спасибо
Изменить: даже ручной импорт файлов json
приводит к той же ошибке, что и раньше. Я собираюсь открыть для этого новый выпуск, я думаю
Я открыл новый выпуск по своей проблеме , на случай, если кто-то захочет присоединиться к обсуждению. Спасибо, @BrunoBernardino !
Другой вариант / обходной путь, позволяющий использовать __dirname
как вы обычно ожидаете, - это настроить конфигурацию webpack.
По умолчанию webpack будет связывать различные глобальные объекты Node с полифилами, если вы не укажете ему:
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 для настройки конфигурации webpack.
ВАЖНО: это работает для моего варианта использования. Он не тестировался в широком диапазоне сред / конфигураций и не был протестирован со всеми модульными / интеграционными тестами Next.js. Его использование может иметь непреднамеренные побочные эффекты в вашей среде.
Кроме того, у Next могут быть определенные причины не использовать настройки веб-пакета по умолчанию для__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 да, ваше решение работает только локально.
В последнее время у меня была аналогичная проблема (хотел прочитать файл в маршруте API), и решение оказалось проще, чем ожидалось.
Попробуйте path.resolve('./public/ts-data.csv')
@borispoehland Большое вам спасибо !! Ваше решение прекрасно сработало!
@bengrunfeld без проблем, я тоже узнал это по совпадению ( @BrunoBernardino ;)). Думаю, это решение всех проблем.
Обратите внимание, что вам все равно нужно установить next.config.js
. Я удалил файл после того, как увидел, что решение @borispoehland работает, и получил аналогичную ошибку.
Затем я сбросил его на решение @jkjustjoshing выше, снова развернул в Vercel, и он сработал.
# next.config.js
module.exports = {
serverRuntimeConfig: {
PROJECT_ROOT: __dirname
}
}
Обратите внимание, что вам все равно нужно установить
next.config.js
. Я удалил его после того, как увидел, что решение @borispoehland работает, и получил аналогичную ошибку.Я сбросил его на решение @jkjustjoshing выше, снова развернул в Vercel, и оно сработало.
# next.config.js module.exports = { serverRuntimeConfig: { PROJECT_ROOT: __dirname } }
@bengrunfeld Правда? Возможно, вы все еще используете подход PROJECT_ROOT
в другом месте кода, потому что в моем проекте он работает без него. Как выглядит ошибка?
При развертывании в Vercel, как мне написать readFile
на странице, которая будет работать как в SSG, так и в режиме SSR / предварительного просмотра?
Демо-репозиторий, где он не работает: https://github.com/mathdroid/blog-fs-demo
@mathdroid попробуйте переместить readFile
и readdir
внутри функций getStaticProps
и getStaticPaths
соответственно, иначе код может работать в браузере.
Тем не менее, импорт fs
должен быть в порядке.
Сообщите нам, как это работает.
@borispoehland Спасибо за прекрасное решение. Не ожидал, что path.resolve()
to /public
будет работать как локально, так и на Vercel: eyes :! Ты мой спаситель на день. : +1:
@borispoehland Я пробовал ваше решение внутри бессерверной функции, но все равно получил:
ENOENT: нет такого файла или каталога, откройте '/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 Я пробовал ваше решение внутри бессерверной функции, но все равно получил:
ENOENT: нет такого файла или каталога, откройте '/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 } }
Может ваше решение не работает на бессерверных функциях?
Я не знаю, почему это не работает с твоей стороны ... Извини
Хорошо, я заставил его работать для чтения, используя код @bengrunfeld, но, к сожалению, вы не можете писать:
[Ошибка: EROFS: файловая система только для чтения, откройте '/var/task/public/posts.json']
Так что нет возможности обновить кеш, чтобы избежать слишком большого количества обращений к базе данных :(
@neckaros пробовали ли вы использовать мой подход для чтения файла, отличного от .json
, например файла .jpg
?
Хорошо, я заставил его работать для чтения, используя код @bengrunfeld, но, к сожалению, вы не можете писать:
[Ошибка: 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
находятся на краю, а функции находятся на лямбде. Теперь @vercel/next
прежнему не позволяет использовать includeFiles
, поэтому самый простой способ заставить его работать - использовать с ним функцию lambda
.
Вот пример кода, который помог другим здесь: https://github.com/vercel/next.js/issues/8251#issuecomment -614220305
Спасибо @BrunoBernardino. Я не осознавал, что пропустил «x скрытых элементов загружают больше ...» и подумал, что схожу с ума от темы, теряющей смысл!
К сожалению, у меня возникли проблемы с вашим решением, поскольку я впервые слышу о Edge / Lambda, однако я обнаружил, что решение https: / /github.com/vercel/next.js/issues/8251#issuecomment -634829189
Большой! Получилось ли у вас работать? Или у вас все еще есть проблемы?
Я не уверен, что Vercel вообще использует эту терминологию, но под Edge я подразумеваю CDN, откуда отправляются статические файлы, а под лямбдой я подразумеваю «внутренние» функции, которые вызываются из маршрутов API, которые изолированы, как функции AWS Lambda. .
Привет,
Есть какие-нибудь обновления по записи в файлы с помощью next.js на Vercel? Я не умею читать. Использование const logoPath = path.resolve('./public/logo.png')
Я пытаюсь перезаписать файл public / sitemap.xml (из-за ограничений размера на vercel), я могу вернуть его без ошибок только как статический файл в общей папке. Я ранее реализовал карту сайта с помощью zlib и потоковой передачи ответа, но, похоже, он ждет завершения потока, а затем возвращает его. Это не касается ошибки ограничения размера, но, к сожалению, очень медленно. Я открыт для любых предложений, которые могут быть у людей. Карта сайта создается из вызова API к отдельной серверной части и требует регулярного обновления.
Я пытался:
Привет, @emomooney , я не представляю, что Vercel когда-либо позволит записывать файлы в функции (даже для кеширования), поскольку главное «преимущество» бессерверного режима - это отсутствие состояния, и это добавило бы к нему состояние, поэтому я думаю, что вы для этого нужно использовать edge / cdn.
Я ранее реализовал карту сайта с помощью zlib и потоковой передачи ответа, но, похоже, он ждет завершения потока, а затем возвращает его.
Мне любопытно, испытывали ли вы эту медлительность только при последующих вызовах или только при первом холодном запуске? Я предполагаю, что это был API-вызов Vercel через функцию api next.js или выделенную лямбду, похожую на то, что я делаю здесь .
Если это было и все еще было слишком медленно, находится ли ваш «отдельный сервер» за пределами Vercel? Если это так, вы можете потенциально использовать его для создания файла sitemap.xml
и vercel --prod
в домене, в основном "кэшируя" файл, чтобы он был доступен для чтения и доступа, и вам просто нужно обновить robots.txt
чтобы связать карту сайта с другим доменом / субдоменом.
Самый полезный комментарий
Обходной путь, который я использую:
и в нужном месте путь
Я знаю, что это не решает необходимости ссылаться на файлы с путями относительно текущего файла, но это решает мой очень связанный вариант использования (чтение файлов изображений из папки
/public/images
).