diff --git a/benchmarks/package.json b/benchmarks/package.json index c5824558a4..f579a3b6f6 100644 --- a/benchmarks/package.json +++ b/benchmarks/package.json @@ -3,8 +3,8 @@ "scripts": { "build": "npm run nuxt -- build", "start": "npm run build && npm run nuxt -- start", - "nuxt": "node -r esm ../packages/cli/bin/nuxt-cli.js", + "nuxt": "jiti ../packages/cli/bin/nuxt-cli.js", "bench:stateless": "ab -c1 -n3000 http://127.0.0.1:3000/stateless", "bench:stateless-big": "ab -c1 -n500 http://127.0.0.1:3000/stateless-big" } -} \ No newline at end of file +} diff --git a/distributions/nuxt-start/bin/nuxt-start.js b/distributions/nuxt-start/bin/nuxt-start.js index d3b7f83011..32112560b7 100755 --- a/distributions/nuxt-start/bin/nuxt-start.js +++ b/distributions/nuxt-start/bin/nuxt-start.js @@ -4,6 +4,8 @@ if (process.argv[2] !== 'start') { process.argv.splice(2, 0, 'start') } +global.__NUXT_PATHS__ = (global.__NUXT_PATHS__ || []).concat(__dirname) + const suffix = require('../package.json').name.includes('-edge') ? '-edge' : '' require('@nuxt/cli' + suffix).run() .catch((error) => { diff --git a/distributions/nuxt-start/package.js b/distributions/nuxt-start/package.js index 2cf87382f8..a4e7e883c9 100644 --- a/distributions/nuxt-start/package.js +++ b/distributions/nuxt-start/package.js @@ -19,5 +19,19 @@ export default { await pkg.writePackage() } - } + }, + ignoreUnused: [ + // directly used by bin + '@nuxt/cli', + // discovered by config + '@nuxt/telemetry', + // vue-app externals for ssr + 'node-fetch', + 'vue', + 'vue-client-only', + 'vue-meta', + 'vue-no-ssr', + 'vue-router', + 'vuex' + ] } diff --git a/distributions/nuxt/bin/nuxt.js b/distributions/nuxt/bin/nuxt.js index 25ea739baf..b22f0844df 100755 --- a/distributions/nuxt/bin/nuxt.js +++ b/distributions/nuxt/bin/nuxt.js @@ -1,5 +1,7 @@ #!/usr/bin/env node +global.__NUXT_PATHS__ = (global.__NUXT_PATHS__ || []).concat(__dirname) + const suffix = require('../package.json').name.includes('-edge') ? '-edge' : '' require('@nuxt/cli' + suffix).run() .catch((error) => { diff --git a/distributions/nuxt/package.js b/distributions/nuxt/package.js index e082988663..9cd159c102 100644 --- a/distributions/nuxt/package.js +++ b/distributions/nuxt/package.js @@ -9,5 +9,13 @@ export default { 'README.md' ]) } - } + }, + ignoreUnused: [ + // used by postinstall + '@nuxt/opencollective', + // discovered by config + '@nuxt/components', + '@nuxt/loading-screen', + '@nuxt/telemetry' + ] } diff --git a/distributions/nuxt/package.json b/distributions/nuxt/package.json index 2e147fefa3..27a2009a60 100644 --- a/distributions/nuxt/package.json +++ b/distributions/nuxt/package.json @@ -48,7 +48,9 @@ } ], "main": "dist/nuxt.js", - "bin": "bin/nuxt.js", + "bin": { + "nuxt": "./bin/nuxt.js" + }, "files": [ "bin", "dist", @@ -65,8 +67,7 @@ "@nuxt/generator": "2.14.12", "@nuxt/loading-screen": "^2.0.3", "@nuxt/opencollective": "^0.3.2", - "@nuxt/telemetry": "^1.3.0", - "@nuxt/webpack": "2.14.12" + "@nuxt/telemetry": "^1.3.0" }, "engines": { "node": ">=10.13.0", diff --git a/distributions/nuxt/src/index.js b/distributions/nuxt/src/index.js index c64dc00e73..36b77ca935 100644 --- a/distributions/nuxt/src/index.js +++ b/distributions/nuxt/src/index.js @@ -1,4 +1,5 @@ export * from '@nuxt/core' export * from '@nuxt/builder' export * from '@nuxt/generator' + export { getWebpackConfig } from '@nuxt/cli' diff --git a/package.json b/package.json index 8be43bf1aa..55d061c78b 100644 --- a/package.json +++ b/package.json @@ -12,18 +12,18 @@ "scripts": { "audit": "improved-yarn-audit --ignore-dev-deps --min-severity moderate -e 1548", "build": "yarn clean && yarn pkg", - "changelog": "node -r esm scripts/changelog.js", + "changelog": "jiti scripts/changelog.js", "clean": "yarn clean:build && yarn clean:examples && yarn clean:test", "clean:build": "rimraf distributions/*/dist packages/*/dist", "clean:examples": "rimraf examples/*/dist examples/*/.nuxt", "clean:test": "rimraf test/fixtures/*/dist test/fixtures/*/.nuxt*/", - "dev": "node -r esm ./scripts/dev.js", + "dev": "jiti ./scripts/dev.js", "postinstall": "lerna link && yarn dev", "lint": "eslint --ext .js,.mjs,.vue .", "lint:app": "eslint-multiplexer eslint --ignore-path packages/vue-app/template/.eslintignore 'test/fixtures/!(missing-plugin)/.nuxt!(-dev)/**' | eslint-multiplexer -b", "ls-lint": "npx @ls-lint/ls-lint", - "nuxt": "node -r esm ./packages/cli/bin/nuxt-cli.js", - "pkg": "node -r esm ./scripts/package", + "nuxt": "jiti ./packages/cli/bin/nuxt-cli.js", + "pkg": "jiti ./scripts/package", "test": "yarn test:fixtures && yarn test:dev && yarn test:unit && test:types", "test:dev": "jest test/dev --forceExit", "test:e2e": "jest -i test/e2e --forceExit", @@ -51,7 +51,6 @@ "cross-spawn": "^7.0.3", "eslint": "^7.16.0", "eslint-multiplexer": "^2.0.0", - "esm": "^3.2.25", "execa": "^5.0.0", "express": "^4.17.1", "finalhandler": "^1.1.2", diff --git a/packages/babel-preset-app/src/index.js b/packages/babel-preset-app/src/index.js index 360452b470..2bb1f335ba 100644 --- a/packages/babel-preset-app/src/index.js +++ b/packages/babel-preset-app/src/index.js @@ -4,14 +4,14 @@ const coreJsMeta = { es6: 'es6', es7: 'es7' }, - builtIns: '@babel/compat-data/corejs2-built-ins' + builtIns: require.resolve('@babel/compat-data/corejs2-built-ins') }, 3: { prefixes: { es6: 'es', es7: 'es' }, - builtIns: 'core-js-compat/data' + builtIns: require.resolve('core-js-compat/data') } } diff --git a/packages/builder/package.js b/packages/builder/package.js index 19d0ef6e2d..fc1a7da314 100644 --- a/packages/builder/package.js +++ b/packages/builder/package.js @@ -1,3 +1,7 @@ export default { - build: true + build: true, + ignoreUnused: [ + // used for legacy _ in template context + 'lodash' + ] } diff --git a/packages/builder/package.json b/packages/builder/package.json index 7328f0d0af..3b854543c7 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -23,8 +23,7 @@ "pify": "^5.0.0", "semver": "^7.3.4", "serialize-javascript": "^5.0.1", - "upath": "^2.0.1", - "url-polyfill": "^1.1.12" + "upath": "^2.0.1" }, "publishConfig": { "access": "public" diff --git a/packages/builder/src/builder.js b/packages/builder/src/builder.js index 283828bab0..122679479c 100644 --- a/packages/builder/src/builder.js +++ b/packages/builder/src/builder.js @@ -8,13 +8,7 @@ import hash from 'hash-sum' import pify from 'pify' import upath from 'upath' import semver from 'semver' - -import debounce from 'lodash/debounce' -import omit from 'lodash/omit' -import template from 'lodash/template' -import uniq from 'lodash/uniq' -import uniqBy from 'lodash/uniqBy' - +import { debounce, omit, template, uniq, uniqBy } from 'lodash' import { r, createRoutes, @@ -27,6 +21,9 @@ import { TARGETS } from '@nuxt/utils' +import { template as VueAppTemplate } from '@nuxt/vue-app' +import { BundleBuilder as WebpackBuilder } from '@nuxt/webpack' + import Ignore from './ignore' import BuildContext from './context/build' import TemplateContext from './context/template' @@ -74,7 +71,7 @@ export default class Builder { } // Resolve template - this.template = this.options.build.template || '@nuxt/vue-app' + this.template = this.options.build.template || VueAppTemplate if (typeof this.template === 'string') { this.template = this.nuxt.resolver.requireModule(this.template).template } @@ -96,7 +93,7 @@ export default class Builder { const context = new BuildContext(this) if (typeof BundleBuilder !== 'function') { - ({ BundleBuilder } = require('@nuxt/webpack')) + BundleBuilder = WebpackBuilder } return new BundleBuilder(context) diff --git a/packages/builder/src/context/template.js b/packages/builder/src/context/template.js index 7ed6e5c45a..6cdf3f6378 100644 --- a/packages/builder/src/context/template.js +++ b/packages/builder/src/context/template.js @@ -1,10 +1,10 @@ import hash from 'hash-sum' import consola from 'consola' -import uniqBy from 'lodash/uniqBy' +import { uniqBy } from 'lodash' import serialize from 'serialize-javascript' import devalue from '@nuxt/devalue' -import { r, wp, wChunk, serializeFunction, isFullStatic } from '@nuxt/utils' +import { r, wp, wChunk, serializeFunction, isFullStatic, requireModule } from '@nuxt/utils' export default class TemplateContext { constructor (builder, options) { @@ -70,7 +70,7 @@ export default class TemplateContext { get (target, prop) { if (!lodash) { consola.warn('Avoid using _ inside templates') - lodash = require('lodash') + lodash = requireModule('lodash') } return lodash[prop] } diff --git a/packages/builder/test/__utils__/index.js b/packages/builder/test/__utils__/index.js index 86fa5b99df..147df3d59c 100644 --- a/packages/builder/test/__utils__/index.js +++ b/packages/builder/test/__utils__/index.js @@ -17,7 +17,7 @@ export const createNuxt = () => ({ hook: jest.fn(), callHook: jest.fn(), resolver: { - requireModule: jest.fn(() => ({ template: 'builder-template' })), + requireModule: jest.fn(), resolveAlias: jest.fn(src => `resolveAlias(${src})`), resolvePath: jest.fn(src => `resolvePath(${src})`) } diff --git a/packages/builder/test/builder.ctor.test.js b/packages/builder/test/builder.ctor.test.js index 8980d0e255..a866d875b9 100644 --- a/packages/builder/test/builder.ctor.test.js +++ b/packages/builder/test/builder.ctor.test.js @@ -1,9 +1,11 @@ import consola from 'consola' +import { template as VueAppTemplate } from '@nuxt/vue-app' import { relativeTo, determineGlobals } from '@nuxt/utils' import Builder from '../src/builder' import { createNuxt } from './__utils__' +jest.mock('@nuxt/vue-app') jest.mock('@nuxt/utils') jest.mock('../src/ignore') @@ -37,9 +39,7 @@ describe('builder: builder constructor', () => { expect(builder._buildStatus).toEqual(1) - expect(nuxt.resolver.requireModule).toBeCalledTimes(1) - expect(nuxt.resolver.requireModule).toBeCalledWith('@nuxt/vue-app') - expect(builder.template).toEqual('builder-template') + expect(builder.template).toBe(VueAppTemplate) expect(builder.bundleBuilder).toBe(bundleBuilder) }) @@ -112,6 +112,5 @@ describe('builder: builder constructor', () => { const builder = new Builder(nuxt, bundleBuilder) expect(builder.template).toBe(nuxt.options.build.template) - expect(nuxt.resolver.requireModule).not.toBeCalled() }) }) diff --git a/packages/builder/test/builder.generate.test.js b/packages/builder/test/builder.generate.test.js index 88c42342c4..913f452ebe 100644 --- a/packages/builder/test/builder.generate.test.js +++ b/packages/builder/test/builder.generate.test.js @@ -1,8 +1,8 @@ import path from 'path' +import lodash from 'lodash' import Glob from 'glob' import fs from 'fs-extra' import consola from 'consola' -import template from 'lodash/template' import { r, createRoutes, stripWhitespace } from '@nuxt/utils' import { BundleBuilder } from '@nuxt/webpack' import Builder from '../src/builder' @@ -10,9 +10,12 @@ import TemplateContext from '../src/context/template' import { createNuxt } from './__utils__' jest.mock('glob') +jest.mock('lodash', () => ({ + ...jest.requireActual('lodash'), + template: jest.fn() +})) jest.mock('pify', () => fn => fn) jest.mock('fs-extra') -jest.mock('lodash/template') jest.mock('@nuxt/utils') jest.mock('../src/context/template', () => jest.fn()) jest.mock('../src/ignore', () => function () { @@ -373,7 +376,7 @@ describe('builder: builder generate', () => { const builder = new Builder(nuxt, BundleBuilder) builder.relativeToBuild = jest.fn() const templateFn = jest.fn(() => 'compiled content') - template.mockImplementation(() => templateFn) + lodash.template.mockImplementation(() => templateFn) stripWhitespace.mockImplementation(content => `trim(${content})`) const templateContext = { @@ -403,10 +406,10 @@ describe('builder: builder generate', () => { expect(fs.readFile).nthCalledWith(1, '/var/nuxt/src/foo.js', 'utf8') expect(fs.readFile).nthCalledWith(2, '/var/nuxt/src/bar.js', 'utf8') expect(fs.readFile).nthCalledWith(3, '/var/nuxt/src/baz.js', 'utf8') - expect(template).toBeCalledTimes(3) - expect(template).nthCalledWith(1, 'readFile(/var/nuxt/src/foo.js, utf8)', templateContext.templateOptions) - expect(template).nthCalledWith(2, 'readFile(/var/nuxt/src/bar.js, utf8)', templateContext.templateOptions) - expect(template).nthCalledWith(3, 'readFile(/var/nuxt/src/baz.js, utf8)', templateContext.templateOptions) + expect(lodash.template).toBeCalledTimes(3) + expect(lodash.template).nthCalledWith(1, 'readFile(/var/nuxt/src/foo.js, utf8)', templateContext.templateOptions) + expect(lodash.template).nthCalledWith(2, 'readFile(/var/nuxt/src/bar.js, utf8)', templateContext.templateOptions) + expect(lodash.template).nthCalledWith(3, 'readFile(/var/nuxt/src/baz.js, utf8)', templateContext.templateOptions) expect(templateFn).toBeCalledTimes(3) expect(templateFn).nthCalledWith(1, { ...templateContext.templateVars, @@ -442,7 +445,7 @@ describe('builder: builder generate', () => { const nuxt = createNuxt() const builder = new Builder(nuxt, BundleBuilder) builder.relativeToBuild = jest.fn() - template.mockImplementation(() => { + lodash.template.mockImplementation(() => { throw new Error('compile failed') }) diff --git a/packages/builder/test/builder.watch.test.js b/packages/builder/test/builder.watch.test.js index 46d0245a4c..255d30be8b 100644 --- a/packages/builder/test/builder.watch.test.js +++ b/packages/builder/test/builder.watch.test.js @@ -1,24 +1,25 @@ import path from 'path' import chokidar from 'chokidar' import upath from 'upath' -import debounce from 'lodash/debounce' +import { debounce } from 'lodash' import { r, isString, isPureObject } from '@nuxt/utils' - import { BundleBuilder } from '@nuxt/webpack' import Builder from '../src/builder' import { createNuxt } from './__utils__' -jest.mock('@nuxt/webpack') +jest.mock('lodash', () => ({ + ...jest.requireActual('lodash'), + debounce: jest.fn(fn => fn) +})) jest.mock('chokidar', () => ({ watch: jest.fn().mockReturnThis(), on: jest.fn().mockReturnThis(), close: jest.fn().mockReturnThis() })) jest.mock('upath', () => ({ normalizeSafe: jest.fn(src => src) })) -jest.mock('lodash/debounce', () => jest.fn(fn => fn)) jest.mock('@nuxt/utils') -jest.mock('../src/ignore') jest.mock('@nuxt/webpack') +jest.mock('../src/ignore') describe('builder: builder watch', () => { beforeEach(() => { diff --git a/packages/builder/test/context/template.test.js b/packages/builder/test/context/template.test.js index 53eb27704f..726619cf72 100644 --- a/packages/builder/test/context/template.test.js +++ b/packages/builder/test/context/template.test.js @@ -6,7 +6,11 @@ import devalue from '@nuxt/devalue' import { r, wp, wChunk, serializeFunction } from '@nuxt/utils' import TemplateContext from '../../src/context/template' -jest.mock('lodash', () => ({ test: 'test lodash', warn: 'only once' })) +jest.mock('lodash', () => ({ + ...jest.requireActual('lodash'), + test: 'test lodash', + warn: 'only once' +})) describe('builder: buildContext', () => { const builder = { diff --git a/packages/cli/package.js b/packages/cli/package.js index 19d0ef6e2d..8cb0cce11d 100644 --- a/packages/cli/package.js +++ b/packages/cli/package.js @@ -1,3 +1,5 @@ export default { - build: true + build: true, + ignoreUnused: ['crc'], + externals: ['crc/lib/crc32'] } diff --git a/packages/cli/package.json b/packages/cli/package.json index 421ff02bd2..3f01258419 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -20,13 +20,14 @@ "connect": "^3.7.0", "consola": "^2.15.0", "crc": "^3.8.0", + "defu": "^3.2.2", "destr": "^1.0.1", - "esm": "^3.2.25", "execa": "^5.0.0", "exit": "^0.1.2", "fs-extra": "^9.0.1", "globby": "^11.0.1", "hable": "^3.0.0", + "lodash": "^4.4.2", "minimist": "^1.2.5", "opener": "1.5.2", "pretty-bytes": "^5.4.1", diff --git a/packages/cli/src/commands/webpack.js b/packages/cli/src/commands/webpack.js index 2059b33847..bd950bb011 100644 --- a/packages/cli/src/commands/webpack.js +++ b/packages/cli/src/commands/webpack.js @@ -1,6 +1,6 @@ import util from 'util' import consola from 'consola' -import get from 'lodash/get' +import { get } from 'lodash' import { common } from '../options' export default { diff --git a/packages/cli/src/imports.js b/packages/cli/src/imports.js index 367b690088..d046d51d98 100644 --- a/packages/cli/src/imports.js +++ b/packages/cli/src/imports.js @@ -1,31 +1,18 @@ -import path from 'path' +import { requireModule } from '@nuxt/utils' -const localNodeModules = path.resolve(process.cwd(), 'node_modules') - -// Prefer importing modules from local node_modules (for NPX and global bin) -async function _import (modulePath) { - for (const mp of [ - path.resolve(localNodeModules, modulePath), - modulePath - ]) { - try { - return await import(mp) - } catch (e) { - if (e.code !== 'MODULE_NOT_FOUND') { - throw e - } +export const importModule = (id) => { + try { + return Promise.resolve(requireModule(id)) + } catch (err) { + if (err.code === 'MODULE_NOT_FOUND') { + err.message = `Cannot import module '${id}'` } + return Promise.reject(err) } - - const error = new Error(`Cannot import module '${modulePath}'`) - error.code = 'MODULE_NOT_FOUND' - throw error } -export const builder = () => _import('@nuxt/builder') -export const webpack = () => _import('@nuxt/webpack') -export const generator = () => _import('@nuxt/generator') -export const core = () => _import('@nuxt/core') -export const server = () => _import('@nuxt/server') - -export const importModule = _import +export const builder = () => importModule('@nuxt/builder') +export const webpack = () => importModule('@nuxt/webpack') +export const generator = () => importModule('@nuxt/generator') +export const core = () => importModule('@nuxt/core') +export const server = () => importModule('@nuxt/server') diff --git a/packages/cli/src/run.js b/packages/cli/src/run.js index 4687589926..2a65a08a5b 100644 --- a/packages/cli/src/run.js +++ b/packages/cli/src/run.js @@ -2,6 +2,7 @@ import { existsSync } from 'fs' import { resolve } from 'path' import execa from 'execa' import consola from 'consola' +import { requireModule } from '@nuxt/utils' import { name as pkgName } from '../package.json' import NuxtCommand from './command' import setup from './setup' @@ -12,7 +13,7 @@ export default async function run (_argv, hooks = {}) { // Check for not installing both nuxt and nuxt-edge const dupPkg = pkgName === '@nuxt/cli-edge' ? 'cli' : 'cli-edge' const dupPkgJSON = resolve(__dirname, '../..' /* dist/../.. */, dupPkg, 'package.json') - if (existsSync(dupPkgJSON) && require(dupPkgJSON).name !== '@nuxt/' + dupPkg) { + if (existsSync(dupPkgJSON) && requireModule(dupPkgJSON).name !== '@nuxt/' + dupPkg) { consola.warn('Both `nuxt` and `nuxt-edge` dependencies are installed! Please choose one and remove the other one from dependencies.') } diff --git a/packages/cli/src/utils/config.js b/packages/cli/src/utils/config.js index db1bece712..0f3122b40c 100644 --- a/packages/cli/src/utils/config.js +++ b/packages/cli/src/utils/config.js @@ -1,5 +1,5 @@ import path from 'path' -import defaultsDeep from 'lodash/defaultsDeep' +import defu from 'defu' import { loadNuxtConfig as _loadNuxtConfig, getDefaultNuxtConfig } from '@nuxt/config' export async function loadNuxtConfig (argv, configContext) { @@ -24,10 +24,10 @@ export async function loadNuxtConfig (argv, configContext) { } // Server options - options.server = defaultsDeep({ - port: argv.port || undefined, - host: argv.hostname || undefined, - socket: argv['unix-socket'] || undefined + options.server = defu({ + port: argv.port || null, + host: argv.hostname || null, + socket: argv['unix-socket'] || null }, options.server || {}, getDefaultNuxtConfig().server) return options diff --git a/packages/cli/src/utils/serve.js b/packages/cli/src/utils/serve.js index 48303ef5c1..055b6ff601 100644 --- a/packages/cli/src/utils/serve.js +++ b/packages/cli/src/utils/serve.js @@ -5,6 +5,7 @@ import connect from 'connect' import serveStatic from 'serve-static' import compression from 'compression' import { getNuxtConfig } from '@nuxt/config' +import { requireModule } from '@nuxt/utils' import { showBanner } from '../utils/banner' import * as imports from '../imports' @@ -16,7 +17,7 @@ export async function serve (cmd) { try { // overwrites with build config - const buildConfig = require(join(options.buildDir, 'nuxt/config.json')) + const buildConfig = requireModule(join(options.buildDir, 'nuxt/config.json')) options.target = buildConfig.target } catch (err) { } diff --git a/packages/cli/test/unit/__snapshots__/webpack.test.js.snap b/packages/cli/test/unit/__snapshots__/webpack.test.js.snap index 8c912c53c4..78da605897 100644 --- a/packages/cli/test/unit/__snapshots__/webpack.test.js.snap +++ b/packages/cli/test/unit/__snapshots__/webpack.test.js.snap @@ -2,7 +2,7 @@ exports[`webpack getWebpackConfig() 1`] = ` "Object { - \\"loader\\": \\"vue-loader\\", + \\"loader\\": \\"/node_modules/vue-loader/lib/index.js\\", \\"options\\": Object { \\"productionMode\\": true, \\"transformAssetUrls\\": Object { @@ -24,7 +24,7 @@ exports[`webpack nuxt webpack devtool 1`] = ` exports[`webpack nuxt webpack module.rules 1`] = ` "Array [ Object { - \\"loader\\": \\"vue-loader\\", + \\"loader\\": \\"/node_modules/vue-loader/lib/index.js\\", \\"options\\": Object { \\"productionMode\\": true, \\"transformAssetUrls\\": Object { @@ -90,13 +90,13 @@ exports[`webpack nuxt webpack module.rules 1`] = ` \\"resourceQuery\\": /module/, \\"use\\": Array [ Object { - \\"loader\\": \\"vue-style-loader\\", + \\"loader\\": \\"/node_modules/vue-style-loader/index.js\\", \\"options\\": Object { \\"sourceMap\\": false, }, }, Object { - \\"loader\\": \\"css-loader\\", + \\"loader\\": \\"/node_modules/css-loader/dist/cjs.js\\", \\"options\\": Object { \\"esModule\\": false, \\"importLoaders\\": 2, @@ -107,7 +107,7 @@ exports[`webpack nuxt webpack module.rules 1`] = ` }, }, Object { - \\"loader\\": \\"postcss-loader\\", + \\"loader\\": \\"/node_modules/postcss-loader/src/index.js\\", \\"options\\": Object { \\"order\\": \\"presetEnvAndCssnanoLast\\", \\"plugins\\": Array [ @@ -124,13 +124,13 @@ exports[`webpack nuxt webpack module.rules 1`] = ` Object { \\"use\\": Array [ Object { - \\"loader\\": \\"vue-style-loader\\", + \\"loader\\": \\"/node_modules/vue-style-loader/index.js\\", \\"options\\": Object { \\"sourceMap\\": false, }, }, Object { - \\"loader\\": \\"css-loader\\", + \\"loader\\": \\"/node_modules/css-loader/dist/cjs.js\\", \\"options\\": Object { \\"esModule\\": false, \\"importLoaders\\": 2, @@ -138,7 +138,7 @@ exports[`webpack nuxt webpack module.rules 1`] = ` }, }, Object { - \\"loader\\": \\"postcss-loader\\", + \\"loader\\": \\"/node_modules/postcss-loader/src/index.js\\", \\"options\\": Object { \\"order\\": \\"presetEnvAndCssnanoLast\\", \\"plugins\\": Array [ @@ -161,13 +161,13 @@ exports[`webpack nuxt webpack module.rules 1`] = ` \\"resourceQuery\\": /module/, \\"use\\": Array [ Object { - \\"loader\\": \\"vue-style-loader\\", + \\"loader\\": \\"/node_modules/vue-style-loader/index.js\\", \\"options\\": Object { \\"sourceMap\\": false, }, }, Object { - \\"loader\\": \\"css-loader\\", + \\"loader\\": \\"/node_modules/css-loader/dist/cjs.js\\", \\"options\\": Object { \\"esModule\\": false, \\"importLoaders\\": 2, @@ -178,7 +178,7 @@ exports[`webpack nuxt webpack module.rules 1`] = ` }, }, Object { - \\"loader\\": \\"postcss-loader\\", + \\"loader\\": \\"/node_modules/postcss-loader/src/index.js\\", \\"options\\": Object { \\"order\\": \\"presetEnvAndCssnanoLast\\", \\"plugins\\": Array [ @@ -195,13 +195,13 @@ exports[`webpack nuxt webpack module.rules 1`] = ` Object { \\"use\\": Array [ Object { - \\"loader\\": \\"vue-style-loader\\", + \\"loader\\": \\"/node_modules/vue-style-loader/index.js\\", \\"options\\": Object { \\"sourceMap\\": false, }, }, Object { - \\"loader\\": \\"css-loader\\", + \\"loader\\": \\"/node_modules/css-loader/dist/cjs.js\\", \\"options\\": Object { \\"esModule\\": false, \\"importLoaders\\": 2, @@ -209,7 +209,7 @@ exports[`webpack nuxt webpack module.rules 1`] = ` }, }, Object { - \\"loader\\": \\"postcss-loader\\", + \\"loader\\": \\"/node_modules/postcss-loader/src/index.js\\", \\"options\\": Object { \\"order\\": \\"presetEnvAndCssnanoLast\\", \\"plugins\\": Array [ @@ -232,13 +232,13 @@ exports[`webpack nuxt webpack module.rules 1`] = ` \\"resourceQuery\\": /module/, \\"use\\": Array [ Object { - \\"loader\\": \\"vue-style-loader\\", + \\"loader\\": \\"/node_modules/vue-style-loader/index.js\\", \\"options\\": Object { \\"sourceMap\\": false, }, }, Object { - \\"loader\\": \\"css-loader\\", + \\"loader\\": \\"/node_modules/css-loader/dist/cjs.js\\", \\"options\\": Object { \\"esModule\\": false, \\"importLoaders\\": 2, @@ -249,7 +249,7 @@ exports[`webpack nuxt webpack module.rules 1`] = ` }, }, Object { - \\"loader\\": \\"postcss-loader\\", + \\"loader\\": \\"/node_modules/postcss-loader/src/index.js\\", \\"options\\": Object { \\"order\\": \\"presetEnvAndCssnanoLast\\", \\"plugins\\": Array [ @@ -272,13 +272,13 @@ exports[`webpack nuxt webpack module.rules 1`] = ` Object { \\"use\\": Array [ Object { - \\"loader\\": \\"vue-style-loader\\", + \\"loader\\": \\"/node_modules/vue-style-loader/index.js\\", \\"options\\": Object { \\"sourceMap\\": false, }, }, Object { - \\"loader\\": \\"css-loader\\", + \\"loader\\": \\"/node_modules/css-loader/dist/cjs.js\\", \\"options\\": Object { \\"esModule\\": false, \\"importLoaders\\": 2, @@ -286,7 +286,7 @@ exports[`webpack nuxt webpack module.rules 1`] = ` }, }, Object { - \\"loader\\": \\"postcss-loader\\", + \\"loader\\": \\"/node_modules/postcss-loader/src/index.js\\", \\"options\\": Object { \\"order\\": \\"presetEnvAndCssnanoLast\\", \\"plugins\\": Array [ @@ -315,13 +315,13 @@ exports[`webpack nuxt webpack module.rules 1`] = ` \\"resourceQuery\\": /module/, \\"use\\": Array [ Object { - \\"loader\\": \\"vue-style-loader\\", + \\"loader\\": \\"/node_modules/vue-style-loader/index.js\\", \\"options\\": Object { \\"sourceMap\\": false, }, }, Object { - \\"loader\\": \\"css-loader\\", + \\"loader\\": \\"/node_modules/css-loader/dist/cjs.js\\", \\"options\\": Object { \\"esModule\\": false, \\"importLoaders\\": 2, @@ -332,7 +332,7 @@ exports[`webpack nuxt webpack module.rules 1`] = ` }, }, Object { - \\"loader\\": \\"postcss-loader\\", + \\"loader\\": \\"/node_modules/postcss-loader/src/index.js\\", \\"options\\": Object { \\"order\\": \\"presetEnvAndCssnanoLast\\", \\"plugins\\": Array [ @@ -345,7 +345,7 @@ exports[`webpack nuxt webpack module.rules 1`] = ` }, }, Object { - \\"loader\\": \\"sass-loader\\", + \\"loader\\": \\"/node_modules/sass-loader/dist/cjs.js\\", \\"options\\": Object { \\"sassOptions\\": Object { \\"indentedSyntax\\": true, @@ -358,13 +358,13 @@ exports[`webpack nuxt webpack module.rules 1`] = ` Object { \\"use\\": Array [ Object { - \\"loader\\": \\"vue-style-loader\\", + \\"loader\\": \\"/node_modules/vue-style-loader/index.js\\", \\"options\\": Object { \\"sourceMap\\": false, }, }, Object { - \\"loader\\": \\"css-loader\\", + \\"loader\\": \\"/node_modules/css-loader/dist/cjs.js\\", \\"options\\": Object { \\"esModule\\": false, \\"importLoaders\\": 2, @@ -372,7 +372,7 @@ exports[`webpack nuxt webpack module.rules 1`] = ` }, }, Object { - \\"loader\\": \\"postcss-loader\\", + \\"loader\\": \\"/node_modules/postcss-loader/src/index.js\\", \\"options\\": Object { \\"order\\": \\"presetEnvAndCssnanoLast\\", \\"plugins\\": Array [ @@ -385,7 +385,7 @@ exports[`webpack nuxt webpack module.rules 1`] = ` }, }, Object { - \\"loader\\": \\"sass-loader\\", + \\"loader\\": \\"/node_modules/sass-loader/dist/cjs.js\\", \\"options\\": Object { \\"sassOptions\\": Object { \\"indentedSyntax\\": true, @@ -404,13 +404,13 @@ exports[`webpack nuxt webpack module.rules 1`] = ` \\"resourceQuery\\": /module/, \\"use\\": Array [ Object { - \\"loader\\": \\"vue-style-loader\\", + \\"loader\\": \\"/node_modules/vue-style-loader/index.js\\", \\"options\\": Object { \\"sourceMap\\": false, }, }, Object { - \\"loader\\": \\"css-loader\\", + \\"loader\\": \\"/node_modules/css-loader/dist/cjs.js\\", \\"options\\": Object { \\"esModule\\": false, \\"importLoaders\\": 2, @@ -421,7 +421,7 @@ exports[`webpack nuxt webpack module.rules 1`] = ` }, }, Object { - \\"loader\\": \\"postcss-loader\\", + \\"loader\\": \\"/node_modules/postcss-loader/src/index.js\\", \\"options\\": Object { \\"order\\": \\"presetEnvAndCssnanoLast\\", \\"plugins\\": Array [ @@ -434,7 +434,7 @@ exports[`webpack nuxt webpack module.rules 1`] = ` }, }, Object { - \\"loader\\": \\"sass-loader\\", + \\"loader\\": \\"/node_modules/sass-loader/dist/cjs.js\\", \\"options\\": Object { \\"sourceMap\\": false, }, @@ -444,13 +444,13 @@ exports[`webpack nuxt webpack module.rules 1`] = ` Object { \\"use\\": Array [ Object { - \\"loader\\": \\"vue-style-loader\\", + \\"loader\\": \\"/node_modules/vue-style-loader/index.js\\", \\"options\\": Object { \\"sourceMap\\": false, }, }, Object { - \\"loader\\": \\"css-loader\\", + \\"loader\\": \\"/node_modules/css-loader/dist/cjs.js\\", \\"options\\": Object { \\"esModule\\": false, \\"importLoaders\\": 2, @@ -458,7 +458,7 @@ exports[`webpack nuxt webpack module.rules 1`] = ` }, }, Object { - \\"loader\\": \\"postcss-loader\\", + \\"loader\\": \\"/node_modules/postcss-loader/src/index.js\\", \\"options\\": Object { \\"order\\": \\"presetEnvAndCssnanoLast\\", \\"plugins\\": Array [ @@ -471,7 +471,7 @@ exports[`webpack nuxt webpack module.rules 1`] = ` }, }, Object { - \\"loader\\": \\"sass-loader\\", + \\"loader\\": \\"/node_modules/sass-loader/dist/cjs.js\\", \\"options\\": Object { \\"sourceMap\\": false, }, @@ -487,13 +487,13 @@ exports[`webpack nuxt webpack module.rules 1`] = ` \\"resourceQuery\\": /module/, \\"use\\": Array [ Object { - \\"loader\\": \\"vue-style-loader\\", + \\"loader\\": \\"/node_modules/vue-style-loader/index.js\\", \\"options\\": Object { \\"sourceMap\\": false, }, }, Object { - \\"loader\\": \\"css-loader\\", + \\"loader\\": \\"/node_modules/css-loader/dist/cjs.js\\", \\"options\\": Object { \\"esModule\\": false, \\"importLoaders\\": 2, @@ -504,7 +504,7 @@ exports[`webpack nuxt webpack module.rules 1`] = ` }, }, Object { - \\"loader\\": \\"postcss-loader\\", + \\"loader\\": \\"/node_modules/postcss-loader/src/index.js\\", \\"options\\": Object { \\"order\\": \\"presetEnvAndCssnanoLast\\", \\"plugins\\": Array [ @@ -527,13 +527,13 @@ exports[`webpack nuxt webpack module.rules 1`] = ` Object { \\"use\\": Array [ Object { - \\"loader\\": \\"vue-style-loader\\", + \\"loader\\": \\"/node_modules/vue-style-loader/index.js\\", \\"options\\": Object { \\"sourceMap\\": false, }, }, Object { - \\"loader\\": \\"css-loader\\", + \\"loader\\": \\"/node_modules/css-loader/dist/cjs.js\\", \\"options\\": Object { \\"esModule\\": false, \\"importLoaders\\": 2, @@ -541,7 +541,7 @@ exports[`webpack nuxt webpack module.rules 1`] = ` }, }, Object { - \\"loader\\": \\"postcss-loader\\", + \\"loader\\": \\"/node_modules/postcss-loader/src/index.js\\", \\"options\\": Object { \\"order\\": \\"presetEnvAndCssnanoLast\\", \\"plugins\\": Array [ @@ -568,7 +568,7 @@ exports[`webpack nuxt webpack module.rules 1`] = ` \\"test\\": /\\\\.(png|jpe?g|gif|svg|webp|avif)$/i, \\"use\\": Array [ Object { - \\"loader\\": \\"url-loader\\", + \\"loader\\": \\"/node_modules/url-loader/dist/cjs.js\\", \\"options\\": Object { \\"esModule\\": false, \\"limit\\": 1000, @@ -581,7 +581,7 @@ exports[`webpack nuxt webpack module.rules 1`] = ` \\"test\\": /\\\\.(woff2?|eot|ttf|otf)(\\\\?.*)?$/i, \\"use\\": Array [ Object { - \\"loader\\": \\"url-loader\\", + \\"loader\\": \\"/node_modules/url-loader/dist/cjs.js\\", \\"options\\": Object { \\"esModule\\": false, \\"limit\\": 1000, @@ -594,7 +594,7 @@ exports[`webpack nuxt webpack module.rules 1`] = ` \\"test\\": /\\\\.(webm|mp4|ogv)$/i, \\"use\\": Array [ Object { - \\"loader\\": \\"file-loader\\", + \\"loader\\": \\"/node_modules/file-loader/dist/cjs.js\\", \\"options\\": Object { \\"esModule\\": false, \\"name\\": \\"videos/[name].[contenthash:7].[ext]\\", @@ -608,7 +608,7 @@ exports[`webpack nuxt webpack module.rules 1`] = ` exports[`webpack nuxt webpack module.rules loader=.*-loader 1`] = ` "Object { - \\"loader\\": \\"vue-loader\\", + \\"loader\\": \\"/node_modules/vue-loader/lib/index.js\\", \\"options\\": Object { \\"productionMode\\": true, \\"transformAssetUrls\\": Object { @@ -625,7 +625,7 @@ exports[`webpack nuxt webpack module.rules loader=.*-loader 1`] = ` exports[`webpack nuxt webpack module.rules loader=vue- 1`] = ` "Object { - \\"loader\\": \\"vue-loader\\", + \\"loader\\": \\"/node_modules/vue-loader/lib/index.js\\", \\"options\\": Object { \\"productionMode\\": true, \\"transformAssetUrls\\": Object { diff --git a/packages/config/package.json b/packages/config/package.json index 7e409295d6..6c031e8e5d 100644 --- a/packages/config/package.json +++ b/packages/config/package.json @@ -17,8 +17,8 @@ "defu": "^3.2.2", "destr": "^1.0.1", "dotenv": "^8.2.0", - "esm": "^3.2.25", "jiti": "^0.1.17", + "lodash": "^4.17.20", "rc9": "^1.2.0", "std-env": "^2.2.1" }, diff --git a/packages/config/src/config/_common.js b/packages/config/src/config/_common.js index b0325c1d1d..1e26379360 100644 --- a/packages/config/src/config/_common.js +++ b/packages/config/src/config/_common.js @@ -1,4 +1,4 @@ -import capitalize from 'lodash/capitalize' +import { capitalize } from 'lodash' import env from 'std-env' import { TARGETS } from '@nuxt/utils' diff --git a/packages/config/src/load.js b/packages/config/src/load.js index 4abb1a9eb8..ac094b4b5a 100644 --- a/packages/config/src/load.js +++ b/packages/config/src/load.js @@ -4,28 +4,28 @@ import defu from 'defu' import consola from 'consola' import dotenv from 'dotenv' import { clearRequireCache, scanRequireTree } from '@nuxt/utils' -import esm from 'esm' +import jiti from 'jiti' import _createRequire from 'create-require' import destr from 'destr' import * as rc from 'rc9' import { defaultNuxtConfigFile } from './config' const isJest = typeof jest !== 'undefined' +const _require = isJest ? _createRequire(__filename) : jiti(__filename) export async function loadNuxtConfig ({ rootDir = '.', envConfig = {}, configFile = defaultNuxtConfigFile, configContext = {}, - configOverrides = {}, - createRequire = module => isJest ? _createRequire(module.filename) : esm(module, { cache: false }) + configOverrides = {} } = {}) { rootDir = path.resolve(rootDir) let options = {} try { - configFile = require.resolve(path.resolve(rootDir, configFile)) + configFile = _require.resolve(path.resolve(rootDir, configFile)) } catch (e) { if (e.code !== 'MODULE_NOT_FOUND') { throw (e) @@ -56,7 +56,6 @@ export async function loadNuxtConfig ({ if (configFile) { // Clear cache clearRequireCache(configFile) - const _require = createRequire(module) options = _require(configFile) || {} if (options.default) { options = options.default diff --git a/packages/config/src/options.js b/packages/config/src/options.js index 29424d084d..badbe0bde8 100644 --- a/packages/config/src/options.js +++ b/packages/config/src/options.js @@ -1,9 +1,7 @@ import path from 'path' import fs from 'fs' -import defaultsDeep from 'lodash/defaultsDeep' +import { defaultsDeep, pick, uniq } from 'lodash' import defu from 'defu' -import pick from 'lodash/pick' -import uniq from 'lodash/uniq' import consola from 'consola' import destr from 'destr' import { TARGETS, MODES, guardDir, isNonEmptyString, isPureObject, isUrl, getMainModule, urlJoin, getPKG } from '@nuxt/utils' @@ -465,20 +463,22 @@ export function getNuxtConfig (_options) { staticAssets.versionBase = urlJoin(staticAssets.base, staticAssets.version) } - // createRequire factory - if (options.createRequire === undefined) { - const isJest = typeof jest !== 'undefined' - options.createRequire = isJest ? false : 'esm' - } - if (options.createRequire === 'esm') { - const esm = require('esm') - options.createRequire = module => esm(module, { cache: false }) + // createRequire + const isJest = typeof jest !== 'undefined' + const defaultCreateRequire = isJest ? 'native' : 'jiti' + + options.createRequire = process.env.NUXT_CREATE_REQUIRE || options.createRequire || defaultCreateRequire + + if (options.createRequire === 'native') { + const createRequire = require('create-require') + options.createRequire = p => createRequire(typeof p === 'string' ? p : p.filename) } else if (options.createRequire === 'jiti') { const jiti = require('jiti') - options.createRequire = module => jiti(module.filename) + options.createRequire = p => jiti(typeof p === 'string' ? p : p.filename) } else if (typeof options.createRequire !== 'function') { - const createRequire = require('create-require') - options.createRequire = module => createRequire(module.filename) + throw new TypeError( + `Unsupported createRequire value ${options.createRequire}! Possible values: "native", "jiti", ` + ) } // Indicator diff --git a/packages/core/package.json b/packages/core/package.json index ca395b5573..7a245a631a 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -9,17 +9,14 @@ ], "dependencies": { "@nuxt/config": "2.14.12", - "@nuxt/devalue": "^1.2.4", "@nuxt/server": "2.14.12", "@nuxt/utils": "2.14.12", - "@nuxt/vue-renderer": "2.14.12", "consola": "^2.15.0", - "debug": "^4.2.0", - "esm": "^3.2.25", + "create-require": "^1.1.1", "fs-extra": "^9.0.1", "hable": "^3.0.0", "hash-sum": "^2.0.0", - "std-env": "^2.2.1" + "lodash": "^4.17.20" }, "publishConfig": { "access": "public" diff --git a/packages/core/src/module.js b/packages/core/src/module.js index 5b51cc31b2..206b0844df 100644 --- a/packages/core/src/module.js +++ b/packages/core/src/module.js @@ -121,11 +121,13 @@ export default class ModuleContainer { ) } - requireModule (moduleOpts) { - return this.addModule(moduleOpts) + requireModule (moduleOpts, { requirePath } = {}) { + return this.addModule(moduleOpts, undefined, { requirePath }) } - async addModule (moduleOpts) { + async addModule (moduleOpts, arg2, arg3) { + // Arg2 was previously used for requireOnce which is ignored now + const { requirePath } = { ...arg2, ...arg3 } let src let options let handler @@ -154,7 +156,7 @@ export default class ModuleContainer { // Resolve handler if (!handler) { try { - handler = this.nuxt.resolver.requireModule(src, { useESM: true }) + handler = this.nuxt.resolver.requireModule(src, { requirePath }) } catch (error) { if (error.code !== 'MODULE_NOT_FOUND') { throw error diff --git a/packages/core/src/nuxt.js b/packages/core/src/nuxt.js index 6e55a6d998..7016adcc32 100644 --- a/packages/core/src/nuxt.js +++ b/packages/core/src/nuxt.js @@ -1,5 +1,5 @@ -import isPlainObject from 'lodash/isPlainObject' +import { isPlainObject } from 'lodash' import consola from 'consola' import Hookable from 'hable' diff --git a/packages/core/src/resolver.js b/packages/core/src/resolver.js index 9e77b1fe62..c0ba676a66 100644 --- a/packages/core/src/resolver.js +++ b/packages/core/src/resolver.js @@ -1,6 +1,7 @@ import { resolve, join } from 'path' import fs from 'fs-extra' import consola from 'consola' +import createRequire from 'create-require' import { startsWithRootAlias, @@ -20,23 +21,20 @@ export default class Resolver { this.resolveModule = this.resolveModule.bind(this) this.requireModule = this.requireModule.bind(this) - const { createRequire } = this.options - this._require = createRequire ? createRequire(module) : module.require - - this._resolve = require.resolve + this._createRequire = this.options.createRequire || createRequire + this._require = this._createRequire(__filename) } - resolveModule (path) { + resolveModule (path, { requirePath } = {}) { try { - return this._resolve(path, { - paths: this.options.modulesDir + return this._require.resolve(path, { + paths: [ + requirePath || __dirname, + ...[].concat(this.options.modulesDir) + ] }) } catch (error) { if (error.code !== 'MODULE_NOT_FOUND') { - // TODO: remove after https://github.com/facebook/jest/pull/8487 released - if (process.env.NODE_ENV === 'test' && error.message.startsWith('Cannot resolve module')) { - return - } throw error } } @@ -54,7 +52,7 @@ export default class Resolver { return resolve(this.options.srcDir, path) } - resolvePath (path, { alias, isAlias = alias, module, isModule = module, isStyle } = {}) { + resolvePath (path, { alias, isAlias = alias, module, isModule = module, isStyle, requirePath } = {}) { // TODO: Remove in Nuxt 3 if (alias) { consola.warn('Using alias is deprecated and will be removed in Nuxt 3. Use `isAlias` instead.') @@ -72,7 +70,7 @@ export default class Resolver { // Try to resolve it as a regular module if (isModule !== false) { - resolvedPath = this.resolveModule(path) + resolvedPath = this.resolveModule(path, { requirePath }) } // Try to resolve alias @@ -119,7 +117,7 @@ export default class Resolver { throw new Error(`Cannot resolve "${path}" from "${resolvedPath}"`) } - requireModule (path, { esm, useESM = esm, alias, isAlias = alias, intropDefault, interopDefault = intropDefault } = {}) { + requireModule (path, { alias, isAlias = alias, intropDefault, interopDefault = intropDefault, requirePath } = {}) { let resolvedPath = path let requiredModule @@ -130,15 +128,12 @@ export default class Resolver { if (alias) { consola.warn('Using alias is deprecated and will be removed in Nuxt 3. Use `isAlias` instead.') } - if (esm) { - consola.warn('Using esm is deprecated and will be removed in Nuxt 3. Use `useESM` instead.') - } let lastError // Try to resolve path try { - resolvedPath = this.resolvePath(path, { isAlias }) + resolvedPath = this.resolvePath(path, { isAlias, requirePath }) } catch (e) { lastError = e } @@ -151,18 +146,10 @@ export default class Resolver { clearRequireCache(resolvedPath) } - // By default use esm only for js,mjs files outside of node_modules - if (useESM === undefined) { - useESM = !isExternal && /.(js|mjs)$/.test(resolvedPath) - } - // Try to require try { - if (useESM) { - requiredModule = this._require(resolvedPath) - } else { - requiredModule = require(resolvedPath) - } + const _require = requirePath ? this._createRequire(requirePath) : this._require + requiredModule = _require(resolvedPath) } catch (e) { lastError = e } diff --git a/packages/core/test/__modules__/__resolver__.js b/packages/core/test/__modules__/__resolver__.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/core/test/module.test.js b/packages/core/test/module.test.js index 90c2a0d029..290c0f95fc 100644 --- a/packages/core/test/module.test.js +++ b/packages/core/test/module.test.js @@ -318,7 +318,7 @@ describe('core: module', () => { module.requireModule(moduleOpts) expect(module.addModule).toBeCalledTimes(1) - expect(module.addModule).toBeCalledWith(moduleOpts) + expect(module.addModule).toBeCalledWith(moduleOpts, undefined, { requirePath: undefined }) }) test('should add string module', async () => { @@ -332,7 +332,7 @@ describe('core: module', () => { const result = await module.addModule('moduleTest') expect(requireModule).toBeCalledTimes(1) - expect(requireModule).toBeCalledWith('moduleTest', { useESM: true }) + expect(requireModule).toBeCalledWith('moduleTest', { requirePath: undefined }) expect(module.requiredModules).toEqual({ moduleTest: { handler: expect.any(Function), @@ -381,7 +381,7 @@ describe('core: module', () => { const result = await module.addModule(['moduleTest', { test: true }]) expect(requireModule).toBeCalledTimes(1) - expect(requireModule).toBeCalledWith('moduleTest', { useESM: true }) + expect(requireModule).toBeCalledWith('moduleTest', { requirePath: undefined }) expect(module.requiredModules).toEqual({ moduleTest: { handler: expect.any(Function), diff --git a/packages/core/test/resolver.test.js b/packages/core/test/resolver.test.js index 9f0b9ee60f..032e4b436c 100644 --- a/packages/core/test/resolver.test.js +++ b/packages/core/test/resolver.test.js @@ -5,13 +5,14 @@ import { startsWithRootAlias, startsWithSrcAlias } from '@nuxt/utils' import Resolver from '../src/resolver' -jest.mock('esm', () => jest.fn(() => jest.fn())) jest.mock('fs-extra') jest.mock('@nuxt/utils') jest.spyOn(path, 'join') jest.spyOn(path, 'resolve') +const modulesDir = path.resolve(__dirname, './__modules__') + describe.posix('core: resolver', () => { beforeEach(() => { jest.clearAllMocks() @@ -33,40 +34,22 @@ describe.posix('core: resolver', () => { test('should call require.resolve in resolveModule', () => { const resolver = new Resolver({ - options: { modulesDir: '/var/nuxt/node_modules' } + options: { modulesDir } }) - const resolve = resolver._resolve = jest.fn(() => '/var/nuxt/resolver/module') - const modulePath = resolver.resolveModule('/var/nuxt/resolver') + const modulePath = resolver.resolveModule('__resolver__') - expect(modulePath).toEqual('/var/nuxt/resolver/module') - expect(resolve).toBeCalledTimes(1) - expect(resolve).toBeCalledWith('/var/nuxt/resolver', { paths: '/var/nuxt/node_modules' }) + expect(modulePath).toEqual(path.resolve(modulesDir, './__resolver__.js')) }) test('should return undefined when module is not found', () => { const resolver = new Resolver({ - options: { modulesDir: '/var/nuxt/node_modules' } - }) - const resolve = resolver._resolve = jest.fn(() => { - const err = new Error() - err.code = 'MODULE_NOT_FOUND' - throw err + options: { modulesDir } }) - const modulePath = resolver.resolveModule('/var/nuxt/resolver') + const modulePath = resolver.resolveModule('non-exist-module') expect(modulePath).toBeUndefined() - expect(resolve).toBeCalledTimes(1) - }) - - test('should throw error when require.resolve failed', () => { - const resolver = new Resolver({ - options: { modulesDir: '/var/nuxt/node_modules' } - }) - resolver._resolve = jest.fn(() => { throw new Error('resolve failed') }) - - expect(() => resolver.resolveModule('/var/nuxt/resolver')).toThrow('resolve failed') }) test('should resolve root alias', () => { @@ -380,18 +363,6 @@ describe.posix('core: resolver', () => { expect(resolvedModule).toEqual({ default: 'resolved module' }) }) - test('should require common module', () => { - const resolver = new Resolver({ - options: {} - }) - resolver.resolvePath = jest.fn(() => 'path') - resolver._require = jest.fn(() => ({ default: 'resolved module' })) - - const resolvedModule = resolver.requireModule('path', { useESM: false }) - - expect(resolvedModule).toBe(path) - }) - test('should throw error if resolvePath failed', () => { const resolver = new Resolver({ options: {} @@ -444,11 +415,6 @@ describe.posix('core: resolver', () => { }) resolver.resolvePath = jest.fn().mockReturnValue('/var/nuxt/resolver/file.js') resolver._require = jest.fn() - - resolver.requireModule('/var/nuxt/resolver/file.js', { esm: true }) - const warnMsg = 'Using esm is deprecated and will be removed in Nuxt 3. Use `useESM` instead.' - expect(consola.warn).toBeCalledTimes(1) - expect(consola.warn).toBeCalledWith(warnMsg) }) }) }) diff --git a/packages/generator/package.json b/packages/generator/package.json index e87eb26d6f..a5d549b6f3 100644 --- a/packages/generator/package.json +++ b/packages/generator/package.json @@ -11,6 +11,7 @@ "@nuxt/utils": "2.14.12", "chalk": "^4.1.0", "consola": "^2.15.0", + "defu": "^3.2.2", "devalue": "^2.0.1", "fs-extra": "^9.0.1", "html-minifier": "^4.0.0", diff --git a/packages/generator/src/generator.js b/packages/generator/src/generator.js index eff7c1e161..f558fa417c 100644 --- a/packages/generator/src/generator.js +++ b/packages/generator/src/generator.js @@ -7,7 +7,7 @@ import defu from 'defu' import htmlMinifier from 'html-minifier' import { parse } from 'node-html-parser' -import { isFullStatic, flatRoutes, isString, isUrl, promisifyRoute, urlJoin, waitFor } from '@nuxt/utils' +import { isFullStatic, flatRoutes, isString, isUrl, promisifyRoute, urlJoin, waitFor, requireModule } from '@nuxt/utils' export default class Generator { constructor (nuxt, builder) { @@ -153,14 +153,14 @@ export default class Generator { getBuildConfig () { try { - return require(path.join(this.options.buildDir, 'nuxt/config.json')) + return requireModule(path.join(this.options.buildDir, 'nuxt/config.json')) } catch (err) { return null } } getAppRoutes () { - return require(path.join(this.options.buildDir, 'routes.json')) + return requireModule(path.join(this.options.buildDir, 'routes.json')) } async generateRoutes (routes) { @@ -382,8 +382,9 @@ export default class Generator { let fileName if (this.options.generate.subFolders) { - fileName = path.join(route, path.sep, 'index.html') // /about -> /about/index.html - fileName = fileName === '/404/index.html' ? '/404.html' : fileName // /404 -> /404.html + fileName = route === '/404' + ? path.join(path.sep, '404.html') // /404 -> /404.html + : path.join(route, path.sep, 'index.html') // /about -> /about/index.html } else { const normalizedRoute = route.replace(/\/$/, '') fileName = route.length > 1 ? path.join(path.sep, normalizedRoute + '.html') : path.join(path.sep, 'index.html') diff --git a/packages/generator/test/__utils__/index.js b/packages/generator/test/__utils__/index.js index 69d003f0a0..24768ec1eb 100644 --- a/packages/generator/test/__utils__/index.js +++ b/packages/generator/test/__utils__/index.js @@ -1,3 +1,10 @@ +import { resolve } from 'path' +import env from 'std-env' + +const isWin = env.windows + +const rootDir = isWin ? 'C:\\nuxt' : '/var/nuxt' + export const createNuxt = () => ({ ready: jest.fn(), callHook: jest.fn(), @@ -6,11 +13,11 @@ export const createNuxt = () => ({ }, options: { mode: 'universal', - srcDir: '/var/nuxt/src', - buildDir: '/var/nuxt/build', - generate: { dir: '/var/nuxt/generate' }, + srcDir: resolve(rootDir, 'src'), + buildDir: resolve(rootDir, 'build'), + generate: { dir: resolve(rootDir, 'generate') }, build: { publicPath: '__public' }, - dir: { static: '/var/nuxt/static' }, + dir: { static: resolve(rootDir, 'static') }, render: {} } }) diff --git a/packages/generator/test/generator.gen.test.js b/packages/generator/test/generator.gen.test.js index 849adf3d73..46c2bd2771 100644 --- a/packages/generator/test/generator.gen.test.js +++ b/packages/generator/test/generator.gen.test.js @@ -1,4 +1,3 @@ -import path from 'path' import chalk from 'chalk' import consola from 'consola' import fsExtra from 'fs-extra' @@ -7,7 +6,6 @@ import { waitFor } from '@nuxt/utils' import Generator from '../src/generator' import { createNuxt } from './__utils__' -jest.mock('path') jest.mock('chalk', () => ({ red: jest.fn(str => `red:${str}`), yellow: jest.fn(str => `yellow:${str}`), @@ -17,17 +15,6 @@ jest.mock('fs-extra') jest.mock('@nuxt/utils') describe('generator: generate routes', () => { - const sep = path.sep - - beforeAll(() => { - path.sep = '[sep]' - path.join.mockImplementation((...args) => `join(${args.join(', ')})`) - }) - - afterAll(() => { - path.sep = sep - }) - beforeEach(() => { jest.clearAllMocks() }) @@ -147,25 +134,28 @@ grey:bar failed`) const nuxt = createNuxt() nuxt.options.generate.fallback = 'fallback.html' const generator = new Generator(nuxt) - path.join.mockClear() fsExtra.exists.mockReturnValueOnce(false) await generator.afterGenerate() - expect(path.join).toBeCalledTimes(1) - expect(path.join).toBeCalledWith(generator.distPath, 'fallback.html') expect(fsExtra.exists).toBeCalledTimes(1) - expect(fsExtra.exists).toBeCalledWith(`join(${generator.distPath}, fallback.html)`) + expect(fsExtra.exists.mock.calls[0][0]).toBePath( + '/var/nuxt/generate/fallback.html', + 'C:\\nuxt\\generate\\fallback.html' + ) expect(nuxt.server.renderRoute).toBeCalledTimes(1) expect(nuxt.server.renderRoute).toBeCalledWith('/', { spa: true }) expect(fsExtra.writeFile).toBeCalledTimes(1) - expect(fsExtra.writeFile).toBeCalledWith(`join(${generator.distPath}, fallback.html)`, 'rendered html', 'utf8') + expect(fsExtra.writeFile).toBeCalledWith(expect.any(String), 'rendered html', 'utf8') + expect(fsExtra.writeFile.mock.calls[0][0]).toBePath( + '/var/nuxt/generate/fallback.html', + 'C:\\nuxt\\generate\\fallback.html' + ) }) test('should disable writing fallback if fallback is empty or not string', async () => { const nuxt = createNuxt() const generator = new Generator(nuxt) - path.join.mockClear() nuxt.options.generate.fallback = '' await generator.afterGenerate() @@ -173,7 +163,6 @@ grey:bar failed`) nuxt.options.generate.fallback = jest.fn() await generator.afterGenerate() - expect(path.join).not.toBeCalled() expect(fsExtra.exists).not.toBeCalled() expect(nuxt.server.renderRoute).not.toBeCalled() expect(fsExtra.writeFile).not.toBeCalled() @@ -183,15 +172,15 @@ grey:bar failed`) const nuxt = createNuxt() nuxt.options.generate.fallback = 'fallback.html' const generator = new Generator(nuxt) - path.join.mockClear() fsExtra.exists.mockReturnValueOnce(true) await generator.afterGenerate() - expect(path.join).toBeCalledTimes(1) - expect(path.join).toBeCalledWith(generator.distPath, 'fallback.html') expect(fsExtra.exists).toBeCalledTimes(1) - expect(fsExtra.exists).toBeCalledWith(`join(${generator.distPath}, fallback.html)`) + expect(fsExtra.exists.mock.calls[0][0]).toBePath( + '/var/nuxt/generate/fallback.html', + 'C:\\nuxt\\generate\\fallback.html' + ) expect(nuxt.server.renderRoute).not.toBeCalled() expect(fsExtra.writeFile).not.toBeCalled() }) @@ -199,7 +188,6 @@ grey:bar failed`) test('should disable writing fallback if fallback is empty or not string', async () => { const nuxt = createNuxt() const generator = new Generator(nuxt) - path.join.mockClear() nuxt.options.generate.fallback = '' await generator.afterGenerate() @@ -207,7 +195,6 @@ grey:bar failed`) nuxt.options.generate.fallback = jest.fn() await generator.afterGenerate() - expect(path.join).not.toBeCalled() expect(fsExtra.exists).not.toBeCalled() expect(fsExtra.writeFile).not.toBeCalled() }) diff --git a/packages/generator/test/generator.init.test.js b/packages/generator/test/generator.init.test.js index 1cf042f9fe..4f130e74e1 100644 --- a/packages/generator/test/generator.init.test.js +++ b/packages/generator/test/generator.init.test.js @@ -1,4 +1,3 @@ -import path from 'path' import consola from 'consola' import fsExtra from 'fs-extra' import { flatRoutes, isString, isUrl, promisifyRoute } from '@nuxt/utils' @@ -6,15 +5,12 @@ import { flatRoutes, isString, isUrl, promisifyRoute } from '@nuxt/utils' import Generator from '../src/generator' import { createNuxt, hookCalls } from './__utils__' -jest.mock('path') jest.mock('fs-extra') jest.mock('@nuxt/utils') describe('generator: initialize', () => { beforeAll(() => { isString.mockImplementation(str => typeof str === 'string') - path.join.mockImplementation((...args) => `join(${args.join(', ')})`) - path.resolve.mockImplementation((...args) => `resolve(${args.join(', ')})`) }) beforeEach(() => { @@ -34,10 +30,22 @@ describe('generator: initialize', () => { expect(generator.nuxt).toBe(nuxt) expect(generator.options).toBe(nuxt.options) expect(generator.builder).toBe(builder) - expect(generator.staticRoutes).toEqual('resolve(/var/nuxt/src, /var/nuxt/static)') - expect(generator.srcBuiltPath).toBe('resolve(/var/nuxt/build, dist, client)') - expect(generator.distPath).toBe('/var/nuxt/generate') - expect(generator.distNuxtPath).toBe('join(/var/nuxt/generate, )') + expect(generator.staticRoutes).toBePath( + '/var/nuxt/static', + 'C:\\nuxt\\static' + ) + expect(generator.srcBuiltPath).toBePath( + '/var/nuxt/build/dist/client', + 'C:\\nuxt\\build\\dist\\client' + ) + expect(generator.distPath).toBePath( + '/var/nuxt/generate', + 'C:\\nuxt\\generate' + ) + expect(generator.distNuxtPath).toBePath( + '/var/nuxt/generate', + 'C:\\nuxt\\generate' + ) }) test('should append publicPath to distPath if publicPath is not url', () => { @@ -50,7 +58,10 @@ describe('generator: initialize', () => { const builder = jest.fn() const generator = new Generator(nuxt, builder) - expect(generator.distNuxtPath).toBe('join(/var/nuxt/generate, __public)') + expect(generator.distNuxtPath).toBePath( + '/var/nuxt/generate/__public', + 'C:\\nuxt\\generate\\__public' + ) }) test('should initiate with build and init by default', async () => { @@ -172,8 +183,6 @@ describe('generator: initialize', () => { const nuxt = createNuxt() nuxt.options.generate.fallback = 'fallback.html' const generator = new Generator(nuxt) - path.join.mockClear() - path.resolve.mockClear() fsExtra.exists.mockReturnValueOnce(false) await generator.initDist() @@ -185,10 +194,11 @@ describe('generator: initialize', () => { expect(fsExtra.exists).toBeCalledWith(generator.staticRoutes) expect(fsExtra.copy).toBeCalledTimes(1) expect(fsExtra.copy).toBeCalledWith(generator.srcBuiltPath, generator.distNuxtPath) - expect(path.resolve).toBeCalledTimes(1) - expect(path.resolve).toBeCalledWith(generator.distPath, '.nojekyll') expect(fsExtra.writeFile).toBeCalledTimes(1) - expect(fsExtra.writeFile).toBeCalledWith(`resolve(${generator.distPath}, .nojekyll)`, '') + expect(fsExtra.writeFile.mock.calls[0][0]).toBePath( + '/var/nuxt/generate/.nojekyll', + 'C:\\nuxt\\generate\\.nojekyll' + ) expect(hookCalls(nuxt, 'generate:distCopied')[0][0]).toMatchObject(generator) }) diff --git a/packages/generator/test/generator.route.test.js b/packages/generator/test/generator.route.test.js index 5c8512741d..67e59049d7 100644 --- a/packages/generator/test/generator.route.test.js +++ b/packages/generator/test/generator.route.test.js @@ -1,4 +1,3 @@ -import path from 'path' import consola from 'consola' import fsExtra from 'fs-extra' import htmlMinifier from 'html-minifier' @@ -6,24 +5,11 @@ import htmlMinifier from 'html-minifier' import Generator from '../src/generator' import { createNuxt, hookCalls } from './__utils__' -jest.mock('path') jest.mock('fs-extra') jest.mock('html-minifier') jest.mock('@nuxt/utils') describe('generator: generate route', () => { - const sep = path.sep - - beforeAll(() => { - path.sep = '[sep]' - path.join.mockImplementation((...args) => `join(${args.join(', ')})`) - path.dirname.mockImplementation((...args) => `dirname(${args.join(', ')})`) - }) - - afterAll(() => { - path.sep = sep - }) - beforeEach(() => { jest.clearAllMocks() }) @@ -34,7 +20,6 @@ describe('generator: generate route', () => { nuxt.options.generate.minify = undefined nuxt.options.generate.subFolders = false const generator = new Generator(nuxt) - path.join.mockClear() const route = '/foo/' const payload = {} @@ -44,26 +29,38 @@ describe('generator: generate route', () => { expect(nuxt.server.renderRoute).toBeCalledTimes(1) expect(nuxt.server.renderRoute).toBeCalledWith(route, { payload }) - expect(path.join).toBeCalledTimes(2) - expect(path.join).nthCalledWith(1, '[sep]', '/foo.html') - expect(path.join).nthCalledWith(2, generator.distPath, 'join([sep], /foo.html)') - expect(hookCalls(nuxt, 'generate:page')[0][0]).toMatchObject({ + const genernatePageHookCall = hookCalls(nuxt, 'generate:page')[0][0] + expect(genernatePageHookCall).toMatchObject({ route, - html: 'rendered html', - path: `join(${generator.distPath}, join([sep], /foo.html))` + html: 'rendered html' }) + expect(genernatePageHookCall.path).toBePath( + '/var/nuxt/generate/foo.html', + 'C:\\nuxt\\generate\\foo.html' + ) - expect(hookCalls(nuxt, 'generate:routeCreated')[0][0]).toMatchObject({ + const genernateRouteCreatedHookCall = hookCalls(nuxt, 'generate:routeCreated')[0][0] + expect(genernateRouteCreatedHookCall).toMatchObject({ route, - errors: [], - path: `join(${generator.distPath}, join([sep], /foo.html))` + errors: [] }) + expect(genernateRouteCreatedHookCall.path).toBePath( + '/var/nuxt/generate/foo.html', + 'C:\\nuxt\\generate\\foo.html' + ) expect(fsExtra.mkdirp).toBeCalledTimes(1) - expect(fsExtra.mkdirp).toBeCalledWith(`dirname(join(${generator.distPath}, join([sep], /foo.html)))`) + expect(fsExtra.mkdirp.mock.calls[0][0]).toBePath( + '/var/nuxt/generate', + 'C:\\nuxt\\generate' + ) expect(fsExtra.writeFile).toBeCalledTimes(1) - expect(fsExtra.writeFile).toBeCalledWith(`join(${generator.distPath}, join([sep], /foo.html))`, 'rendered html', 'utf8') + expect(fsExtra.writeFile).toBeCalledWith(expect.any(String), 'rendered html', 'utf8') + expect(fsExtra.writeFile.mock.calls[0][0]).toBePath( + '/var/nuxt/generate/foo.html', + 'C:\\nuxt\\generate\\foo.html' + ) expect(returned).toEqual(true) }) @@ -158,7 +155,11 @@ describe('generator: generate route', () => { expect(htmlMinifier.minify).toBeCalledTimes(1) expect(htmlMinifier.minify).toBeCalledWith('rendered html', { value: 'test-minify' }) expect(fsExtra.writeFile).toBeCalledTimes(1) - expect(fsExtra.writeFile).toBeCalledWith(`join(${generator.distPath}, join([sep], /foo.html))`, 'minified rendered html', 'utf8') + expect(fsExtra.writeFile).toBeCalledWith(expect.any(String), 'minified rendered html', 'utf8') + expect(fsExtra.writeFile.mock.calls[0][0]).toBePath( + '/var/nuxt/generate/foo.html', + 'C:\\nuxt\\generate\\foo.html' + ) expect(returned).toEqual(true) }) @@ -191,17 +192,17 @@ describe('generator: generate route', () => { nuxt.options.build.html = { minify: false } nuxt.options.generate.subFolders = true const generator = new Generator(nuxt) - path.join.mockClear() const route = '/foo' const returned = await generator.generateRoute({ route }) - expect(path.join).toBeCalledTimes(2) - expect(path.join).nthCalledWith(1, route, '[sep]', 'index.html') - expect(path.join).nthCalledWith(2, generator.distPath, 'join(/foo, [sep], index.html)') expect(fsExtra.writeFile).toBeCalledTimes(1) - expect(fsExtra.writeFile).toBeCalledWith(`join(${generator.distPath}, join(/foo, [sep], index.html))`, 'rendered html', 'utf8') + expect(fsExtra.writeFile).toBeCalledWith(expect.any(String), 'rendered html', 'utf8') + expect(fsExtra.writeFile.mock.calls[0][0]).toBePath( + '/var/nuxt/generate/foo/index.html', + 'C:\\nuxt\\generate\\foo\\index.html' + ) expect(returned).toEqual(true) }) @@ -210,18 +211,17 @@ describe('generator: generate route', () => { nuxt.options.build.html = { minify: false } nuxt.options.generate.subFolders = true const generator = new Generator(nuxt) - path.join.mockClear() - path.join.mockReturnValueOnce('/404/index.html') const route = '/404' const returned = await generator.generateRoute({ route }) - expect(path.join).toBeCalledTimes(2) - expect(path.join).nthCalledWith(1, route, '[sep]', 'index.html') - expect(path.join).nthCalledWith(2, generator.distPath, '/404.html') expect(fsExtra.writeFile).toBeCalledTimes(1) - expect(fsExtra.writeFile).toBeCalledWith(`join(${generator.distPath}, /404.html)`, 'rendered html', 'utf8') + expect(fsExtra.writeFile).toBeCalledWith(expect.any(String), 'rendered html', 'utf8') + expect(fsExtra.writeFile.mock.calls[0][0]).toBePath( + '/var/nuxt/generate/404.html', + 'C:\\nuxt\\generate\\404.html' + ) expect(returned).toEqual(true) }) @@ -229,17 +229,17 @@ describe('generator: generate route', () => { const nuxt = createNuxt() nuxt.options.build.html = { minify: false } const generator = new Generator(nuxt) - path.join.mockClear() const route = '' const returned = await generator.generateRoute({ route }) - expect(path.join).toBeCalledTimes(2) - expect(path.join).nthCalledWith(1, '[sep]', 'index.html') - expect(path.join).nthCalledWith(2, generator.distPath, 'join([sep], index.html)') expect(fsExtra.writeFile).toBeCalledTimes(1) - expect(fsExtra.writeFile).toBeCalledWith(`join(${generator.distPath}, join([sep], index.html))`, 'rendered html', 'utf8') + expect(fsExtra.writeFile).toBeCalledWith(expect.any(String), 'rendered html', 'utf8') + expect(fsExtra.writeFile.mock.calls[0][0]).toBePath( + '/var/nuxt/generate/index.html', + 'C:\\nuxt\\generate\\index.html' + ) expect(returned).toEqual(true) }) }) diff --git a/packages/server/package.js b/packages/server/package.js index 2f78f772ee..ef48328e7b 100644 --- a/packages/server/package.js +++ b/packages/server/package.js @@ -1,6 +1,4 @@ export default { build: true, - rollup: { - externals: ['jsdom'] - } + externals: ['jsdom'] } diff --git a/packages/server/package.json b/packages/server/package.json index a8c59ad242..558291ff00 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -8,11 +8,9 @@ "dist" ], "dependencies": { - "@nuxt/config": "2.14.12", "@nuxt/utils": "2.14.12", "@nuxt/vue-renderer": "2.14.12", "@nuxtjs/youch": "^4.2.3", - "chalk": "^4.1.0", "compression": "^1.7.4", "connect": "^3.7.0", "consola": "^2.15.0", diff --git a/packages/server/src/server.js b/packages/server/src/server.js index f93260acaf..99be2a6805 100644 --- a/packages/server/src/server.js +++ b/packages/server/src/server.js @@ -4,8 +4,9 @@ import launchMiddleware from 'launch-editor-middleware' import serveStatic from 'serve-static' import servePlaceholder from 'serve-placeholder' import connect from 'connect' +import compression from 'compression' import { determineGlobals, isUrl, urlJoin } from '@nuxt/utils' - +import { VueRenderer } from '@nuxt/vue-renderer' import ServerContext from './context' import renderAndGetWindow from './jsdom' import nuxtMiddleware from './middleware/nuxt' @@ -53,8 +54,6 @@ export default class Server { await this.nuxt.callHook('render:before', this, this.options.render) // Initialize vue-renderer - const { VueRenderer } = await import('@nuxt/vue-renderer') - this.serverContext = new ServerContext(this) this.renderer = new VueRenderer(this.serverContext) await this.renderer.ready() @@ -77,7 +76,6 @@ export default class Server { const { compressor } = this.options.render if (typeof compressor === 'object') { // If only setting for `compression` are provided, require the module and insert - const compression = this.nuxt.resolver.requireModule('compression') this.useMiddleware(compression(compressor)) } else if (compressor) { // Else, require own compression middleware if compressor is actually truthy diff --git a/packages/server/test/server.test.js b/packages/server/test/server.test.js index e7403449d4..184fb160e2 100644 --- a/packages/server/test/server.test.js +++ b/packages/server/test/server.test.js @@ -1,4 +1,5 @@ import path from 'path' +import compression from 'compression' import connect from 'connect' import consola from 'consola' import serveStatic from 'serve-static' @@ -15,6 +16,7 @@ import nuxtMiddleware from '../src/middleware/nuxt' import errorMiddleware from '../src/middleware/error' import createTimingMiddleware from '../src/middleware/timing' +jest.mock('compression') jest.mock('connect') jest.mock('serve-static') jest.mock('serve-placeholder') @@ -229,12 +231,11 @@ describe('server: server', () => { server.useMiddleware.mockClear() nuxt.options.render.compressor = { id: 'test-render-compressor' } - nuxt.resolver.requireModule.mockImplementationOnce(name => jest.fn(options => ({ - name, + compression.mockImplementationOnce(options => ({ + name: 'compression', ...options - }))) + })) await server.setupMiddleware() - expect(nuxt.resolver.requireModule).nthCalledWith(1, 'compression') expect(server.useMiddleware).nthCalledWith(1, { id: 'test-render-compressor', name: 'compression' diff --git a/packages/utils/package.json b/packages/utils/package.json index d51df74a6c..1e7f1146f1 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -10,8 +10,10 @@ "dependencies": { "@nuxt/ufo": "^0.5.2", "consola": "^2.15.0", + "create-require": "^1.1.1", "fs-extra": "^9.0.1", "hash-sum": "^2.0.0", + "lodash": "^4.17.20", "proper-lockfile": "^4.1.1", "semver": "^7.3.4", "serialize-javascript": "^5.0.1", diff --git a/packages/utils/src/cjs.js b/packages/utils/src/cjs.js index 84f9f384f2..beb45c5b31 100644 --- a/packages/utils/src/cjs.js +++ b/packages/utils/src/cjs.js @@ -1,4 +1,7 @@ import { join } from 'path' +import createRequire from 'create-require' + +const _require = createRequire() export function isHMRCompatible (id) { return !/[/\\]mongoose[/\\/]/.test(id) @@ -16,7 +19,7 @@ export function clearRequireCache (id) { const entry = getRequireCacheItem(id) if (!entry) { - delete require.cache[id] + delete _require.cache[id] return } @@ -25,7 +28,7 @@ export function clearRequireCache (id) { } // Needs to be cleared before children, to protect against circular deps (#7966) - delete require.cache[id] + delete _require.cache[id] for (const child of entry.children) { clearRequireCache(child.id) @@ -55,18 +58,34 @@ export function scanRequireTree (id, files = new Set()) { export function getRequireCacheItem (id) { try { - return require.cache[id] + return _require.cache[id] } catch (e) { } } -export function tryRequire (id) { - try { - return require(id) - } catch (e) { +export function resolveModule (id, paths) { + if (typeof paths === 'string') { + paths = [paths] } + return _require.resolve(id, [ + process.cwd(), + ...(paths || []), + ...(global.__NUXT_PATHS__ || []) + ]) } -export function getPKG (id) { - return tryRequire(join(id, 'package.json')) +export function requireModule (id, paths) { + return _require(resolveModule(id, paths)) +} + +export function tryRequire (id, paths) { + try { return requireModule(id, paths) } catch (e) { } +} + +export function tryResolve (id, paths) { + try { return resolveModule(id, paths) } catch (e) { } +} + +export function getPKG (id, paths) { + return tryRequire(join(id, 'package.json'), paths) } diff --git a/packages/utils/src/modern.js b/packages/utils/src/modern.js index a080a5d7d6..4ac4e0d58f 100644 --- a/packages/utils/src/modern.js +++ b/packages/utils/src/modern.js @@ -1,4 +1,5 @@ import UAParser from 'ua-parser-js' +import semver from 'semver' export const ModernBrowsers = { Edge: '16', @@ -14,7 +15,6 @@ export const ModernBrowsers = { 'Mobile Safari': '10.3' } -let semver let __modernBrowsers const getModernBrowsers = () => { @@ -34,9 +34,6 @@ export const isModernBrowser = (ua) => { if (!ua) { return false } - if (!semver) { - semver = require('semver') - } const { browser } = UAParser(ua) const browserVersion = semver.coerce(browser.version) if (!browserVersion) { diff --git a/packages/utils/src/resolve.js b/packages/utils/src/resolve.js index 4a54c6cfba..22e7593bc5 100644 --- a/packages/utils/src/resolve.js +++ b/packages/utils/src/resolve.js @@ -1,6 +1,6 @@ import path from 'path' import consola from 'consola' -import escapeRegExp from 'lodash/escapeRegExp' +import { escapeRegExp } from 'lodash' export const startsWithAlias = aliasArray => str => aliasArray.some(c => str.startsWith(c)) @@ -105,5 +105,5 @@ export function isIndexFileAndFolder (pluginFiles) { } export const getMainModule = () => { - return require.main || (module && module.main) || module + return (require && require.main) || (module && module.main) || module } diff --git a/packages/utils/src/route.js b/packages/utils/src/route.js index 91371f2976..91eb29640f 100644 --- a/packages/utils/src/route.js +++ b/packages/utils/src/route.js @@ -1,5 +1,5 @@ import path from 'path' -import get from 'lodash/get' +import { get } from 'lodash' import consola from 'consola' import { normalizeURL, withTrailingSlash, withoutTrailingSlash } from '@nuxt/ufo' import { r } from './resolve' diff --git a/packages/vue-app/package.js b/packages/vue-app/package.js index 19d0ef6e2d..96cdfb3009 100644 --- a/packages/vue-app/package.js +++ b/packages/vue-app/package.js @@ -1,3 +1,16 @@ export default { - build: true + build: true, + ignoreUnused: [ + // used in vue-app + '@nuxt/ufo', + 'node-fetch', + 'unfetch', + 'vue', + 'vue-client-only', + 'vue-meta', + 'vue-no-ssr', + 'vue-router', + 'vue-template-compiler', + 'vuex' + ] } diff --git a/packages/vue-renderer/package.json b/packages/vue-renderer/package.json index 2422ffb52c..3aabeb0ed6 100644 --- a/packages/vue-renderer/package.json +++ b/packages/vue-renderer/package.json @@ -13,6 +13,7 @@ "@nuxt/utils": "2.14.12", "consola": "^2.15.0", "fs-extra": "^9.0.1", + "lodash": "^4.17.20", "lru-cache": "^5.1.1", "vue": "^2.6.12", "vue-meta": "^2.4.0", diff --git a/packages/vue-renderer/src/renderer.js b/packages/vue-renderer/src/renderer.js index 564c21e618..b6ffb8f091 100644 --- a/packages/vue-renderer/src/renderer.js +++ b/packages/vue-renderer/src/renderer.js @@ -1,7 +1,7 @@ import path from 'path' import fs from 'fs-extra' import consola from 'consola' -import template from 'lodash/template' +import { template } from 'lodash' import { TARGETS, isModernRequest, waitFor } from '@nuxt/utils' import { normalizeURL } from '@nuxt/ufo' diff --git a/packages/vue-renderer/src/renderers/spa.js b/packages/vue-renderer/src/renderers/spa.js index 5b689642b9..196e837491 100644 --- a/packages/vue-renderer/src/renderers/spa.js +++ b/packages/vue-renderer/src/renderers/spa.js @@ -1,5 +1,5 @@ import { extname } from 'path' -import cloneDeep from 'lodash/cloneDeep' +import { cloneDeep } from 'lodash' import VueMeta from 'vue-meta' import { createRenderer } from 'vue-server-renderer' import LRU from 'lru-cache' diff --git a/packages/webpack/package.js b/packages/webpack/package.js index 19d0ef6e2d..3699342f92 100644 --- a/packages/webpack/package.js +++ b/packages/webpack/package.js @@ -1,3 +1,18 @@ export default { - build: true + build: true, + ignoreUnused: [ + '@nuxt/babel-preset-app', + 'babel-loader', + 'cache-loader', + 'caniuse-lite', + 'css-loader', + 'cssnano', + 'eventsource-polyfill', + 'file-loader', + 'postcss-loader', + 'postcss-preset-env', + 'postcss-url', + 'style-resources-loader', + 'url-loader' + ] } diff --git a/packages/webpack/package.json b/packages/webpack/package.json index 9185842513..6f8baf569e 100644 --- a/packages/webpack/package.json +++ b/packages/webpack/package.json @@ -8,16 +8,13 @@ "dist" ], "dependencies": { - "@babel/core": "^7.12.10", "@nuxt/babel-preset-app": "2.14.12", "@nuxt/friendly-errors-webpack-plugin": "^2.5.0", "@nuxt/utils": "2.14.12", "babel-loader": "^8.2.2", "cache-loader": "^4.1.0", - "caniuse-lite": "^1.0.30001170", - "chalk": "^4.1.0", + "caniuse-lite": "^1.0.30001168", "consola": "^2.15.0", - "create-require": "^1.1.1", "css-loader": "^4.3.0", "cssnano": "^4.1.10", "eventsource-polyfill": "^0.9.6", @@ -27,9 +24,11 @@ "hard-source-webpack-plugin": "^0.13.1", "hash-sum": "^2.0.0", "html-webpack-plugin": "^4.5.0", + "lodash": "^4.17.20", "memory-fs": "^0.5.0", "optimize-css-assets-webpack-plugin": "^5.0.4", "pify": "^5.0.0", + "pnp-webpack-plugin": "^1.6.4", "postcss": "^7.0.32", "postcss-import": "^12.0.1", "postcss-import-resolver": "^2.0.0", diff --git a/packages/webpack/src/builder.js b/packages/webpack/src/builder.js index c588b7d80a..c890a22063 100644 --- a/packages/webpack/src/builder.js +++ b/packages/webpack/src/builder.js @@ -6,7 +6,7 @@ import webpackDevMiddleware from 'webpack-dev-middleware' import webpackHotMiddleware from 'webpack-hot-middleware' import consola from 'consola' -import { TARGETS, parallel, sequence, wrapArray, isModernRequest } from '@nuxt/utils' +import { TARGETS, parallel, sequence, tryResolve, wrapArray, isModernRequest } from '@nuxt/utils' import AsyncMFS from './utils/async-mfs' import * as WebpackConfigs from './config' @@ -92,7 +92,7 @@ export class WebpackBundler { // Warm up perfLoader before build if (options.build.parallel) { consola.info('Warming up worker pools') - PerfLoader.warmupAll({ dev: options.dev }) + PerfLoader.warmupAll({ dev: options.dev, resolveModule: id => tryResolve(id, __filename) || id }) consola.success('Worker pools ready') } diff --git a/packages/webpack/src/config/base.js b/packages/webpack/src/config/base.js index fa55d958bd..6f2a697164 100644 --- a/packages/webpack/src/config/base.js +++ b/packages/webpack/src/config/base.js @@ -1,19 +1,18 @@ import path from 'path' import consola from 'consola' import TimeFixPlugin from 'time-fix-plugin' -import cloneDeep from 'lodash/cloneDeep' -import escapeRegExp from 'lodash/escapeRegExp' +import { escapeRegExp, cloneDeep } from 'lodash' import VueLoader from 'vue-loader' import ExtractCssChunksPlugin from 'extract-css-chunks-webpack-plugin' +import * as PnpWebpackPlugin from 'pnp-webpack-plugin' import HardSourcePlugin from 'hard-source-webpack-plugin' import TerserWebpackPlugin from 'terser-webpack-plugin' import WebpackBar from 'webpackbar' import env from 'std-env' import semver from 'semver' -import { TARGETS, isUrl, urlJoin, getPKG } from '@nuxt/utils' +import { TARGETS, isUrl, urlJoin, getPKG, tryResolve, requireModule } from '@nuxt/utils' -import createRequire from 'create-require' import PerfLoader from '../utils/perf-loader' import StyleLoader from '../utils/style-loader' import WarningIgnorePlugin from '../plugins/warning-ignore' @@ -23,6 +22,7 @@ export default class WebpackBaseConfig { constructor (builder) { this.builder = builder this.buildContext = builder.buildContext + this.resolveModule = id => tryResolve(id) || id } get colors () { @@ -121,7 +121,7 @@ export default class WebpackBaseConfig { let corejsVersion = corejs if (corejsVersion === 'auto') { try { - corejsVersion = Number.parseInt(createRequire(rootDir)('core-js/package.json').version.split('.')[0]) + corejsVersion = Number.parseInt(requireModule('core-js/package.json', rootDir).version.split('.')[0]) } catch (_err) { corejsVersion = 2 } @@ -134,7 +134,7 @@ export default class WebpackBaseConfig { corejsVersion = 2 } - const defaultPreset = [require.resolve('@nuxt/babel-preset-app'), { + const defaultPreset = [this.resolveModule('@nuxt/babel-preset-app'), { corejs: { version: corejsVersion } @@ -229,12 +229,20 @@ export default class WebpackBaseConfig { resolve: { extensions: ['.wasm', '.mjs', '.js', '.json', '.vue', '.jsx'], alias: this.alias(), - modules: webpackModulesDir + modules: webpackModulesDir, + plugins: [ + PnpWebpackPlugin, + PnpWebpackPlugin.moduleLoader(this.buildContext.options.rootDir), + PnpWebpackPlugin.moduleLoader(__dirname) + ] }, resolveLoader: { modules: [ path.resolve(__dirname, '../node_modules'), ...webpackModulesDir + ], + plugins: [ + PnpWebpackPlugin.moduleLoader(module) ] } } @@ -271,26 +279,26 @@ export default class WebpackBaseConfig { alias () { return { ...this.buildContext.options.alias, - 'vue-meta': require.resolve(`vue-meta${this.isServer ? '' : '/dist/vue-meta.esm.browser.js'}`) + 'vue-meta': this.resolveModule(`vue-meta${this.isServer ? '' : '/dist/vue-meta.esm.browser.js'}`) } } rules () { - const perfLoader = new PerfLoader(this.name, this.buildContext) + const perfLoader = new PerfLoader(this.name, this.buildContext, { resolveModule: this.resolveModule }) const styleLoader = new StyleLoader( this.buildContext, - { isServer: this.isServer, perfLoader } + { isServer: this.isServer, perfLoader, resolveModule: this.resolveModule } ) const babelLoader = { - loader: require.resolve('babel-loader'), + loader: this.resolveModule('babel-loader'), options: this.getBabelOptions() } return [ { test: /\.vue$/i, - loader: 'vue-loader', + loader: this.resolveModule('vue-loader'), options: this.loaders.vue }, { @@ -299,15 +307,15 @@ export default class WebpackBaseConfig { { resourceQuery: /^\?vue/i, use: [{ - loader: 'pug-plain-loader', + loader: this.resolveModule('pug-plain-loader'), options: this.loaders.pugPlain }] }, { use: [ - 'raw-loader', + this.resolveModule('raw-loader'), { - loader: 'pug-plain-loader', + loader: this.resolveModule('pug-plain-loader'), options: this.loaders.pugPlain } ] @@ -340,35 +348,35 @@ export default class WebpackBaseConfig { { test: /\.less$/i, oneOf: styleLoader.apply('less', { - loader: 'less-loader', + loader: this.resolveModule('less-loader'), options: this.loaders.less }) }, { test: /\.sass$/i, oneOf: styleLoader.apply('sass', { - loader: 'sass-loader', + loader: this.resolveModule('sass-loader'), options: this.loaders.sass }) }, { test: /\.scss$/i, oneOf: styleLoader.apply('scss', { - loader: 'sass-loader', + loader: this.resolveModule('sass-loader'), options: this.loaders.scss }) }, { test: /\.styl(us)?$/i, oneOf: styleLoader.apply('stylus', { - loader: 'stylus-loader', + loader: this.resolveModule('stylus-loader'), options: this.loaders.stylus }) }, { test: /\.(png|jpe?g|gif|svg|webp|avif)$/i, use: [{ - loader: 'url-loader', + loader: this.resolveModule('url-loader'), options: Object.assign( this.loaders.imgUrl, { name: this.getFileName('img') } @@ -378,7 +386,7 @@ export default class WebpackBaseConfig { { test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/i, use: [{ - loader: 'url-loader', + loader: this.resolveModule('url-loader'), options: Object.assign( this.loaders.fontUrl, { name: this.getFileName('font') } @@ -388,7 +396,7 @@ export default class WebpackBaseConfig { { test: /\.(webm|mp4|ogv)$/i, use: [{ - loader: 'file-loader', + loader: this.resolveModule('file-loader'), options: Object.assign( this.loaders.file, { name: this.getFileName('video') } diff --git a/packages/webpack/src/config/client.js b/packages/webpack/src/config/client.js index 26e3256e0d..5f1f0f0116 100644 --- a/packages/webpack/src/config/client.js +++ b/packages/webpack/src/config/client.js @@ -199,9 +199,9 @@ export default class WebpackClientConfig extends WebpackBaseConfig { if (this.dev) { config.entry.app.unshift( // https://github.com/webpack-contrib/webpack-hot-middleware/issues/53#issuecomment-162823945 - 'eventsource-polyfill', + this.resolveModule('eventsource-polyfill'), // https://github.com/glenjamin/webpack-hot-middleware#config - `webpack-hot-middleware/client?${hotMiddlewareClientOptionsStr}` + `${this.resolveModule('webpack-hot-middleware/client')}?${hotMiddlewareClientOptionsStr}` ) } diff --git a/packages/webpack/src/plugins/vue/client.js b/packages/webpack/src/plugins/vue/client.js index 6f718747ed..be08d91872 100644 --- a/packages/webpack/src/plugins/vue/client.js +++ b/packages/webpack/src/plugins/vue/client.js @@ -4,7 +4,7 @@ */ import hash from 'hash-sum' -import uniq from 'lodash/uniq' +import { uniq } from 'lodash' import { isJS, isCSS } from './util' diff --git a/packages/webpack/src/utils/perf-loader.js b/packages/webpack/src/utils/perf-loader.js index f37d59a03f..bb6a439dbe 100644 --- a/packages/webpack/src/utils/perf-loader.js +++ b/packages/webpack/src/utils/perf-loader.js @@ -6,10 +6,11 @@ import { warmup } from 'thread-loader' // https://github.com/webpack-contrib/cache-loader export default class PerfLoader { - constructor (name, buildContext) { + constructor (name, buildContext, { resolveModule }) { this.name = name this.buildContext = buildContext this.workerPools = PerfLoader.defaultPools({ dev: buildContext.options.dev }) + this.resolveModule = resolveModule return new Proxy(this, { get (target, name) { return target[name] ? target[name] : target.use.bind(target, name) @@ -25,13 +26,13 @@ export default class PerfLoader { } } - static warmupAll ({ dev }) { + static warmupAll ({ dev, resolveModule }) { const pools = PerfLoader.defaultPools({ dev }) PerfLoader.warmup(pools.js, [ - require.resolve('babel-loader'), - require.resolve('@babel/preset-env') + resolveModule('babel-loader'), + resolveModule('@babel/preset-env') ]) - PerfLoader.warmup(pools.css, ['css-loader']) + PerfLoader.warmup(pools.css, [resolveModule('css-loader')]) } static warmup (...args) { @@ -43,7 +44,7 @@ export default class PerfLoader { if (this.buildContext.buildOptions.cache) { loaders.push({ - loader: 'cache-loader', + loader: this.resolveModule('cache-loader'), options: { cacheDirectory: path.resolve(`node_modules/.cache/cache-loader/${this.name}`) } @@ -54,7 +55,7 @@ export default class PerfLoader { const pool = this.workerPools[poolName] if (pool) { loaders.push({ - loader: 'thread-loader', + loader: this.resolveModule('thread-loader'), options: pool }) } diff --git a/packages/webpack/src/utils/postcss.js b/packages/webpack/src/utils/postcss.js index 07a80a753c..1377fb71ac 100644 --- a/packages/webpack/src/utils/postcss.js +++ b/packages/webpack/src/utils/postcss.js @@ -1,9 +1,7 @@ import fs from 'fs' import path from 'path' import consola from 'consola' -import defaults from 'lodash/defaults' -import merge from 'lodash/merge' -import cloneDeep from 'lodash/cloneDeep' +import { defaults, merge, cloneDeep } from 'lodash' import createResolver from 'postcss-import-resolver' import { isPureObject } from '@nuxt/utils' diff --git a/packages/webpack/src/utils/style-loader.js b/packages/webpack/src/utils/style-loader.js index 1a7ca5a220..0b79b1184c 100644 --- a/packages/webpack/src/utils/style-loader.js +++ b/packages/webpack/src/utils/style-loader.js @@ -6,10 +6,11 @@ import { wrapArray } from '@nuxt/utils' import PostcssConfig from './postcss' export default class StyleLoader { - constructor (buildContext, { isServer, perfLoader }) { + constructor (buildContext, { isServer, perfLoader, resolveModule }) { this.buildContext = buildContext this.isServer = isServer this.perfLoader = perfLoader + this.resolveModule = resolveModule if (buildContext.options.build.postcss) { this.postcssConfig = new PostcssConfig(buildContext) @@ -40,7 +41,7 @@ export default class StyleLoader { const patterns = wrapArray(extResource).map(p => path.resolve(rootDir, p)) return { - loader: 'style-resources-loader', + loader: this.resolveModule('style-resources-loader'), options: Object.assign( { patterns }, styleResources.options || {} @@ -62,13 +63,13 @@ export default class StyleLoader { } return { - loader: 'postcss-loader', + loader: this.resolveModule('postcss-loader'), options: Object.assign({ sourceMap: this.buildContext.buildOptions.cssSourceMap }, config) } } css (options) { - const cssLoader = { loader: 'css-loader', options } + const cssLoader = { loader: this.resolveModule('css-loader'), options } if (this.exportOnlyLocals) { options.modules = { @@ -102,7 +103,7 @@ export default class StyleLoader { styleLoader () { return this.extract() || { - loader: 'vue-style-loader', + loader: this.resolveModule('vue-style-loader'), options: this.buildContext.buildOptions.loaders.vueStyle } } diff --git a/scripts/changelog.js b/scripts/changelog.js index 66c2a7893b..38ac382e8f 100644 --- a/scripts/changelog.js +++ b/scripts/changelog.js @@ -1,8 +1,6 @@ import consola from 'consola' import execa from 'execa' -import groupBy from 'lodash/groupBy' -import sortBy from 'lodash/sortBy' -import uniq from 'lodash/uniq' +import { uniq, sortBy, groupBy } from 'lodash' import { writeFile } from 'fs-extra' const types = { diff --git a/scripts/dev.js b/scripts/dev.js index 6a823e5a40..1b81e92c5f 100755 --- a/scripts/dev.js +++ b/scripts/dev.js @@ -1,4 +1,3 @@ -#!/usr/bin/env node -r esm import path from 'path' import fs from 'fs-extra' import consola from 'consola' @@ -11,7 +10,7 @@ const useCjs = [ const stub = { es: 'export * from \'../src/index\'', - cjs: `const _require = typeof jest === 'undefined' ? require('esm')(module) : require + cjs: `const _require = typeof jest === 'undefined' ? require('jiti')(__dirname) : require global.__NUXT_DEV__ = true module.exports = _require('../src/index') ` diff --git a/scripts/link.js b/scripts/link.js index 017cebb580..0bfc834b54 100644 --- a/scripts/link.js +++ b/scripts/link.js @@ -1,8 +1,11 @@ -const path = require('path') -const consola = require('consola') -const execa = require('execa') -const fs = require('fs-extra') -const glob = require('pify')(require('glob')) +import path from 'path' +import consola from 'consola' +import execa from 'execa' +import fs from 'fs-extra' +import _glob from 'glob' +import pify from 'pify' + +const glob = pify(_glob) // TODO: use globby async function main () { const packageDirs = await glob('+(packages|distributions)/*') diff --git a/scripts/package b/scripts/package index 8ba0035f6b..e3416046fb 100755 --- a/scripts/package +++ b/scripts/package @@ -1,5 +1,3 @@ -#!/usr/bin/env node -r esm - import consola from 'consola' import Package from './package.js' diff --git a/scripts/package.js b/scripts/package.js index e23bab9359..0bfe8c165c 100644 --- a/scripts/package.js +++ b/scripts/package.js @@ -9,6 +9,7 @@ import pify from 'pify' import sortPackageJson from 'sort-package-json' import rollupConfig from './rollup.config' +import { builtins } from './builtins' const DEFAULTS = { rootDir: process.cwd(), @@ -185,11 +186,22 @@ export default class Package { } async build (_watch = false) { + // Externals + const externals = [ + // Dependencies that will be installed alongise with the nuxt package + ...Object.keys(this.pkg.dependencies || {}), + // Builtin node modules + ...builtins, + // Custom externals + ...(this.options.externals || []) + ] + // Prepare rollup config const config = { rootDir: this.options.rootDir, alias: {}, replace: {}, + externals, ...this.options.rollup } @@ -250,27 +262,30 @@ export default class Package { try { const bundle = await rollup(_rollupConfig) await remove(_rollupConfig.output.dir) - await bundle.write(_rollupConfig.output) + const result = await bundle.write(_rollupConfig.output) this.logger.success('Bundle built') await this.callHook('build:done', { bundle }) // Analyze bundle imports against pkg - // if (this.pkg.dependencies) { - // const dependencies = {} - // for (const dep in this.pkg.dependencies) { - // dependencies[dep] = this.pkg.dependencies[dep] - // } - // for (const imp of bundle.imports) { - // delete dependencies[imp] - // } - // for (const dep in dependencies) { - // this.logger.warn(`Unused dependency ${dep}@${dependencies[dep]}`) - // } - // } + const dependencies = Object.keys(this.pkg.dependencies || {}) + const imports = [].concat(...result.output.map(o => o.imports)) + + const missingDependencies = imports + .filter(i => !i.endsWith('.js')) // dynamic imports + .filter(i => !dependencies.includes(i) && !externals.find(e => i.startsWith(e))) + if (missingDependencies.length) { + throw new Error(`Missing dependencies in ${this.pkg.name}: ` + missingDependencies.join(', ')) + } + const ignoreUnused = this.options.ignoreUnused || [] + const unusedDependencies = dependencies.filter(d => + !imports.find(i => i.startsWith(d)) && !ignoreUnused.includes(d) + ) + if (unusedDependencies.length) { + throw new Error(`Unused dependencies in ${this.pkg.name}: ` + unusedDependencies.join(', ')) + } } catch (err) { this.formatError(err) - this.logger.error(err) throw err } } @@ -282,7 +297,7 @@ export default class Package { const { file, column, line } = error.loc loc = `${file}:${line}:${column}` } - error.message = `[${error.code}] ${error.message}\nat ${loc}` + error.message = `[${error.code || ''}] ${error.message}\nat ${loc}` return error } diff --git a/scripts/rollup.config.js b/scripts/rollup.config.js index d6a974cd33..26372d6861 100644 --- a/scripts/rollup.config.js +++ b/scripts/rollup.config.js @@ -4,11 +4,9 @@ import jsonPlugin from '@rollup/plugin-json' import commonjsPlugin from '@rollup/plugin-commonjs' import replacePlugin from '@rollup/plugin-replace' import aliasPlugin from '@rollup/plugin-alias' -import nodeResolvePlugin from '@rollup/plugin-node-resolve' +// import nodeResolvePlugin from '@rollup/plugin-node-resolve' import licensePlugin from 'rollup-plugin-license' -import defaultsDeep from 'lodash/defaultsDeep' - -import { builtins } from './builtins' +import { defaultsDeep } from 'lodash' export default function rollupConfig ({ rootDir = process.cwd(), @@ -17,12 +15,6 @@ export default function rollupConfig ({ replace = {}, alias = {}, externals = [], - resolve = { - resolveOnly: [ - /lodash/, - /^((?!node_modules).)*$/ - ] - }, ...options }, pkg) { if (!pkg) { @@ -40,14 +32,7 @@ export default function rollupConfig ({ format: 'cjs', preferConst: true }, - external: [ - // Dependencies that will be installed alongise with the nuxt package - ...Object.keys(pkg.dependencies || {}), - // Builtin node modules - ...builtins, - // Explicit externals - ...externals - ], + external: externals, plugins: [ aliasPlugin(alias), replacePlugin({ @@ -58,7 +43,12 @@ export default function rollupConfig ({ ...replace } }), - nodeResolvePlugin(resolve), + // nodeResolvePlugin({ + // preferBuiltins: true, + // resolveOnly: [ + // /lodash/ + // ] + // }), commonjsPlugin({ include: /node_modules/ }), jsonPlugin(), licensePlugin({ diff --git a/test/dev/wp.config.test.js b/test/dev/wp.config.test.js index 04af2f43da..089fed40fc 100644 --- a/test/dev/wp.config.test.js +++ b/test/dev/wp.config.test.js @@ -6,12 +6,13 @@ describe('webpack configuration', () => { test('performance loader', () => { const js = { name: 'js', poolTimeout: Infinity } const css = { name: 'css', poolTimeout: Infinity } + const resolveModule = jest.fn(id => id) PerfLoader.warmup = jest.fn() - PerfLoader.warmupAll({ dev: true }) + PerfLoader.warmupAll({ dev: true, resolveModule }) expect(PerfLoader.warmup).toHaveBeenCalledTimes(2) expect(PerfLoader.warmup).toHaveBeenCalledWith(js, [ - require.resolve('babel-loader'), - require.resolve('@babel/preset-env') + 'babel-loader', + '@babel/preset-env' ]) expect(PerfLoader.warmup).toHaveBeenCalledWith(css, ['css-loader']) @@ -25,6 +26,9 @@ describe('webpack configuration', () => { parallel: true, cache: true } + }, + { + resolveModule } ) expect(perfLoader.workerPools).toMatchObject({ js, css }) diff --git a/test/fixtures/spa-base/nuxt.config.js b/test/fixtures/spa-base/nuxt.config.js index e8b342045f..53777836b9 100644 --- a/test/fixtures/spa-base/nuxt.config.js +++ b/test/fixtures/spa-base/nuxt.config.js @@ -1,5 +1,5 @@ import { resolve } from 'path' -import defaultsDeep from 'lodash/defaultsDeep' +import { defaultsDeep } from 'lodash' import baseNuxtConfig from '../spa/nuxt.config' const config = { diff --git a/test/fixtures/spa-hash/nuxt.config.js b/test/fixtures/spa-hash/nuxt.config.js index 16c8423d0d..992e3f362d 100644 --- a/test/fixtures/spa-hash/nuxt.config.js +++ b/test/fixtures/spa-hash/nuxt.config.js @@ -1,5 +1,5 @@ import { resolve } from 'path' -import defaultsDeep from 'lodash/defaultsDeep' +import { defaultsDeep } from 'lodash' import baseNuxtConfig from '../spa/nuxt.config' const config = { diff --git a/test/utils/chrome.js b/test/utils/chrome.js index 5059285cb5..2e57f4d19e 100644 --- a/test/utils/chrome.js +++ b/test/utils/chrome.js @@ -8,7 +8,7 @@ import path from 'path' import { execSync, execFileSync } from 'child_process' import isWsl from 'is-wsl' import consola from 'consola' -import uniq from 'lodash/uniq' +import { uniq } from 'lodash' const newLineRegex = /\r?\n/ diff --git a/test/utils/setup-env.js b/test/utils/setup-env.js index 95dc33e23b..89d1192093 100644 --- a/test/utils/setup-env.js +++ b/test/utils/setup-env.js @@ -21,3 +21,15 @@ function errorTrap (error) { process.on('unhandledRejection', errorTrap) process.on('uncaughtException', errorTrap) + +expect.extend({ + toBePath (received, posixPath, winPath) { + const expectedPath = isWin ? winPath : posixPath + const pass = received === expectedPath + return { + pass, + message: () => + `expected path ${received} to be ${expectedPath}` + } + } +}) diff --git a/yarn.lock b/yarn.lock index b1c9c05bdf..428197d097 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3966,7 +3966,7 @@ caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30001109, can resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001168.tgz#6fcd098c139d003b9bd484cbb9ca26cb89907f9a" integrity sha512-P2zmX7swIXKu+GMMR01TWa4csIKELTNnZKc+f1CjebmZJQtTAEXmpQSoKVJVVcvPGAA0TEYTOUp3VehavZSFPQ== -caniuse-lite@^1.0.30001170: +caniuse-lite@^1.0.30001168: version "1.0.30001170" resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001170.tgz#0088bfecc6a14694969e391cc29d7eb6362ca6a7" integrity sha512-Dd4d/+0tsK0UNLrZs3CvNukqalnVTRrxb5mcQm8rHL49t7V5ZaTygwXkrq+FB+dVDf++4ri8eJnFEJAB8332PA== @@ -4973,7 +4973,7 @@ debug@3.1.0: dependencies: ms "2.0.0" -debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0: +debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: version "4.3.1" resolved "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== @@ -5761,11 +5761,6 @@ eslint@^7.16.0: text-table "^0.2.0" v8-compile-cache "^2.0.3" -esm@^3.2.25: - version "3.2.25" - resolved "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz#342c18c29d56157688ba5ce31f8431fbb795cc10" - integrity sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA== - espree@^6.2.1: version "6.2.1" resolved "https://registry.npmjs.org/espree/-/espree-6.2.1.tgz#77fc72e1fd744a2052c20f38a5b575832e82734a" @@ -8500,7 +8495,7 @@ lodash@4.17.19: resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b" integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ== -lodash@^4.15.0, lodash@^4.17.12, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.5, lodash@^4.2.1: +lodash@^4.15.0, lodash@^4.17.12, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.5, lodash@^4.2.1, lodash@^4.4.2: version "4.17.20" resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== @@ -10041,6 +10036,13 @@ pluralize@^8.0.0: resolved "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1" integrity sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA== +pnp-webpack-plugin@^1.6.4: + version "1.6.4" + resolved "https://registry.npmjs.org/pnp-webpack-plugin/-/pnp-webpack-plugin-1.6.4.tgz#c9711ac4dc48a685dabafc86f8b6dd9f8df84149" + integrity sha512-7Wjy+9E3WwLOEL30D+m8TSTF7qJJUJLONBnwQp0518siuMxUQUbgZwssaFX+QKlZkjHZcw/IpZCt/H0srrntSg== + dependencies: + ts-pnp "^1.1.6" + posix-character-classes@^0.1.0: version "0.1.1" resolved "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" @@ -12768,6 +12770,11 @@ ts-jest@26.x: semver "7.x" yargs-parser "20.x" +ts-pnp@^1.1.6: + version "1.2.0" + resolved "https://registry.npmjs.org/ts-pnp/-/ts-pnp-1.2.0.tgz#a500ad084b0798f1c3071af391e65912c86bca92" + integrity sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw== + tsconfig-paths@^3.9.0: version "3.9.0" resolved "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz#098547a6c4448807e8fcb8eae081064ee9a3c90b" @@ -13052,11 +13059,6 @@ url-loader@^4.1.1: mime-types "^2.1.27" schema-utils "^3.0.0" -url-polyfill@^1.1.12: - version "1.1.12" - resolved "https://registry.npmjs.org/url-polyfill/-/url-polyfill-1.1.12.tgz#6cdaa17f6b022841b3aec0bf8dbd87ac0cd33331" - integrity sha512-mYFmBHCapZjtcNHW0MDq9967t+z4Dmg5CJ0KqysK3+ZbyoNOWQHksGCTWwDhxGXllkWlOc10Xfko6v4a3ucM6A== - url@^0.11.0: version "0.11.0" resolved "https://registry.npmjs.org/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1"