mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-22 13:45:18 +00:00
feat(nitro, vite): use native module (#252)
Co-authored-by: Daniel Roe <daniel@roe.dev>
This commit is contained in:
parent
569d4f3cb3
commit
6318438415
5
.github/workflows/test-compat.yml
vendored
5
.github/workflows/test-compat.yml
vendored
@ -43,7 +43,4 @@ jobs:
|
|||||||
run: yarn build
|
run: yarn build
|
||||||
|
|
||||||
- name: Test
|
- name: Test
|
||||||
run: TEST_COMPAT=1 yarn jest --ci
|
run: TEST_COMPAT=1 yarn test:presets
|
||||||
|
|
||||||
# - name: Coverage
|
|
||||||
# uses: codecov/codecov-action@v1
|
|
||||||
|
5
.github/workflows/test.yml
vendored
5
.github/workflows/test.yml
vendored
@ -44,10 +44,7 @@ jobs:
|
|||||||
run: yarn build
|
run: yarn build
|
||||||
|
|
||||||
- name: Test
|
- name: Test
|
||||||
run: yarn jest --ci
|
run: yarn test:presets
|
||||||
|
|
||||||
# - name: Coverage
|
|
||||||
# uses: codecov/codecov-action@v1
|
|
||||||
|
|
||||||
- name: Release Edge
|
- name: Release Edge
|
||||||
if: github.event_name == 'push'
|
if: github.event_name == 'push'
|
||||||
|
@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
> How to deploy Nuxt to a Node.js host with Nuxt Nitro
|
> How to deploy Nuxt to a Node.js host with Nuxt Nitro
|
||||||
|
|
||||||
- Support for ultra-minimal SSR build
|
- Support for ultra-minimal SSR build
|
||||||
- Zero millisecond cold start
|
- Zero millisecond cold start
|
||||||
- More configuration required
|
- More configuration required
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
|
|
||||||
@ -28,7 +28,7 @@ This `.output` folder can be deployed to your Node.js host and the server can be
|
|||||||
To start the server in production mode, run:
|
To start the server in production mode, run:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
node .output/server
|
node .output/server/index.mjs
|
||||||
```
|
```
|
||||||
|
|
||||||
For example, using `pm2`:
|
For example, using `pm2`:
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
# Node.js server
|
# Node.js server
|
||||||
|
|
||||||
- Default preset if none is specified or auto-detected
|
- Default preset if none is specified or auto-detected
|
||||||
- Loads only the chunks required to render the request for optimal cold start timing
|
- Loads only the chunks required to render the request for optimal cold start timing
|
||||||
- Useful for debugging
|
- Useful for debugging
|
||||||
|
|
||||||
### Entrypoint
|
### Entrypoint
|
||||||
|
|
||||||
@ -11,7 +11,7 @@ With `{ preset: 'server' }` the result will be an entrypoint that launches a rea
|
|||||||
#### Example
|
#### Example
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
node .output/server
|
node .output/server/index.mjs
|
||||||
# > Load chunks/nitro/server (10.405923ms)
|
# > Load chunks/nitro/server (10.405923ms)
|
||||||
# > Cold Start (26.289817ms)
|
# > Cold Start (26.289817ms)
|
||||||
# Listening on http://localhost:3000
|
# Listening on http://localhost:3000
|
||||||
|
@ -7,6 +7,6 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "nu dev",
|
"dev": "nu dev",
|
||||||
"build": "nu build",
|
"build": "nu build",
|
||||||
"start": "node .output/server"
|
"start": "node .output/server/index.mjs"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,6 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "nu dev",
|
"dev": "nu dev",
|
||||||
"build": "nu build",
|
"build": "nu build",
|
||||||
"start": "node .output/server"
|
"start": "node .output/server/index.mjs"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,6 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "nu dev",
|
"dev": "nu dev",
|
||||||
"build": "nu build",
|
"build": "nu build",
|
||||||
"start": "node .output/server"
|
"start": "node .output/server/index.mjs"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,6 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "nu dev",
|
"dev": "nu dev",
|
||||||
"build": "nu build",
|
"build": "nu build",
|
||||||
"start": "node .output/server"
|
"start": "node .output/server/index.mjs"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,6 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "nu dev",
|
"dev": "nu dev",
|
||||||
"build": "nu build",
|
"build": "nu build",
|
||||||
"start": "node .output/server"
|
"start": "node .output/server/index.mjs"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,6 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "nu dev",
|
"dev": "nu dev",
|
||||||
"build": "nu build",
|
"build": "nu build",
|
||||||
"start": "node .output/server"
|
"start": "node .output/server/index.mjs"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,6 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "nu dev",
|
"dev": "nu dev",
|
||||||
"build": "nu build",
|
"build": "nu build",
|
||||||
"start": "node .output/server"
|
"start": "node .output/server/index.mjs"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,6 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "nu dev",
|
"dev": "nu dev",
|
||||||
"build": "nu build",
|
"build": "nu build",
|
||||||
"start": "node .output/server"
|
"start": "node .output/server/index.mjs"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
testEnvironment: 'node',
|
|
||||||
transform: {
|
|
||||||
'\\.[jt]sx?$': './scripts/jest-transform.mjs'
|
|
||||||
},
|
|
||||||
testPathIgnorePatterns: [
|
|
||||||
'.output/.*'
|
|
||||||
]
|
|
||||||
}
|
|
15
package.json
15
package.json
@ -16,8 +16,9 @@
|
|||||||
"example": "yarn workspace example-$0 dev",
|
"example": "yarn workspace example-$0 dev",
|
||||||
"example:build": "yarn workspace example-$0 build",
|
"example:build": "yarn workspace example-$0 build",
|
||||||
"lint": "eslint --ext .vue,.ts,.js .",
|
"lint": "eslint --ext .vue,.ts,.js .",
|
||||||
"test": "yarn lint && jest",
|
"test": "yarn lint && yarn test:presets",
|
||||||
"test:compat": "TEST_COMPAT=1 jest",
|
"test:presets": "mocha test/presets/*.mjs",
|
||||||
|
"test:compat": "TEST_COMPAT=1 yarn test:presets",
|
||||||
"version": "yarn && git add yarn.lock"
|
"version": "yarn && git add yarn.lock"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
@ -26,17 +27,23 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@nuxtjs/eslint-config": "^6.0.1",
|
"@nuxtjs/eslint-config": "^6.0.1",
|
||||||
"@nuxtjs/eslint-config-typescript": "^6.0.1",
|
"@nuxtjs/eslint-config-typescript": "^6.0.1",
|
||||||
"@types/jest": "^26.0.24",
|
"@types/chai": "^4.2.21",
|
||||||
|
"@types/jsdom": "^16",
|
||||||
|
"@types/mocha": "^8.2.3",
|
||||||
"@types/node": "^14.17.5",
|
"@types/node": "^14.17.5",
|
||||||
"@types/object-hash": "^2",
|
"@types/object-hash": "^2",
|
||||||
|
"chai": "^4.3.4",
|
||||||
"esbuild": "^0.12.15",
|
"esbuild": "^0.12.15",
|
||||||
"eslint": "^7.30.0",
|
"eslint": "^7.30.0",
|
||||||
"eslint-plugin-jsdoc": "^35.4.3",
|
"eslint-plugin-jsdoc": "^35.4.3",
|
||||||
|
"execa": "^5.1.1",
|
||||||
"globby": "^11.0.4",
|
"globby": "^11.0.4",
|
||||||
"jest": "^27.0.6",
|
|
||||||
"jiti": "^1.10.1",
|
"jiti": "^1.10.1",
|
||||||
|
"jsdom": "^16.6.0",
|
||||||
"lerna": "^4.0.0",
|
"lerna": "^4.0.0",
|
||||||
|
"mocha": "^9.0.2",
|
||||||
"object-hash": "^2.2.0",
|
"object-hash": "^2.2.0",
|
||||||
|
"ts-mocha": "^8.0.0",
|
||||||
"typescript": "^4.3.5",
|
"typescript": "^4.3.5",
|
||||||
"unbuild": "^0.3.2",
|
"unbuild": "^0.3.2",
|
||||||
"upath": "^2.0.1"
|
"upath": "^2.0.1"
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
import head from '#app/plugins/head'
|
|
||||||
import preload from '#app/plugins/preload.server'
|
import preload from '#app/plugins/preload.server'
|
||||||
|
|
||||||
<%= utils.importSources(app.plugins.map(p => p.src)) %>
|
<%= utils.importSources(app.plugins.map(p => p.src)) %>
|
||||||
|
|
||||||
const commonPlugins = [
|
const commonPlugins = [
|
||||||
head,
|
|
||||||
<%= app.plugins.filter(p => !p.mode || p.mode === 'all').map(p => utils.importName(p.src)).join(',\n ') %>
|
<%= app.plugins.filter(p => !p.mode || p.mode === 'all').map(p => utils.importName(p.src)).join(',\n ') %>
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -47,10 +47,10 @@ export async function build (nitroContext: NitroContext) {
|
|||||||
// Compile html template
|
// Compile html template
|
||||||
const htmlSrc = resolve(nitroContext._nuxt.buildDir, `views/${{ 2: 'app', 3: 'document' }[2]}.template.html`)
|
const htmlSrc = resolve(nitroContext._nuxt.buildDir, `views/${{ 2: 'app', 3: 'document' }[2]}.template.html`)
|
||||||
const htmlTemplate = { src: htmlSrc, contents: '', dst: '', compiled: '' }
|
const htmlTemplate = { src: htmlSrc, contents: '', dst: '', compiled: '' }
|
||||||
htmlTemplate.dst = htmlTemplate.src.replace(/.html$/, '.js').replace('app.', 'document.')
|
htmlTemplate.dst = htmlTemplate.src.replace(/.html$/, '.mjs').replace('app.', 'document.')
|
||||||
htmlTemplate.contents = await readFile(htmlTemplate.src, 'utf-8')
|
htmlTemplate.contents = await readFile(htmlTemplate.src, 'utf-8')
|
||||||
await nitroContext._internal.hooks.callHook('nitro:document', htmlTemplate)
|
await nitroContext._internal.hooks.callHook('nitro:document', htmlTemplate)
|
||||||
htmlTemplate.compiled = 'module.exports = ' + serializeTemplate(htmlTemplate.contents)
|
htmlTemplate.compiled = 'export default ' + serializeTemplate(htmlTemplate.contents)
|
||||||
await writeFile(htmlTemplate.dst, htmlTemplate.compiled)
|
await writeFile(htmlTemplate.dst, htmlTemplate.compiled)
|
||||||
|
|
||||||
nitroContext.rollupConfig = getRollupConfig(nitroContext)
|
nitroContext.rollupConfig = getRollupConfig(nitroContext)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import fetch from 'node-fetch'
|
import fetch from 'node-fetch'
|
||||||
import { resolve } from 'upath'
|
import { resolve } from 'upath'
|
||||||
import { resolveModule } from '@nuxt/kit'
|
import { readFile, writeFile } from 'fs-extra'
|
||||||
import { build, generate, prepare } from './build'
|
import { build, generate, prepare } from './build'
|
||||||
import { getNitroContext, NitroContext } from './context'
|
import { getNitroContext, NitroContext } from './context'
|
||||||
import { createDevServer } from './server/dev'
|
import { createDevServer } from './server/dev'
|
||||||
@ -55,16 +55,25 @@ export default function nuxt2CompatModule () {
|
|||||||
|
|
||||||
// Nitro client plugin
|
// Nitro client plugin
|
||||||
this.addPlugin({
|
this.addPlugin({
|
||||||
fileName: 'nitro.client.js',
|
fileName: 'nitro.client.mjs',
|
||||||
src: resolve(nitroContext._internal.runtimeDir, 'app/nitro.client.js')
|
src: resolve(nitroContext._internal.runtimeDir, 'app/nitro.client.mjs')
|
||||||
})
|
})
|
||||||
|
|
||||||
// Fix module resolution
|
// Fix module resolution
|
||||||
nuxt.hook('webpack:config', (configs) => {
|
nuxt.hook('webpack:config', (configs) => {
|
||||||
for (const config of configs) {
|
for (const config of configs) {
|
||||||
if (config.name === 'client') {
|
config.resolve.alias.ufo = 'ufo/dist/index.mjs'
|
||||||
config.resolve.alias.ufo = resolveModule('ufo/dist/index.mjs')
|
config.resolve.alias.ohmyfetch = 'ohmyfetch/dist/index.mjs'
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Generate mjs resources
|
||||||
|
nuxt.hook('build:compiled', async ({ name }) => {
|
||||||
|
if (name === 'server') {
|
||||||
|
await writeFile(resolve(nuxt.options.buildDir, 'dist/server/server.mjs'), 'export { default } from "./server.js"', 'utf8')
|
||||||
|
} else if (name === 'client') {
|
||||||
|
const manifest = await readFile(resolve(nuxt.options.buildDir, 'dist/server/client.manifest.json'), 'utf8')
|
||||||
|
await writeFile(resolve(nuxt.options.buildDir, 'dist/server/client.manifest.mjs'), 'export default ' + manifest, 'utf8')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ if ('serviceWorker' in navigator) {
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<link rel="prefetch" href="${routerBase}sw.js">
|
<link rel="prefetch" href="${routerBase}sw.js">
|
||||||
<link rel="prefetch" href="${routerBase}_server/index.js">
|
<link rel="prefetch" href="${routerBase}_server/index.mjs">
|
||||||
<script>
|
<script>
|
||||||
async function register () {
|
async function register () {
|
||||||
const registration = await navigator.serviceWorker.register('${routerBase}sw.js')
|
const registration = await navigator.serviceWorker.register('${routerBase}sw.js')
|
||||||
@ -65,7 +65,7 @@ if ('serviceWorker' in navigator) {
|
|||||||
tmpl.compiled = tmpl.compiled.replace('</body>', script + '</body>')
|
tmpl.compiled = tmpl.compiled.replace('</body>', script + '</body>')
|
||||||
},
|
},
|
||||||
async 'nitro:compiled' ({ output }: NitroContext) {
|
async 'nitro:compiled' ({ output }: NitroContext) {
|
||||||
await writeFile(resolve(output.publicDir, 'sw.js'), `self.importScripts('${input._nuxt.routerBase}_server/index.js');`)
|
await writeFile(resolve(output.publicDir, 'sw.js'), `self.importScripts('${input._nuxt.routerBase}_server/index.mjs');`)
|
||||||
|
|
||||||
// Temp fix
|
// Temp fix
|
||||||
await writeFile(resolve(output.publicDir, 'index.html'), html)
|
await writeFile(resolve(output.publicDir, 'index.html'), html)
|
||||||
|
@ -11,7 +11,7 @@ export const cloudflare: NitroPreset = extendPreset(worker, {
|
|||||||
],
|
],
|
||||||
hooks: {
|
hooks: {
|
||||||
async 'nitro:compiled' ({ output, _nuxt }: NitroContext) {
|
async 'nitro:compiled' ({ output, _nuxt }: NitroContext) {
|
||||||
await writeFile(resolve(output.dir, 'package.json'), JSON.stringify({ private: true, main: './server/index.js' }, null, 2))
|
await writeFile(resolve(output.dir, 'package.json'), JSON.stringify({ private: true, main: './server/index.mjs' }, null, 2))
|
||||||
await writeFile(resolve(output.dir, 'package-lock.json'), JSON.stringify({ lockfileVersion: 1 }, null, 2))
|
await writeFile(resolve(output.dir, 'package-lock.json'), JSON.stringify({ lockfileVersion: 1 }, null, 2))
|
||||||
let inDir = prettyPath(_nuxt.rootDir)
|
let inDir = prettyPath(_nuxt.rootDir)
|
||||||
if (inDir) {
|
if (inDir) {
|
||||||
|
@ -8,7 +8,7 @@ export const server: NitroPreset = extendPreset(node, {
|
|||||||
serveStatic: true,
|
serveStatic: true,
|
||||||
hooks: {
|
hooks: {
|
||||||
'nitro:compiled' ({ output }: NitroContext) {
|
'nitro:compiled' ({ output }: NitroContext) {
|
||||||
consola.success('Ready to run', hl('node ' + prettyPath(output.serverDir)))
|
consola.success('Ready to run', hl('node ' + prettyPath(output.serverDir) + '/index.mjs'))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -60,7 +60,7 @@ export const getRollupConfig = (nitroContext: NitroContext) => {
|
|||||||
delete env.alias['node-fetch'] // FIX ME
|
delete env.alias['node-fetch'] // FIX ME
|
||||||
|
|
||||||
if (nitroContext.sourceMap) {
|
if (nitroContext.sourceMap) {
|
||||||
env.polyfill.push('source-map-support/register')
|
env.polyfill.push('source-map-support/register.js')
|
||||||
}
|
}
|
||||||
|
|
||||||
const buildServerDir = join(nitroContext._nuxt.buildDir, 'dist/server')
|
const buildServerDir = join(nitroContext._nuxt.buildDir, 'dist/server')
|
||||||
@ -70,7 +70,7 @@ export const getRollupConfig = (nitroContext: NitroContext) => {
|
|||||||
input: resolvePath(nitroContext, nitroContext.entry),
|
input: resolvePath(nitroContext, nitroContext.entry),
|
||||||
output: {
|
output: {
|
||||||
dir: nitroContext.output.serverDir,
|
dir: nitroContext.output.serverDir,
|
||||||
entryFileNames: 'index.js',
|
entryFileNames: 'index.mjs',
|
||||||
chunkFileNames (chunkInfo) {
|
chunkFileNames (chunkInfo) {
|
||||||
let prefix = ''
|
let prefix = ''
|
||||||
const modules = Object.keys(chunkInfo.modules)
|
const modules = Object.keys(chunkInfo.modules)
|
||||||
@ -88,10 +88,10 @@ export const getRollupConfig = (nitroContext: NitroContext) => {
|
|||||||
} else if (lastModule.includes('assets')) {
|
} else if (lastModule.includes('assets')) {
|
||||||
prefix = 'assets'
|
prefix = 'assets'
|
||||||
}
|
}
|
||||||
return join('chunks', prefix, '[name].js')
|
return join('chunks', prefix, '[name].mjs')
|
||||||
},
|
},
|
||||||
inlineDynamicImports: nitroContext.inlineDynamicImports,
|
inlineDynamicImports: nitroContext.inlineDynamicImports,
|
||||||
format: 'cjs',
|
format: 'esm',
|
||||||
exports: 'auto',
|
exports: 'auto',
|
||||||
intro: '',
|
intro: '',
|
||||||
outro: '',
|
outro: '',
|
||||||
@ -232,6 +232,7 @@ export const getRollupConfig = (nitroContext: NitroContext) => {
|
|||||||
'~~',
|
'~~',
|
||||||
'@@/',
|
'@@/',
|
||||||
'virtual:',
|
'virtual:',
|
||||||
|
'ohmyfetch', // TODO: Webpack externals forces default import!
|
||||||
nitroContext._internal.runtimeDir,
|
nitroContext._internal.runtimeDir,
|
||||||
nitroContext._nuxt.srcDir,
|
nitroContext._nuxt.srcDir,
|
||||||
nitroContext._nuxt.rootDir,
|
nitroContext._nuxt.rootDir,
|
||||||
@ -241,7 +242,8 @@ export const getRollupConfig = (nitroContext: NitroContext) => {
|
|||||||
],
|
],
|
||||||
traceOptions: {
|
traceOptions: {
|
||||||
base: '/',
|
base: '/',
|
||||||
processCwd: nitroContext._nuxt.rootDir
|
processCwd: nitroContext._nuxt.rootDir,
|
||||||
|
exportsOnly: true
|
||||||
}
|
}
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
@ -252,7 +254,13 @@ export const getRollupConfig = (nitroContext: NitroContext) => {
|
|||||||
preferBuiltins: true,
|
preferBuiltins: true,
|
||||||
rootDir: nitroContext._nuxt.rootDir,
|
rootDir: nitroContext._nuxt.rootDir,
|
||||||
moduleDirectories,
|
moduleDirectories,
|
||||||
mainFields: ['main'] // Force resolve CJS (@vue/runtime-core ssrUtils)
|
// 'module' is intentionally not supported because of externals
|
||||||
|
mainFields: ['main'],
|
||||||
|
exportConditions: [
|
||||||
|
'default',
|
||||||
|
'module',
|
||||||
|
'import'
|
||||||
|
]
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// Automatically mock unresolved externals
|
// Automatically mock unresolved externals
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import consola from 'consola'
|
import consola from 'consola'
|
||||||
import { normalize } from 'upath'
|
|
||||||
|
|
||||||
const internalRegex = /^\.|\?|\.[mc]?js$|.ts$|.json$/
|
const internalRegex = /^\.|\?|\.[mc]?js$|.ts$|.json$/
|
||||||
|
|
||||||
@ -10,7 +9,7 @@ export function autoMock () {
|
|||||||
if (src && !internalRegex.test(src)) {
|
if (src && !internalRegex.test(src)) {
|
||||||
consola.warn('Auto mock external ', src)
|
consola.warn('Auto mock external ', src)
|
||||||
return {
|
return {
|
||||||
id: normalize(require.resolve('unenv/runtime/mock/proxy'))
|
id: 'unenv/runtime/mock/proxy'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
|
@ -103,16 +103,20 @@ export default function dynamicRequire(id) {
|
|||||||
|
|
||||||
function TMPL_LAZY ({ chunks }: TemplateContext) {
|
function TMPL_LAZY ({ chunks }: TemplateContext) {
|
||||||
return `
|
return `
|
||||||
function dynamicWebpackModule(id, getChunk) {
|
function dynamicWebpackModule(id, getChunk, ids) {
|
||||||
return function (module, exports, require) {
|
return function (module, exports, require) {
|
||||||
const r = getChunk()
|
const r = getChunk()
|
||||||
if (r instanceof Promise) {
|
if (typeof r.then === 'function') {
|
||||||
module.exports = r.then(r => {
|
module.exports = r.then(r => {
|
||||||
const realModule = { exports: {}, require };
|
const realModule = { exports: {}, require };
|
||||||
r.modules[id](realModule, realModule.exports, realModule.require);
|
r.modules[id](realModule, realModule.exports, realModule.require);
|
||||||
|
for (const _id of ids) {
|
||||||
|
if (_id === id) continue;
|
||||||
|
r.modules[_id](realModule, realModule.exports, realModule.require);
|
||||||
|
}
|
||||||
return realModule.exports;
|
return realModule.exports;
|
||||||
});
|
});
|
||||||
} else {
|
} else if (r && typeof r.modules[id] === 'function') {
|
||||||
r.modules[id](module, exports, require);
|
r.modules[id](module, exports, require);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -121,7 +125,7 @@ function dynamicWebpackModule(id, getChunk) {
|
|||||||
function webpackChunk (meta, getChunk) {
|
function webpackChunk (meta, getChunk) {
|
||||||
const chunk = { ...meta, modules: {} };
|
const chunk = { ...meta, modules: {} };
|
||||||
for (const id of meta.moduleIds) {
|
for (const id of meta.moduleIds) {
|
||||||
chunk.modules[id] = dynamicWebpackModule(id, getChunk);
|
chunk.modules[id] = dynamicWebpackModule(id, getChunk, meta.moduleIds);
|
||||||
};
|
};
|
||||||
return chunk;
|
return chunk;
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { resolve, dirname, normalize } from 'upath'
|
import { resolve, dirname } from 'upath'
|
||||||
import { copyFile, mkdirp } from 'fs-extra'
|
import { copyFile, mkdirp } from 'fs-extra'
|
||||||
import { nodeFileTrace, NodeFileTraceOptions } from '@vercel/nft'
|
import { nodeFileTrace, NodeFileTraceOptions } from '@vercel/nft'
|
||||||
import type { Plugin } from 'rollup'
|
import type { Plugin } from 'rollup'
|
||||||
@ -13,11 +13,11 @@ export interface NodeExternalsOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function externals (opts: NodeExternalsOptions): Plugin {
|
export function externals (opts: NodeExternalsOptions): Plugin {
|
||||||
const resolvedExternals = new Set<string>()
|
const trackedExternals = new Set<string>()
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: 'node-externals',
|
name: 'node-externals',
|
||||||
resolveId (id) {
|
async resolveId (id, importer, options) {
|
||||||
// Internals
|
// Internals
|
||||||
if (!id || id.startsWith('\x00') || id.includes('?') || id.startsWith('#')) {
|
if (!id || id.startsWith('\x00') || id.includes('?') || id.startsWith('#')) {
|
||||||
return null
|
return null
|
||||||
@ -44,11 +44,10 @@ export function externals (opts: NodeExternalsOptions): Plugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to resolve for nft
|
// Track externals
|
||||||
if (opts.trace !== false) {
|
if (opts.trace !== false) {
|
||||||
let _resolvedId = _id
|
const resolved = await this.resolve(id, importer, { ...options, skipSelf: true }).then(r => r.id)
|
||||||
try { _resolvedId = normalize(require.resolve(_resolvedId, { paths: opts.moduleDirectories })) } catch (_err) { }
|
trackedExternals.add(resolved)
|
||||||
resolvedExternals.add(_resolvedId)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -58,14 +57,25 @@ export function externals (opts: NodeExternalsOptions): Plugin {
|
|||||||
},
|
},
|
||||||
async buildEnd () {
|
async buildEnd () {
|
||||||
if (opts.trace !== false) {
|
if (opts.trace !== false) {
|
||||||
const tracedFiles = await nodeFileTrace(Array.from(resolvedExternals), opts.traceOptions)
|
const tracedFiles = await nodeFileTrace(Array.from(trackedExternals), opts.traceOptions)
|
||||||
.then(r => r.fileList.map(f => resolve(opts.traceOptions.base, f)))
|
.then(r => r.fileList.map(f => resolve(opts.traceOptions.base, f)))
|
||||||
|
.then(r => r.filter(file => file.includes('node_modules')))
|
||||||
|
|
||||||
|
// // Find all unique package names
|
||||||
|
const pkgs = new Set<string>()
|
||||||
|
for (const file of tracedFiles) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
const [_, baseDir, pkgName, _importPath] = /(.+\/node_modules\/)([^/]+)\/(.*)/.exec(file)
|
||||||
|
pkgs.add(resolve(baseDir, pkgName, 'package.json'))
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const pkg of pkgs) {
|
||||||
|
if (!tracedFiles.includes(pkg)) {
|
||||||
|
tracedFiles.push(pkg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
await Promise.all(tracedFiles.map(async (file) => {
|
await Promise.all(tracedFiles.map(async (file) => {
|
||||||
if (!file.includes('node_modules')) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// TODO: Minify package.json
|
|
||||||
const src = resolve(opts.traceOptions.base, file)
|
const src = resolve(opts.traceOptions.base, file)
|
||||||
const dst = resolve(opts.outDir, 'node_modules', file.split('node_modules/').pop())
|
const dst = resolve(opts.outDir, 'node_modules', file.split('node_modules/').pop())
|
||||||
await mkdirp(dirname(dst))
|
await mkdirp(dirname(dst))
|
||||||
|
@ -32,8 +32,12 @@ export function staticAssets (context: NitroContext) {
|
|||||||
'#static': `
|
'#static': `
|
||||||
import { promises } from 'fs'
|
import { promises } from 'fs'
|
||||||
import { resolve } from 'path'
|
import { resolve } from 'path'
|
||||||
|
import { dirname } from 'path'
|
||||||
|
import { fileURLToPath } from 'url'
|
||||||
import assets from '#static-assets'
|
import assets from '#static-assets'
|
||||||
|
|
||||||
|
const mainDir = dirname(fileURLToPath(globalThis.entryURL))
|
||||||
|
|
||||||
export function readAsset (id) {
|
export function readAsset (id) {
|
||||||
return promises.readFile(resolve(mainDir, getAsset(id).path))
|
return promises.readFile(resolve(mainDir, getAsset(id).path))
|
||||||
}
|
}
|
||||||
@ -50,7 +54,7 @@ export function dirnames (): Plugin {
|
|||||||
name: 'dirnames',
|
name: 'dirnames',
|
||||||
renderChunk (code, chunk) {
|
renderChunk (code, chunk) {
|
||||||
return {
|
return {
|
||||||
code: code + (chunk.isEntry ? 'globalThis.mainDir="undefined"!=typeof __dirname?__dirname:require.main.filename;' : ''),
|
code: code + (chunk.isEntry ? 'globalThis.entryURL = import.meta.url' : ''),
|
||||||
map: null
|
map: null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ import { createRenderer } from 'vue-bundle-renderer'
|
|||||||
import devalue from '@nuxt/devalue'
|
import devalue from '@nuxt/devalue'
|
||||||
import { runtimeConfig } from './config'
|
import { runtimeConfig } from './config'
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import htmlTemplate from '#build/views/document.template.js'
|
import htmlTemplate from '#build/views/document.template.mjs'
|
||||||
|
|
||||||
function _interopDefault (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e }
|
function _interopDefault (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e }
|
||||||
|
|
||||||
@ -17,9 +17,9 @@ async function loadRenderer () {
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const { renderToString } = await import('#nitro-renderer')
|
const { renderToString } = await import('#nitro-renderer')
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const createApp = await import('#build/dist/server/server')
|
const createApp = await import('#build/dist/server/server.mjs')
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const clientManifest = await import('#build/dist/server/client.manifest.json')
|
const clientManifest = await import('#build/dist/server/client.manifest.mjs')
|
||||||
_renderer = createRenderer(_interopDefault(createApp), {
|
_renderer = createRenderer(_interopDefault(createApp), {
|
||||||
clientManifest: _interopDefault(clientManifest),
|
clientManifest: _interopDefault(clientManifest),
|
||||||
renderToString
|
renderToString
|
||||||
|
@ -14,7 +14,7 @@ import type { NitroContext } from '../context'
|
|||||||
|
|
||||||
export function createDevServer (nitroContext: NitroContext) {
|
export function createDevServer (nitroContext: NitroContext) {
|
||||||
// Worker
|
// Worker
|
||||||
const workerEntry = resolve(nitroContext.output.dir, nitroContext.output.serverDir, 'index.js')
|
const workerEntry = resolve(nitroContext.output.dir, nitroContext.output.serverDir, 'index.mjs')
|
||||||
let pendingWorker: Worker | null
|
let pendingWorker: Worker | null
|
||||||
let activeWorker: Worker
|
let activeWorker: Worker
|
||||||
let workerAddress: string | null
|
let workerAddress: string | null
|
||||||
|
@ -36,7 +36,7 @@ function filesToMiddleware (files: string[], baseDir: string, basePath: string,
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function scanMiddleware (serverDir: string, onChange?: (results: ServerMiddleware[], event: 'add' | 'addDir' | 'change' | 'unlink' | 'unlinkDir', file: string) => void): Promise<ServerMiddleware[]> {
|
export function scanMiddleware (serverDir: string, onChange?: (results: ServerMiddleware[], event: 'add' | 'addDir' | 'change' | 'unlink' | 'unlinkDir', file: string) => void): Promise<ServerMiddleware[]> {
|
||||||
const pattern = '**/*.{js,ts}'
|
const pattern = '**/*.{ts,mjs,js,cjs}'
|
||||||
const globalDir = resolve(serverDir, 'middleware')
|
const globalDir = resolve(serverDir, 'middleware')
|
||||||
const apiDir = resolve(serverDir, 'api')
|
const apiDir = resolve(serverDir, 'api')
|
||||||
|
|
||||||
@ -78,7 +78,7 @@ export function resolveMiddleware (nuxt: Nuxt) {
|
|||||||
middleware.push({
|
middleware.push({
|
||||||
...m,
|
...m,
|
||||||
handle: tryResolvePath(handle, {
|
handle: tryResolvePath(handle, {
|
||||||
extensions: ['.ts', '.js'],
|
extensions: ['.ts', '.mjs', '.js', '.cjs'],
|
||||||
alias: nuxt.options.alias,
|
alias: nuxt.options.alias,
|
||||||
base: nuxt.options.srcDir
|
base: nuxt.options.srcDir
|
||||||
}),
|
}),
|
||||||
|
@ -32,6 +32,11 @@ export async function buildServer (ctx: ViteBuildContext) {
|
|||||||
ssr: true,
|
ssr: true,
|
||||||
rollupOptions: {
|
rollupOptions: {
|
||||||
input: resolve(ctx.nuxt.options.buildDir, 'entry.mjs'),
|
input: resolve(ctx.nuxt.options.buildDir, 'entry.mjs'),
|
||||||
|
output: {
|
||||||
|
entryFileNames: 'server.mjs',
|
||||||
|
preferConst: true,
|
||||||
|
format: 'module'
|
||||||
|
},
|
||||||
onwarn (warning, rollupWarn) {
|
onwarn (warning, rollupWarn) {
|
||||||
if (!['UNUSED_EXTERNAL_IMPORT'].includes(warning.code)) {
|
if (!['UNUSED_EXTERNAL_IMPORT'].includes(warning.code)) {
|
||||||
rollupWarn(warning)
|
rollupWarn(warning)
|
||||||
@ -51,8 +56,8 @@ export async function buildServer (ctx: ViteBuildContext) {
|
|||||||
const serverDist = resolve(ctx.nuxt.options.buildDir, 'dist/server')
|
const serverDist = resolve(ctx.nuxt.options.buildDir, 'dist/server')
|
||||||
await mkdirp(serverDist)
|
await mkdirp(serverDist)
|
||||||
|
|
||||||
await writeFile(resolve(serverDist, 'server.js'), 'module.exports = require("./entry")', 'utf8')
|
|
||||||
await writeFile(resolve(serverDist, 'client.manifest.json'), 'false', 'utf8')
|
await writeFile(resolve(serverDist, 'client.manifest.json'), 'false', 'utf8')
|
||||||
|
await writeFile(resolve(serverDist, 'client.manifest.mjs'), 'export default false', 'utf8')
|
||||||
|
|
||||||
const onBuild = () => ctx.nuxt.callHook('build:resources', wpfs)
|
const onBuild = () => ctx.nuxt.callHook('build:resources', wpfs)
|
||||||
|
|
||||||
|
@ -113,6 +113,10 @@ export default class VueSSRClientPlugin {
|
|||||||
|
|
||||||
await mkdirp(dirname(this.options.filename))
|
await mkdirp(dirname(this.options.filename))
|
||||||
await writeFile(this.options.filename, src)
|
await writeFile(this.options.filename, src)
|
||||||
|
|
||||||
|
const mjsSrc = 'export default ' + src
|
||||||
|
await writeFile(this.options.filename.replace('.json', '.mjs'), mjsSrc)
|
||||||
|
|
||||||
// assets[this.options.filename] = {
|
// assets[this.options.filename] = {
|
||||||
// source: () => src,
|
// source: () => src,
|
||||||
// size: () => src.length
|
// size: () => src.length
|
||||||
|
@ -74,6 +74,21 @@ export default class VueSSRServerPlugin {
|
|||||||
size: () => src.length
|
size: () => src.length
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const mjsSrc = 'export default ' + src
|
||||||
|
assets[this.options.filename.replace('.json', '.mjs')] = {
|
||||||
|
source: () => mjsSrc,
|
||||||
|
map: () => null,
|
||||||
|
size: () => mjsSrc.length
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Workaround for webpack
|
||||||
|
const serverJS = 'export { default } from "./server.js"'
|
||||||
|
assets['server.mjs'] = {
|
||||||
|
source: () => serverJS,
|
||||||
|
map: () => null,
|
||||||
|
size: () => serverJS.length
|
||||||
|
}
|
||||||
|
|
||||||
cb()
|
cb()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -7,6 +7,6 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "nu dev",
|
"dev": "nu dev",
|
||||||
"build": "nu build",
|
"build": "nu build",
|
||||||
"start": "node .output/server"
|
"start": "node .output/server/index.mjs"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,33 +0,0 @@
|
|||||||
import { transformSync } from 'esbuild'
|
|
||||||
|
|
||||||
// https://jestjs.io/docs/next/code-transformation
|
|
||||||
export default {
|
|
||||||
process (src, path, _opts) {
|
|
||||||
const r = transformSync(src, {
|
|
||||||
target: 'node14',
|
|
||||||
format: 'cjs',
|
|
||||||
sourcefile: path,
|
|
||||||
loader: path.endsWith('.ts') ? 'ts' : 'default'
|
|
||||||
})
|
|
||||||
r.code = r.code.replace(/import(\(.*\))/g, (_, id) => {
|
|
||||||
let openBrackets = 0
|
|
||||||
|
|
||||||
for (let pos = 0; pos < id.length; pos++) {
|
|
||||||
const char = id[pos]
|
|
||||||
switch (char) {
|
|
||||||
case '(':
|
|
||||||
openBrackets++
|
|
||||||
break
|
|
||||||
case ')':
|
|
||||||
openBrackets--
|
|
||||||
if (!openBrackets) {
|
|
||||||
return 'Promise.resolve(require' + id.slice(0, pos) + ')' + id.slice(pos)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 'Promise.resolve(require' + id + ')'
|
|
||||||
})
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
}
|
|
7
test/fixtures/basic/nuxt.config.ts
vendored
7
test/fixtures/basic/nuxt.config.ts
vendored
@ -1,3 +1,8 @@
|
|||||||
import { defineNuxtConfig } from '@nuxt/kit'
|
import { defineNuxtConfig } from '@nuxt/kit'
|
||||||
|
|
||||||
export default defineNuxtConfig({})
|
export default defineNuxtConfig({
|
||||||
|
buildDir: process.env.NITRO_BUILD_DIR,
|
||||||
|
nitro: {
|
||||||
|
output: { dir: process.env.NITRO_OUTPUT_DIR }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
6
test/fixtures/compat/nuxt.config.ts
vendored
6
test/fixtures/compat/nuxt.config.ts
vendored
@ -6,5 +6,9 @@ global.__NUXT_PREPATHS__ = (global.__NUXT_PREPATHS__ || []).concat(__dirname)
|
|||||||
export default defineNuxtConfig({
|
export default defineNuxtConfig({
|
||||||
buildModules: [
|
buildModules: [
|
||||||
'@nuxt/nitro/compat'
|
'@nuxt/nitro/compat'
|
||||||
]
|
],
|
||||||
|
buildDir: process.env.NITRO_BUILD_DIR,
|
||||||
|
nitro: {
|
||||||
|
output: { dir: process.env.NITRO_OUTPUT_DIR }
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
70
test/presets/_tests.mjs
Normal file
70
test/presets/_tests.mjs
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
import { resolve } from 'path'
|
||||||
|
import destr from 'destr'
|
||||||
|
import { listen } from 'listhen'
|
||||||
|
import { $fetch } from 'ohmyfetch/node'
|
||||||
|
import execa from 'execa'
|
||||||
|
import { expect } from 'chai'
|
||||||
|
import { fixtureDir, resolveWorkspace } from '../utils.mjs'
|
||||||
|
|
||||||
|
const isCompat = Boolean(process.env.TEST_COMPAT)
|
||||||
|
|
||||||
|
export function importModule (path) {
|
||||||
|
return import(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setupTest (preset) {
|
||||||
|
const fixture = isCompat ? 'compat' : 'basic'
|
||||||
|
const rootDir = fixtureDir(fixture)
|
||||||
|
const buildDir = resolve(rootDir, '.nuxt-' + preset)
|
||||||
|
|
||||||
|
const ctx = {
|
||||||
|
rootDir,
|
||||||
|
outDir: resolve(buildDir, 'output'),
|
||||||
|
fetch: url => $fetch(url, { baseURL: ctx.server.url })
|
||||||
|
}
|
||||||
|
|
||||||
|
it('nitro build', async () => {
|
||||||
|
const nuxtCLI = isCompat
|
||||||
|
? resolve(ctx.rootDir, 'node_modules/nuxt/bin/nuxt.js')
|
||||||
|
: resolveWorkspace('packages/cli/bin/nuxt.js')
|
||||||
|
|
||||||
|
await execa('node', [nuxtCLI, 'build', ctx.rootDir], {
|
||||||
|
env: {
|
||||||
|
NITRO_PRESET: preset,
|
||||||
|
NITRO_BUILD_DIR: buildDir,
|
||||||
|
NITRO_OUTPUT_DIR: ctx.outDir,
|
||||||
|
NODE_ENV: 'production'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}).timeout(60000)
|
||||||
|
|
||||||
|
after('Cleanup', async () => {
|
||||||
|
if (ctx.server) {
|
||||||
|
await ctx.server.close()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function startServer (ctx, handle) {
|
||||||
|
ctx.server = await listen(handle)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function testNitroBehavior (ctx, getHandler) {
|
||||||
|
let handler
|
||||||
|
|
||||||
|
it('setup handler', async () => {
|
||||||
|
handler = await getHandler()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('SSR Works', async () => {
|
||||||
|
const { data } = await handler({ url: '/' })
|
||||||
|
expect(data).to.have.string('Hello Vue')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('API Works', async () => {
|
||||||
|
const { data } = await handler({ url: '/api/hello' })
|
||||||
|
expect(destr(data)).to.have.string('Hello API')
|
||||||
|
})
|
||||||
|
}
|
@ -1,103 +0,0 @@
|
|||||||
import { RequestListener } from 'http'
|
|
||||||
import { resolve } from 'upath'
|
|
||||||
import destr from 'destr'
|
|
||||||
import consola from 'consola'
|
|
||||||
import { Listener, listen } from 'listhen'
|
|
||||||
import { $fetch } from 'ohmyfetch/node'
|
|
||||||
import createRequire from 'create-require'
|
|
||||||
import type { LoadNuxtOptions } from '@nuxt/kit'
|
|
||||||
import { fixtureDir, buildFixture, loadFixture } from '../utils'
|
|
||||||
|
|
||||||
const isCompat = Boolean(process.env.TEST_COMPAT)
|
|
||||||
|
|
||||||
export interface TestContext {
|
|
||||||
rootDir: string
|
|
||||||
outDir: string
|
|
||||||
nuxt?: any
|
|
||||||
fetch: (url: string) => Promise<any>
|
|
||||||
server?: Listener
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AbstractRequest {
|
|
||||||
url: string
|
|
||||||
headers?: any
|
|
||||||
method?: string
|
|
||||||
body?: any
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AbstractResponse {
|
|
||||||
data: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export type AbstractHandler = (req: AbstractRequest) => Promise<AbstractResponse>
|
|
||||||
|
|
||||||
export function setupTest (): TestContext {
|
|
||||||
const fixture = isCompat ? 'compat' : 'basic'
|
|
||||||
const rootDir = fixtureDir(fixture)
|
|
||||||
const outDir = resolve(__dirname, '.output', fixture)
|
|
||||||
|
|
||||||
const ctx: TestContext = {
|
|
||||||
rootDir,
|
|
||||||
outDir,
|
|
||||||
fetch: url => $fetch<any>(url, { baseURL: ctx.server.url })
|
|
||||||
}
|
|
||||||
|
|
||||||
beforeAll(() => {
|
|
||||||
jest.mock('jiti', () => createRequire)
|
|
||||||
consola.wrapAll()
|
|
||||||
consola.mock(() => jest.fn())
|
|
||||||
})
|
|
||||||
|
|
||||||
afterAll(async () => {
|
|
||||||
if (ctx.nuxt) {
|
|
||||||
await ctx.nuxt.close()
|
|
||||||
}
|
|
||||||
if (ctx.server) {
|
|
||||||
await ctx.server.close()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return ctx
|
|
||||||
}
|
|
||||||
|
|
||||||
export function testNitroBuild (ctx: TestContext, preset: string) {
|
|
||||||
test('nitro build', async () => {
|
|
||||||
ctx.outDir = resolve(ctx.outDir, preset)
|
|
||||||
|
|
||||||
const loadOpts: LoadNuxtOptions = { rootDir: ctx.rootDir, dev: false, version: isCompat ? 2 : 3 }
|
|
||||||
await buildFixture(loadOpts)
|
|
||||||
const nuxt = await loadFixture(loadOpts, {
|
|
||||||
nitro: {
|
|
||||||
preset,
|
|
||||||
minify: false,
|
|
||||||
serveStatic: false,
|
|
||||||
externals: preset === 'cloudflare' ? false : { trace: false },
|
|
||||||
output: { dir: ctx.outDir }
|
|
||||||
}
|
|
||||||
})
|
|
||||||
await nuxt.callHook('build:done', {})
|
|
||||||
ctx.nuxt = nuxt
|
|
||||||
}, 60000)
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function startServer (ctx: TestContext, handle: RequestListener) {
|
|
||||||
ctx.server = await listen(handle)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function testNitroBehavior (_ctx: TestContext, getHandler: () => Promise<AbstractHandler>) {
|
|
||||||
let handler
|
|
||||||
|
|
||||||
test('setup handler', async () => {
|
|
||||||
handler = await getHandler()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('SSR Works', async () => {
|
|
||||||
const { data } = await handler({ url: '/' })
|
|
||||||
expect(data).toMatch('Hello Vue')
|
|
||||||
}, 10000)
|
|
||||||
|
|
||||||
test('API Works', async () => {
|
|
||||||
const { data } = await handler({ url: '/api/hello' })
|
|
||||||
expect(destr(data)).toEqual('Hello API')
|
|
||||||
})
|
|
||||||
}
|
|
@ -1,15 +1,14 @@
|
|||||||
import { resolve } from 'upath'
|
import { resolve } from 'path'
|
||||||
import { readFile } from 'fs-extra'
|
import { promises as fsp } from 'fs'
|
||||||
import { JSDOM } from 'jsdom'
|
import { JSDOM } from 'jsdom'
|
||||||
|
|
||||||
import { setupTest, testNitroBuild, testNitroBehavior } from './_utils'
|
import { setupTest, testNitroBehavior } from './_tests.mjs'
|
||||||
|
|
||||||
// TODO: fix SyntaxError: Unexpected end of input on script executation
|
// TODO: fix SyntaxError: Unexpected end of input on script executation
|
||||||
describe.skip('nitro:preset:cloudflare', () => {
|
describe('nitro:preset:cloudflare', () => {
|
||||||
const ctx = setupTest()
|
const ctx = setupTest('cloudflare')
|
||||||
testNitroBuild(ctx, 'cloudflare')
|
|
||||||
testNitroBehavior(ctx, async () => {
|
testNitroBehavior(ctx, async () => {
|
||||||
const script = await readFile(resolve(ctx.outDir, 'server/index.js'), 'utf-8')
|
const script = await fsp.readFile(resolve(ctx.outDir, 'server/index.mjs'), 'utf-8')
|
||||||
const dom = new JSDOM(
|
const dom = new JSDOM(
|
||||||
`<!DOCTYPE html>
|
`<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
@ -1,11 +1,10 @@
|
|||||||
import { resolve } from 'upath'
|
import { resolve } from 'path'
|
||||||
import { testNitroBuild, setupTest, testNitroBehavior } from './_utils'
|
import { setupTest, testNitroBehavior, importModule } from './_tests.mjs'
|
||||||
|
|
||||||
describe('nitro:preset:lambda', () => {
|
describe('nitro:preset:lambda', () => {
|
||||||
const ctx = setupTest()
|
const ctx = setupTest('lambda')
|
||||||
testNitroBuild(ctx, 'lambda')
|
|
||||||
testNitroBehavior(ctx, async () => {
|
testNitroBehavior(ctx, async () => {
|
||||||
const { handler } = await import(resolve(ctx.outDir, 'server/index.js'))
|
const { handler } = await importModule(resolve(ctx.outDir, 'server/index.mjs'))
|
||||||
return async ({ url: rawRelativeUrl, headers, method, body }) => {
|
return async ({ url: rawRelativeUrl, headers, method, body }) => {
|
||||||
// creating new URL object to parse query easier
|
// creating new URL object to parse query easier
|
||||||
const url = new URL(`https://example.com${rawRelativeUrl}`)
|
const url = new URL(`https://example.com${rawRelativeUrl}`)
|
16
test/presets/node.test.mjs
Normal file
16
test/presets/node.test.mjs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { resolve } from 'path'
|
||||||
|
import { startServer, setupTest, testNitroBehavior, importModule } from './_tests.mjs'
|
||||||
|
|
||||||
|
describe('nitro:preset:node', () => {
|
||||||
|
const ctx = setupTest('node')
|
||||||
|
testNitroBehavior(ctx, async () => {
|
||||||
|
const { handle } = await importModule(resolve(ctx.outDir, 'server/index.mjs'))
|
||||||
|
await startServer(ctx, handle)
|
||||||
|
return async ({ url }) => {
|
||||||
|
const data = await ctx.fetch(url)
|
||||||
|
return {
|
||||||
|
data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
@ -1,17 +0,0 @@
|
|||||||
import { resolve } from 'upath'
|
|
||||||
import { testNitroBuild, startServer, setupTest, testNitroBehavior } from './_utils'
|
|
||||||
|
|
||||||
describe('nitro:preset:node', () => {
|
|
||||||
const ctx = setupTest()
|
|
||||||
testNitroBuild(ctx, 'node')
|
|
||||||
testNitroBehavior(ctx, async () => {
|
|
||||||
const { handle } = await import(resolve(ctx.outDir, 'server/index.js'))
|
|
||||||
await startServer(ctx, handle)
|
|
||||||
return async ({ url }) => {
|
|
||||||
const data = await ctx.fetch(url)
|
|
||||||
return {
|
|
||||||
data
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
@ -1,11 +1,10 @@
|
|||||||
import { resolve } from 'upath'
|
import { resolve } from 'path'
|
||||||
import { testNitroBuild, setupTest, startServer, testNitroBehavior } from './_utils'
|
import { setupTest, startServer, testNitroBehavior, importModule } from './_tests.mjs'
|
||||||
|
|
||||||
describe('nitro:preset:vercel', () => {
|
describe('nitro:preset:vercel', () => {
|
||||||
const ctx = setupTest()
|
const ctx = setupTest('vercel')
|
||||||
testNitroBuild(ctx, 'vercel')
|
|
||||||
testNitroBehavior(ctx, async () => {
|
testNitroBehavior(ctx, async () => {
|
||||||
const handle = await import(resolve(ctx.outDir, 'functions/node/server/index.js'))
|
const handle = await importModule(resolve(ctx.outDir, 'functions/node/server/index.mjs'))
|
||||||
.then(r => r.default || r)
|
.then(r => r.default || r)
|
||||||
await startServer(ctx, handle)
|
await startServer(ctx, handle)
|
||||||
return async ({ url }) => {
|
return async ({ url }) => {
|
@ -1,15 +1,27 @@
|
|||||||
import { existsSync, readFileSync, writeFileSync, rmSync, mkdirSync } from 'fs'
|
import { existsSync, readFileSync, writeFileSync, rmSync, mkdirSync } from 'fs'
|
||||||
import { execSync } from 'child_process'
|
import { execSync } from 'child_process'
|
||||||
import { resolve, dirname } from 'upath'
|
import { resolve, dirname } from 'path'
|
||||||
|
import { fileURLToPath } from 'url'
|
||||||
import defu from 'defu'
|
import defu from 'defu'
|
||||||
import hash from 'object-hash'
|
import hash from 'object-hash'
|
||||||
import type { LoadNuxtOptions, NuxtConfig } from '@nuxt/kit'
|
import execa from 'execa'
|
||||||
|
|
||||||
export function fixtureDir (name: string) {
|
const __dirname = dirname(fileURLToPath(import.meta.url))
|
||||||
|
|
||||||
|
export function resolveWorkspace (name) {
|
||||||
|
return resolve(__dirname, '../', name)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function fixtureDir (name) {
|
||||||
return resolve(__dirname, 'fixtures', name)
|
return resolve(__dirname, 'fixtures', name)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function loadFixture (opts: LoadNuxtOptions, unhashedConfig?: NuxtConfig) {
|
export async function execNuxtCLI (args, opts) {
|
||||||
|
const nuxtCLI = resolveWorkspace('packages/cli/bin/nuxt.js')
|
||||||
|
await execa('node', [nuxtCLI, ...args], opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function loadFixture (opts, unhashedConfig) {
|
||||||
const buildId = hash(opts)
|
const buildId = hash(opts)
|
||||||
const buildDir = resolve(opts.rootDir, '.nuxt', buildId)
|
const buildDir = resolve(opts.rootDir, '.nuxt', buildId)
|
||||||
const { loadNuxt } = await import('@nuxt/kit')
|
const { loadNuxt } = await import('@nuxt/kit')
|
||||||
@ -17,7 +29,7 @@ export async function loadFixture (opts: LoadNuxtOptions, unhashedConfig?: NuxtC
|
|||||||
return nuxt
|
return nuxt
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function buildFixture (opts: LoadNuxtOptions) {
|
export async function buildFixture (opts) {
|
||||||
const buildId = hash(opts)
|
const buildId = hash(opts)
|
||||||
const buildDir = resolve(opts.rootDir, '.nuxt', buildId)
|
const buildDir = resolve(opts.rootDir, '.nuxt', buildId)
|
||||||
|
|
||||||
@ -75,6 +87,6 @@ function waitWhile (check, interval = 100, timeout = 30000) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function gitHead (): string {
|
function gitHead () {
|
||||||
return execSync('git rev-parse HEAD').toString('utf8').trim()
|
return execSync('git rev-parse HEAD').toString('utf8').trim()
|
||||||
}
|
}
|
@ -11,7 +11,8 @@
|
|||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"types": [
|
"types": [
|
||||||
"node",
|
"node",
|
||||||
"jest",
|
"mocha",
|
||||||
|
"chai",
|
||||||
"@nuxt/app",
|
"@nuxt/app",
|
||||||
"@nuxt/nitro",
|
"@nuxt/nitro",
|
||||||
]
|
]
|
||||||
|
Loading…
Reference in New Issue
Block a user