Skip to content

Commit

Permalink
feat: use native esm for all packages (nuxt#539)
Browse files Browse the repository at this point in the history
  • Loading branch information
pi0 authored Oct 2, 2021
1 parent f21b7de commit 6e49637
Show file tree
Hide file tree
Showing 83 changed files with 472 additions and 366 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ jobs:
- name: Install dependencies
run: yarn --immutable

- name: Stub
run: yarn stub
- name: Build
run: yarn build

- name: Test (unit)
run: yarn test:unit
Expand Down Expand Up @@ -81,8 +81,8 @@ jobs:
- name: Install dependencies (bridge fixture)
run: yarn --immutable && cd test/fixtures/bridge && yarn --immutable

- name: Stub
run: yarn stub
- name: Build
run: yarn build

- name: Test (presets bridge)
run: yarn test:bridge
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"name": "nuxt-framework",
"license": "MIT",
"type": "module",
"workspaces": [
"packages/*",
"examples/*",
Expand Down
1 change: 1 addition & 0 deletions packages/bridge/build.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { defineBuildConfig } from 'unbuild'

export default defineBuildConfig({
emitCJS: false,
entries: [
'src/module',
{ input: 'src/runtime/', outDir: 'dist/runtime', format: 'esm' }
Expand Down
4 changes: 4 additions & 0 deletions packages/bridge/module.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// CommonJS proxy to bypass jiti transforms from nuxt 2
module.exports = function (...args) {
return import('./dist/module.mjs').then(m => m.default.call(this, ...args))
}
5 changes: 4 additions & 1 deletion packages/bridge/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
"version": "0.0.0",
"repository": "nuxt/framework",
"license": "MIT",
"main": "./dist/module.cjs",
"type": "module",
"exports": "./module.cjs",
"types": "./dist/index.d.ts",
"files": [
"module.cjs",
"dist"
],
"scripts": {
Expand All @@ -22,6 +24,7 @@
"estree-walker": "^2.0.2",
"fs-extra": "^10.0.0",
"magic-string": "^0.25.7",
"mlly": "^0.2.2",
"node-fetch": "^3.0.0",
"nuxi": "^0.10.0",
"nuxt-swc": "^0.1.0",
Expand Down
13 changes: 11 additions & 2 deletions packages/bridge/src/async-loading.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,24 @@
// Based on https://github.com/webpack/webpack/blob/v4.46.0/lib/node/NodeMainTemplatePlugin.js#L81-L191

import { createRequire } from 'module'
import type { Compiler } from 'webpack'

export class AsyncLoadingPlugin {
private opts: any
private Template: any
constructor (opts) {
this.opts = opts
const _require = createRequire(import.meta.url)
const TemplatePath = _require.resolve('webpack/lib/Template', { paths: [...this.opts.modulesDir] })
this.Template = _require(TemplatePath)
}

apply (compiler: Compiler) {
const Template = require('webpack/lib/Template')
compiler.hooks.compilation.tap('AsyncLoading', (compilation) => {
const mainTemplate = compilation.mainTemplate
mainTemplate.hooks.requireEnsure.tap(
'AsyncLoading',
(_source, chunk, hash) => {
const Template = this.Template
const chunkFilename = mainTemplate.outputOptions.chunkFilename
const chunkMaps = chunk.getChunkMaps()
const insertMoreModules = [
Expand Down
5 changes: 3 additions & 2 deletions packages/bridge/src/capi.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { createRequire } from 'module'
import { useNuxt, addPlugin, addPluginTemplate, addVitePlugin, addWebpackPlugin } from '@nuxt/kit'
import { resolve } from 'pathe'

import { distDir } from './dirs'
import { KeyPlugin } from './capi-legacy-key-plugin'

Expand All @@ -13,7 +13,8 @@ export function setupCAPIBridge (_options: any) {
}

// Add composition-api support
const vueCapiEntry = require.resolve('@vue/composition-api/dist/vue-composition-api.mjs')
const _require = createRequire(import.meta.url)
const vueCapiEntry = _require.resolve('@vue/composition-api/dist/vue-composition-api.mjs')
nuxt.options.alias['@vue/composition-api/dist/vue-composition-api.common.js'] = vueCapiEntry
nuxt.options.alias['@vue/composition-api/dist/vue-composition-api.common.prod.js'] = vueCapiEntry
nuxt.options.alias['@vue/composition-api/dist/vue-composition-api.esm.js'] = vueCapiEntry
Expand Down
5 changes: 3 additions & 2 deletions packages/bridge/src/dirs.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { resolve } from 'pathe'
import { fileURLToPath } from 'url'
import { dirname, resolve } from 'pathe'

export const distDir = __dirname
export const distDir = dirname(fileURLToPath(import.meta.url))
export const pkgDir = resolve(distDir, '..')
8 changes: 5 additions & 3 deletions packages/bridge/src/module.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { createRequire } from 'module'
import { defineNuxtModule, installModule } from '@nuxt/kit'
import { setupNitroBridge } from './nitro'
import { setupAppBridge } from './app'
Expand Down Expand Up @@ -30,14 +31,15 @@ export default defineNuxtModule({
}
await setupCAPIBridge(opts.capi)
}
const _require = createRequire(import.meta.url)
if (opts.vite) {
await installModule(nuxt, require.resolve('nuxt-vite'))
await installModule(nuxt, _require.resolve('nuxt-vite'))
}
if (opts.postcss8) {
await installModule(nuxt, require.resolve('@nuxt/postcss8'))
await installModule(nuxt, _require.resolve('@nuxt/postcss8'))
}
if (opts.swc) {
await installModule(nuxt, require.resolve('nuxt-swc'))
await installModule(nuxt, _require.resolve('nuxt-swc'))
}
if (opts.resolve) {
setupBetterResolve()
Expand Down
14 changes: 8 additions & 6 deletions packages/bridge/src/nitro.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { promises as fsp } from 'fs'
import fetch from 'node-fetch'
import { addPluginTemplate, useNuxt } from '@nuxt/kit'
import { stringifyQuery } from 'ufo'
import { resolve } from 'pathe'
import { readFile, writeFile } from 'fs-extra'
import { build, generate, prepare, getNitroContext, NitroContext, createDevServer, wpfs, resolveMiddleware } from '@nuxt/nitro'
import { AsyncLoadingPlugin } from './async-loading'
import { distDir } from './dirs'
Expand Down Expand Up @@ -68,7 +68,9 @@ export function setupNitroBridge () {
const serverConfig = webpackConfigs.find(config => config.name === 'server')
if (serverConfig) {
serverConfig.plugins = serverConfig.plugins || []
serverConfig.plugins.push(new AsyncLoadingPlugin())
serverConfig.plugins.push(new AsyncLoadingPlugin({
modulesDir: nuxt.options.modulesDir
}))
}
})

Expand Down Expand Up @@ -98,11 +100,11 @@ export function setupNitroBridge () {
nuxt.hook('build:compiled', async ({ name }) => {
if (name === 'server') {
const jsServerEntry = resolve(nuxt.options.buildDir, 'dist/server/server.js')
await writeFile(jsServerEntry.replace(/.js$/, '.cjs'), 'module.exports = require("./server.js")', 'utf8')
await writeFile(jsServerEntry.replace(/.js$/, '.mjs'), 'export { default } from "./server.cjs"', 'utf8')
await fsp.writeFile(jsServerEntry.replace(/.js$/, '.cjs'), 'module.exports = require("./server.js")', 'utf8')
await fsp.writeFile(jsServerEntry.replace(/.js$/, '.mjs'), 'export { default } from "./server.cjs"', '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')
const manifest = await fsp.readFile(resolve(nuxt.options.buildDir, 'dist/server/client.manifest.json'), 'utf8')
await fsp.writeFile(resolve(nuxt.options.buildDir, 'dist/server/client.manifest.mjs'), 'export default ' + manifest, 'utf8')
}
})

Expand Down
13 changes: 6 additions & 7 deletions packages/bridge/src/resolve.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,27 @@
import fs from 'fs'
import { promisify } from 'util'
import defu from 'defu'

import { CachedInputFileSystem, ResolveContext, ResolverFactory } from 'enhanced-resolve'
import enhancedResolve from 'enhanced-resolve'
import { ResolveOptions } from 'webpack/types'
import { extendWebpackConfig, useNuxt } from '@nuxt/kit'

type UserResolveOptions = Parameters<typeof ResolverFactory['createResolver']>[0]
type ResolverOptions = Omit<UserResolveOptions, 'fileSystem'> & { fileSystem?: CachedInputFileSystem }
type UserResolveOptions = Parameters<typeof enhancedResolve.ResolverFactory['createResolver']>[0]
type ResolverOptions = Omit<UserResolveOptions, 'fileSystem'> & { fileSystem?: enhancedResolve.CachedInputFileSystem }

const DEFAULTS: UserResolveOptions = {
fileSystem: new CachedInputFileSystem(fs, 4000),
fileSystem: new enhancedResolve.CachedInputFileSystem(fs, 4000),
extensions: ['.ts', '.mjs', '.cjs', '.js', '.json'],
mainFields: ['module', 'main']
}

// Abstracted resolver factory which can be used in rollup, webpack, etc.
const createResolver = (resolveOptions: ResolverOptions) => {
const options = defu(resolveOptions, DEFAULTS) as UserResolveOptions
const resolver = ResolverFactory.createResolver(options)
const resolver = enhancedResolve.ResolverFactory.createResolver(options)

const root = options.roots?.[0] || '.'

const promisifiedResolve = promisify(resolver.resolve.bind(resolver)) as (context: object, path: string, request: string, resolveContext: ResolveContext) => Promise<string | false>
const promisifiedResolve = promisify(resolver.resolve.bind(resolver)) as (context: object, path: string, request: string, resolveContext: enhancedResolve.ResolveContext) => Promise<string | false>

const resolve = (id: string, importer?: string) => promisifiedResolve({}, importer || root, id, {})

Expand Down
1 change: 1 addition & 0 deletions packages/kit/build.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { defineBuildConfig } from 'unbuild'

export default defineBuildConfig({
declaration: true,
emitCJS: false,
entries: [
{
input: 'src/config/schema/index',
Expand Down
7 changes: 4 additions & 3 deletions packages/kit/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
"version": "0.10.0",
"repository": "nuxt/framework",
"license": "MIT",
"main": "./dist/index.cjs",
"type": "module",
"exports": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"files": [
"dist",
Expand All @@ -13,7 +14,7 @@
"prepack": "unbuild"
},
"devDependencies": {
"@types/lodash": "^4.14.175",
"@types/lodash.template": "^4",
"unbuild": "latest"
},
"dependencies": {
Expand All @@ -25,7 +26,7 @@
"hash-sum": "^2.0.0",
"hookable": "^5.0.0",
"jiti": "^1.12.6",
"lodash": "^4.17.21",
"lodash.template": "^4.5.0",
"pathe": "^0.2.0",
"rc9": "^1.2.0",
"scule": "^0.2.1",
Expand Down
4 changes: 2 additions & 2 deletions packages/kit/src/config/env.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { existsSync, promises as fsp } from 'fs'
import { resolve } from 'pathe'
import dotenv from 'dotenv'
import { parse as parseDotEnv } from 'dotenv'
import { LoadNuxtConfigOptions } from './load'

export interface LoadDotEnvOptions {
Expand Down Expand Up @@ -62,7 +62,7 @@ export async function loadDotenv (opts: LoadDotEnvOptions) {
const dotenvFile = resolve(opts.rootDir, opts.dotenvFile)

if (existsSync(dotenvFile)) {
const parsed = dotenv.parse(await fsp.readFile(dotenvFile, 'utf-8'))
const parsed = parseDotEnv(await fsp.readFile(dotenvFile, 'utf-8'))
Object.assign(env, parsed)
}

Expand Down
2 changes: 1 addition & 1 deletion packages/kit/src/config/schema/_common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -627,7 +627,7 @@ export default {
* @example
* ```js
* import fs from 'fs'
* import path from 'pathe'
* import path from 'path'
* export default {
* hooks: {
* build: {
Expand Down
6 changes: 3 additions & 3 deletions packages/kit/src/module/container.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import path from 'pathe'
import { parse, relative } from 'pathe'
import consola from 'consola'
import type { Nuxt, NuxtPluginTemplate, NuxtTemplate } from '../types/nuxt'
import { chainFn } from '../utils/task'
Expand Down Expand Up @@ -66,7 +66,7 @@ export function createModuleContainer (nuxt: Nuxt) {
/** Register a custom layout. If its name is 'error' it will override the default error layout. */
addLayout (tmpl: NuxtTemplate, name: string) {
const { filename, src } = addTemplate(tmpl)
const layoutName = name || path.parse(src).name
const layoutName = name || parse(src).name
const layout = nuxt.options.layouts[layoutName]

if (layout) {
Expand All @@ -88,7 +88,7 @@ export function createModuleContainer (nuxt: Nuxt) {
* @param dst - Path to layout file within the buildDir (`.nuxt/<dst>.vue`)
*/
addErrorLayout (dst: string) {
const relativeBuildDir = path.relative(nuxt.options.rootDir, nuxt.options.buildDir)
const relativeBuildDir = relative(nuxt.options.rootDir, nuxt.options.buildDir)
nuxt.options.ErrorPage = `~/${relativeBuildDir}/${dst}`
},

Expand Down
2 changes: 1 addition & 1 deletion packages/kit/src/module/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { existsSync, promises as fsp } from 'fs'
import { basename, extname, parse, resolve } from 'pathe'
import lodashTemplate from 'lodash/template'
import lodashTemplate from 'lodash.template'
import hash from 'hash-sum'
import type { WebpackPluginInstance, Configuration as WebpackConfig } from 'webpack'
import type { Plugin as VitePlugin, UserConfig as ViteConfig } from 'vite'
Expand Down
12 changes: 6 additions & 6 deletions packages/kit/src/nuxt.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { getContext } from 'unctx'
import { requireModule, tryRequireModule, tryResolveModule } from './utils/cjs'
import { importModule, tryImportModule, tryResolveModule } from './utils/cjs'
import type { Nuxt } from './types/nuxt'
import type { NuxtConfig } from './types/config'
import type { LoadNuxtConfigOptions } from './config/load'
Expand Down Expand Up @@ -50,14 +50,14 @@ export async function loadNuxt (opts: LoadNuxtOptions): Promise<Nuxt> {

// Nuxt 3
if (opts.version !== 2) {
const { loadNuxt } = requireModule('nuxt3', resolveOpts)
const { loadNuxt } = await importModule('nuxt3', resolveOpts)
const nuxt = await loadNuxt(opts)
return nuxt
}

// Nuxt 2
// @ts-ignore
const { loadNuxt } = tryRequireModule('nuxt-edge', resolveOpts) || requireModule('nuxt', resolveOpts)
const { loadNuxt } = await tryImportModule('nuxt-edge', resolveOpts) || await importModule('nuxt', resolveOpts)
const nuxt = await loadNuxt({
rootDir: opts.rootDir,
for: opts.dev ? 'dev' : 'build',
Expand All @@ -68,17 +68,17 @@ export async function loadNuxt (opts: LoadNuxtOptions): Promise<Nuxt> {
return nuxt as Nuxt
}

export function buildNuxt (nuxt: Nuxt): Promise<any> {
export async function buildNuxt (nuxt: Nuxt): Promise<any> {
const resolveOpts = { paths: nuxt.options.rootDir }

// Nuxt 3
if (nuxt.options._majorVersion === 3) {
const { build } = requireModule('nuxt3', resolveOpts)
const { build } = await importModule('nuxt3', resolveOpts)
return build(nuxt)
}

// Nuxt 2
// @ts-ignore
const { build } = tryRequireModule('nuxt-edge', resolveOpts) || requireModule('nuxt', resolveOpts)
const { build } = tryImportModule('nuxt-edge', resolveOpts) || tryImportModule('nuxt', resolveOpts)
return build(nuxt)
}
12 changes: 11 additions & 1 deletion packages/kit/src/utils/cjs.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { pathToFileURL } from 'url'
import { join, normalize } from 'pathe'
import jiti from 'jiti'

// TODO: use create-require for jest environment
const _require = jiti(process.cwd())
const _require = jiti(process.cwd(), { interopDefault: true })

export interface ResolveModuleOptions {
paths?: string | string[]
Expand Down Expand Up @@ -125,6 +126,15 @@ export function requireModule (id: string, opts: RequireModuleOptions = {}) {
return requiredModule
}

export function importModule (id: string, opts: RequireModuleOptions = {}) {
const resolvedPath = resolveModule(id, opts)
return import(pathToFileURL(resolvedPath).href)
}

export function tryImportModule (id: string, opts: RequireModuleOptions = {}) {
return importModule(id, opts).catch(() => undefined)
}

/** Try to require a module, but don't emit an error if the module can't be required. */
export function tryRequireModule (id: string, opts: RequireModuleOptions = {}) {
try {
Expand Down
1 change: 1 addition & 0 deletions packages/nitro/build.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { defineBuildConfig } from 'unbuild'

export default defineBuildConfig({
declaration: true,
emitCJS: false,
entries: [
'src/index',
{ input: 'src/runtime/', outDir: 'dist/runtime', format: 'esm' },
Expand Down
3 changes: 0 additions & 3 deletions packages/nitro/compat.js

This file was deleted.

Loading

0 comments on commit 6e49637

Please sign in to comment.