Next.js: So transpilieren Sie Serverdateien

Erstellt am 15. Apr. 2017  ·  24Kommentare  ·  Quelle: vercel/next.js

Hallo,

Ich möchte import Syntax in meinem benutzerdefinierten Server verwenden&Routing ist das möglich? Ich benötige mein Modul, das import s verwendet, und erhalte diese Fehlermeldung.

(function (exports, require, module, __filename, __dirname) { import Router from 'koa-router'
                                                              ^^^^^^
SyntaxError: Unexpected token import

Ich habe versucht, meine .babelrc zu bearbeiten, aber ohne Erfolg.

{
  "plugins": [
    ["module-resolver", {
      "root": ["./"]
    }]
  ],
  "presets": [
    "next/babel",
    "es2015",
    "stage-0"
  ]
}

Hilfreichster Kommentar

Ich wäre auch gespannt, ob es einen "offiziell empfohlenen" Weg gibt, dies ohne schreckliche Leistung, Inkonsistenzen in Ihrem Projekt und viele Babel-Deps zu erreichen.

Ich denke, genug NextJs-Benutzer wollen dies, dass es einen relativ einfachen Weg geben sollte, dies zu erreichen, ohne dass jeder seine eigene Rolle spielt.

Alle 24 Kommentare

Sie müssen es selbst implementieren, entweder mit nur babel oder mit einer benutzerdefinierten Webpack-Konfiguration. Next transpiliert Ihren benutzerdefinierten Server nicht.

@sergiodxa Gibt es eine Dokumentation oder Ressourcen dazu, wie dies möglich ist?

Ich habe auch meine .babelrc-Konfiguration aktualisiert

{
  "plugins": [
    [
      "module-resolver",
      {
        "root": ["."],
        "alias": {
          "styles": "./styles"
        },
        "cwd": "babelrc"
      }
    ],
    [
      "wrap-in-js",
      {
        "extensions": ["css$"]
      }
    ]
  ],
  "presets": [
    "next/babel",
    "es2015",
    "stage-0",
  ],
  "ignore": []
}

Ich gehe davon aus, dass ich Babel mitteilen muss, welche Datei es "überwachen" und transpilieren soll?

Ja, sobald Sie eine benutzerdefinierte .babelrc-Datei haben, müssen Sie babel-cli für Ihre benutzerdefinierten Serverdateien ausführen. Aber ich würde Ihnen das nicht empfehlen, mit Node.js v7 haben Sie bereits viele ES2015 und größere Funktionen, Sie können require anstelle von Babel verwenden und das Transpile vermeiden.

Ihr universeller Code sollte von Next.js in Ihren Seiten und importierten Modulen verarbeitet werden. Ihr benutzerdefinierter Server sollte Node.js-kompatibel sein und nur benutzerdefiniertes Routing und andere wenige Dinge haben (möglicherweise einen Pass für die Authentifizierung).

@sergiodxa Bei der Verwendung von TypeScript ist die Entwicklungserfahrung ziemlich schrecklich. Sie müssen import-Anweisungen verwenden, um die Eingaben zu erhalten, und sie dann wiederholen, um die tatsächlichen Importe zu erhalten. Zum Beispiel:

import HTTP from 'http'
import URL from 'url'
import Next from 'next'

const { createServer } = require('http') as typeof HTTP
const { parse } = require('url') as typeof URL
const next = require('next') as typeof Next

Der Versuch, den serverseitigen Next-Code so zu gestalten, dass er dem Client-Code ähnelt, ist, soweit ich das beurteilen kann, praktisch unmöglich. Ich habe meine versucht kompilieren src/ Verzeichnis lib/ mit tsc , dann mit babel kompilieren lib auf out , und ich bekomme es einfach nicht zum Laufen. Ich muss es2015 und stage-0 Babel-Plugins einbinden, damit die Serverimporte funktionieren, aber dies scheint Probleme mit Next zu verursachen.

Wenn ich es6-Module mit Typoskript zu commonjs kompilieren lasse, anstatt es babel/webpack handhaben zu lassen, kann mein Servercode es6-Importe verwenden, aber ich kann in meinem Client keine dynamischen Importe mehr verwenden (wegen der speziellen SameLoopPromise Klasse, die Nächste Verwendungen).

Die Schlussfolgerung scheint also zu sein: "Nächstes nur für die Bereitstellung statischer Assets verwenden und ein völlig anderes Projekt mit einem völlig anderen Framework für Ihre API erstellen". Das scheint ein unglücklicher Ort für Next zu sein. Ich möchte ein Server-Framework, das SSR und andere Server-Aufgaben übernehmen kann. Plötzlich habe ich Mühe, den Wert zu sehen

@sbking Angesichts der Schwierigkeiten habe ich beschlossen, die es6-Importsyntax in meinem benutzerdefinierten Server nicht zu verwenden. Scheint es nicht wert zu sein.

@gragland Das habe ich auch so ziemlich entschieden, aber alle meine Importanweisungen verdoppeln zu müssen, um Eingaben zu erhalten, ist meiner Meinung nach ein schlechter Kompromiss. Es bedeutet auch, dass der einzige universelle Code in meiner Anwendung die Dinge sind, die direkt von Next verarbeitet werden.

Was ist die Lösung für das Schreiben einer Validierungsbibliothek, die sowohl auf dem Client als auch auf dem Server ausgeführt werden muss? Welchen Modulstil sollten Sie verwenden?

Die Antwort scheint, soweit ich das beurteilen kann, zu sein, nicht die Validierungsbibliothek im eigentlichen benutzerdefinierten Next-Server zu verwenden, sondern stattdessen eine separate Knotenanwendung nur für die API zu erstellen und dann git-Submodule oder private NPM-Module oder so etwas zu verwenden um die Validierungsbibliothek freizugeben. Plötzlich gibt es drei separate Projekte zu verwalten. Dies lässt mich den Nutzen von Next.js für alles andere als die grundlegendsten Anwendungen in Frage stellen.

@sergiodxa @gragland Okay, ein wenig mehr Optimierungen und ich denke, ich habe meine Probleme gelöst, sodass ich meinen Nur-Server-Code in src/api und meinen nächsten universellen Code in src/ui , und Ich kann immer noch ES6-Importe überall außer next.config.ts und dynamische import() Aufrufe weiterhin im UI-Code funktionieren lassen. Ich glaube, das Problem war, dass ich die next.config.ts Datei in mein UI-Projektunterverzeichnis (wo mein nächstes pages Verzeichnis lebt) und nicht in mein Repository-Stammverzeichnis legen musste. Hier sind die Einstellungen, die bei mir am Ende funktioniert haben:

package.json :

  "scripts": {
    "dev": "concurrently 'npm run compile-watch' 'npm run test-watch' 'npm run start-watch'",
    "lint-api": "tslint --type-check --project src/api",
    "lint-ui": "tslint --type-check --project src/ui",
    "lint": "npm run lint-api && npm run lint-ui",
    "compile-api": "tsc --project src/api",
    "compile-api-watch": "tsc --project src/api --watch",
    "compile-ui": "tsc --project src/ui",
    "compile-ui-watch": "tsc --project src/ui --watch",
    "compile-watch": "npm run compile-api-watch && npm run compile-ui-watch",
    "compile-only": "npm run compile-api && npm run compile-ui",
    "compile": "npm run lint && npm run compile-only",
    "test-watch": "jest --coverage --verbose --watchAll",
    "test-only": "jest --coverage --verbose",
    "test": "npm run compile && npm run test-only",
    "build-only": "next build out/ui",
    "build": "npm run test && npm run build-only",
    "start-watch": "nodemon -w out/api -w yarn.lock out/api/server.js",
    "start": "NODE_ENV=production node out/api/server.js"
  },

.babelrc :

{
  "presets": [
    "next/babel"
  ],
  "plugins": [
    ["module-resolver", {
      "root": ["./out"],
      "cwd": "babelrc"
    }]
  ]
}

tsconfig.base.json :

{
  "compilerOptions": {
    "moduleResolution": "node",
    "target": "esnext",
    "lib": ["esnext", "dom"],
    "allowSyntheticDefaultImports": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "experimentalDecorators": true
  }
}

src/api/tsconfig.json :

{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": {
    "module": "commonjs",
    "baseUrl": "..",
    "outDir": "../../out/api"
  },
  "include": [
    ".",
    "../types"
  ]
}

src/ui/tsconfig.json :

{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": {
    "module": "esnext",
    "baseUrl": "..",
    "outDir": "../../out/ui",
    "jsx": "react-native"
  },
  "include": [
    ".",
    "../types"
  ]
}

src/ui/next.config.ts :

import { Config } from 'next/config'

const config: Config = {
  distDir: '../../build',
  webpack: (config) => {
    if (config.resolve && config.resolve.alias) {
      delete config.resolve.alias['react']
      delete config.resolve.alias['react-dom']
    }
    return config
  }
}

module.exports = config

Normalerweise transpiliere ich Servercode wie diesen.
Stellen Sie sich vor, wir haben server.js mit schickem "ES-was auch immer" -Code, dann erstellen Sie zB index.js mit etwas wie:

require('babel-register')({
    babelrc: false, // Tell babel-register to ignore the .babelrc file
    presets: {
        'env': {
            'development': {'presets': ['react', 'stage-1', 'env', 'es2015']},
            'production': {'presets': ['react', 'stage-1', 'env', 'es2015']},
            'test': {'presets': [['react', 'stage-1', 'env', 'es2015', {'modules': 'commonjs'}]]}
        }
    },
    plugins: [['transform-runtime', {'polyfill': false, 'regenerator': true}]]
});
require('./server');

Und in package.json Sie Ihren Anwendungsstart wie folgt an:

"start": "NODE_ENV=production node ./server/index.js"

und das war's, du schreibst normalerweise alle ES*-Features auf der Serverseite

Ich habe es geschafft, meine mit dieser Konfiguration zum Laufen zu bringen.

// ./devServer.js
require('babel-register')({
  babelrc: false,
  presets: [
    [
      'env',
      {
        targets: {
          node: '8',
        },
      },
    ],
    'stage-3', // I use object-reset-spread 😀
  ],
})
require('./server')

Meine Server-"Quell"-Dateien sind in einem "Server"-Ordner innerhalb einer "index.js" enthalten, die der Einstiegspunkt ist. Ich transpiliere die Serverquelle in einen Ordner ".server", indem ich den Build-Schritt in der Vorbereitung für die Produktionsausführung verwende. Fügen Sie am besten den Ordner ".server" zu Ihrer gitignore-Liste hinzu.

Und dann in meinem package.json

json{ "scripts": { "dev": "NODE_ENV=development nodemon -w server devServer.js", "build:next": "NODE_ENV=production next build ./frontend", "build:server": "NODE_ENV=production babel -s -D -d .server server", "build": "npm run build:next && npm run build:server", "start": "NODE_ENV=production node .server" }

Eine andere Lösung mit next , express , babel und nodemon .

server.js

import express from 'express'
import next from 'next'

const port = parseInt(process.env.PORT, 10) || 3000
const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()

app
  .prepare()
  .then(() => {
    const server = express()

    server.get('*', (req, res) => {
      return handle(req, res)
    })

    server.listen(port, (err) => {
      if (err) throw err
      console.log(`> Ready on http://localhost:${port}`)
    })
  })

Paket.json

{
  "scripts": {
    "dev": "cross-env NODE_ENV=development nodemon server.js --exec babel-node --presets babel-preset-env --watch server.js",
    "build": "yarn build:next && yarn build:server",
    "build:next": "next build",
    "build:server": "cross-env NODE_ENV=production babel server.js --out-dir .next/dist",
    "start": "cross-env NODE_ENV=production node .next/dist/server.js"
  },
  "dependencies": {
    "cross-env": "^5.1.3",
    "express": "^4.16.2",
    "next": "^4.2.3",
    "react": "^16.2.0",
    "react-dom": "^16.2.0"
  },
  "devDependencies": {
    "babel-cli": "^6.26.0",
    "babel-preset-env": "^1.6.1",
    "nodemon": "^1.14.11"
  },
  "babel": {
    "env": {
      "development": {
        "presets": [
          "next/babel"
        ]
      },
      "production": {
        "presets": [
          "next/babel",
          [
            "env",
            {
              "targets": {
                "node": "8"
              }
            }
          ]
        ]
      },
      "test": {
        "presets": [
          [
            "next/babel",
            {
              "preset-env": {
                "modules": "commonjs"
              }
            }
          ]
        ]
      }
    }
  }
}

@sergiodxa / @timneutkens Soll ich ein Beispiel erstellen?

@ctrlplusb Arbeit für mich

@danielbayerlein könntest du ein Beispiel erstellen?

Ich bevorzuge den Ansatz von @danielbayerlein gegenüber dem, den ich gepostet habe.

Inspiriert von @danielbayerlein und falls jemand eine laufende Version mit next 6.0.3 package.json möchte, sollte das

{
  "scripts": {
    "dev": "NODE_ENV=development nodemon ./server.js --exec babel-node --presets @babel/preset-env --watch server.js",
    "build": "yarn build:next && yarn build:server",
    "build:next": "next build",
    "build:server": "NODE_ENV=production babel server.js --out-dir .next/dist",
    "start": "NODE_ENV=production node .next/dist/server.js"
  },
  "babel": {
    "env": {
      "development": {
        "presets": [
          "next/babel"
        ]
      },
      "production": {
        "presets": [
          "next/babel",
          [
            "@babel/env",
            {
              "targets": {
                "node": "8"
              }
            }
          ]
        ]
      }
    }
  },
  "dependencies": {
    "next": "^6.0.3",
    "react": "^16.4.1",
    "react-dom": "^16.4.1"
  },
  "devDependencies": {
    "@babel/cli": "^7.0.0-beta.44",
    "@babel/core": "^7.0.0-beta.44",
    "@babel/node": "^7.0.0-beta.44",
    "@babel/preset-env": "^7.0.0-beta.44",
    "nodemon": "^1.17.5"
  }
}

Tipp: Next.js verwendet ein einfaches require('build/...') , um eine Seite auf der Serverseite anzufordern. Wenn Sie einen benutzerdefinierten Server mit @babel/register , müssen Sie das Verzeichnis build und auch pages explizit ignorieren (da es von Webpack transpiliert wird).

Dies verhindert, dass @babel/register versucht, einen normalerweise sehr großen, bereits verketteten und transpilierten Code zu parsen. Verbessert die Startzeit.

Außerdem speichert @babel/register seinen Cache beim Beenden nicht richtig. Sie sollten require('@babel/register/lib/cache.js').save() manuell aufrufen, wenn Sie Ihre App beenden. Außerdem wird process.env.BABEL_CACHE_PATH verwendet, um das Cache-Verzeichnis festzulegen. Der Standardwert ist node_modules/.cache . Verbessert auch die Startzeit.

@vjpr Könnten Sie ein Codebeispiel bereitstellen, das das Ignorieren dieser Verzeichnisse und das Speichern des Cache beim Beenden behandelt?

Ist @babel/register etwas, für das ich mich entscheiden werde, wenn ich das Setup von Next verwende? Ich habe das Gefühl, dass wir möchten, dass unsere Server- und Client-Transpilation identisch sind.

Ich wäre auch gespannt, ob es einen "offiziell empfohlenen" Weg gibt, dies ohne schreckliche Leistung, Inkonsistenzen in Ihrem Projekt und viele Babel-Deps zu erreichen.

Ich denke, genug NextJs-Benutzer wollen dies, dass es einen relativ einfachen Weg geben sollte, dies zu erreichen, ohne dass jeder seine eigene Rolle spielt.

Der Hauptanwendungsfall ist wiederverwendbarer Code für Server/Client (denken Sie an util-Dateien ...). Sie können jedoch nicht alle ES6 schreiben oder in reinem Node.js importieren/exportieren.

Ich denke wirklich, dass das Transpilieren (nicht unbedingt das Bündeln) von Servercode ein Muss ist. Besonders bei mittleren/großen Projekten

Für das, was es wert ist, habe ich mich für die native Modulunterstützung in Node 10 über das Flag experimental-modules . Höchstwahrscheinlich ist dies nur eine Option für Leute, die neue Projekte starten oder auf die neueste Version von Node.

Ja, mir ist bewusst, dass es in zukünftigen Node-Versionen bahnbrechende Änderungen geben könnte, aber das gleiche gilt für Babel, es sei denn, Sie halten an den "alten" Einstellungen fest. Das Problem mit der Erweiterung trifft hier nicht wirklich zu, da wir nur unsere Serverdatei ändern müssen, die von Browsern nie verarbeitet wird.

So könnte ein Beispiel für package.json aussehen:

"name": "nextjs-node-ten",
"engineStrict": true,
"engines": {
  "node": ">=10.11.0"
},
"scripts": {
  "dev": "node --experimental-modules server.mjs",
  "build": "next build",
  "start": "NODE_ENV=production node --experimental-modules server.mjs"
}

Beachten Sie, dass Sie Ihre Erweiterung server.js in mjs ändern müssen. Daran führt kein Weg vorbei, aber Sie können dann reguläre .js Dateien daraus importieren.

Damit erhalten Sie eine native Node-Performance ohne Startzeiteinbußen, keine Transpilations- oder Speicherprobleme bei der Ausführung von babel-node in der Produktion, keinen Build-Schritt für Ihren Servercode usw.

Es gibt ein paar kleine Fallstricke, wie zum Beispiel, dass __dirname nicht definiert ist und so weiter. Ich konnte schnelle Lösungen finden und meinen Servercode in etwa 5 Minuten nativ in Node laufen lassen.

Hoffentlich werden wir innerhalb eines Jahres nicht mehr über die Modulunterstützung diskutieren...

Ich habe eine start.js Datei, die server.js :

require("@babel/register")({
  presets: ["@babel/preset-env"],
  ignore: ["node_modules", ".next"]
});

// Import the rest of our application.
module.exports = require("./server.js");

Stellen Sie sicher, dass Sie das Verzeichnis .next nicht transpilieren, sonst geraten Ihre Kompilierzeiten außer Kontrolle.

es gibt ein tolles Paket, esm , das man einfach installiert und dann die nodemon server.js mit ändert

nodemon -r esm server.js

das ist es!

Ich habe eine start.js Datei, die server.js :

require("@babel/register")({
  presets: ["@babel/preset-env"],
  ignore: ["node_modules", ".next"]
});

// Import the rest of our application.
module.exports = require("./server.js");

Stellen Sie sicher, dass Sie das Verzeichnis .next nicht transpilieren, sonst geraten Ihre Kompilierzeiten außer Kontrolle.

Bei mir hat es funktioniert.

es gibt ein tolles Paket, esm , das man einfach installiert und dann die nodemon server.js mit ändert

nodemon -r esm server.js

das ist es!

Irgendwelche Hinweise darauf, wie Sie esm für nextjs konfiguriert haben?

Ich habe eine start.js Datei, die server.js :

require("@babel/register")({
  presets: ["@babel/preset-env"],
  ignore: ["node_modules", ".next"]
});

// Import the rest of our application.
module.exports = require("./server.js");

Stellen Sie sicher, dass Sie das Verzeichnis .next nicht transpilieren, sonst geraten Ihre Kompilierzeiten außer Kontrolle.

Exzellent. Me ha servido mucho. Le agregué otro parámetro más

require('@babel/register')({
  cache: false,
  babelrc: false,
  presets: ['@babel/preset-env'],
  plugins: [['inline-dotenv'], ['console-source', { segments: 2 }]],
  ignore: ['node_modules', '.next']
})

// Import the rest of our application.
module.exports = require('./server.js')

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen

Verwandte Themen

DvirSh picture DvirSh  ·  3Kommentare

timneutkens picture timneutkens  ·  3Kommentare

irrigator picture irrigator  ·  3Kommentare

knipferrc picture knipferrc  ·  3Kommentare

jesselee34 picture jesselee34  ·  3Kommentare