mirror of https://github.com/nuxt/nuxt.git
fix(cli): config cache invalidation + refactors (#5500)
This commit is contained in:
parent
52ad79baeb
commit
d0afaa1daf
|
@ -3,7 +3,8 @@ import path from 'path'
|
||||||
import consola from 'consola'
|
import consola from 'consola'
|
||||||
import minimist from 'minimist'
|
import minimist from 'minimist'
|
||||||
import { name, version } from '../package.json'
|
import { name, version } from '../package.json'
|
||||||
import { loadNuxtConfig, forceExit } from './utils'
|
import { forceExit } from './utils'
|
||||||
|
import { loadNuxtConfig } from './utils/config'
|
||||||
import { indent, foldLines, colorize } from './utils/formatting'
|
import { indent, foldLines, colorize } from './utils/formatting'
|
||||||
import { startSpaces, optionSpaces, forceExitTimeout } from './utils/constants'
|
import { startSpaces, optionSpaces, forceExitTimeout } from './utils/constants'
|
||||||
import { detectTypeScript } from './utils/typescript'
|
import { detectTypeScript } from './utils/typescript'
|
||||||
|
|
|
@ -2,7 +2,8 @@ import consola from 'consola'
|
||||||
import chalk from 'chalk'
|
import chalk from 'chalk'
|
||||||
import opener from 'opener'
|
import opener from 'opener'
|
||||||
import { common, server } from '../options'
|
import { common, server } from '../options'
|
||||||
import { showBanner, eventsMapping, formatPath } from '../utils'
|
import { eventsMapping, formatPath } from '../utils'
|
||||||
|
import { showBanner } from '../utils/banner'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'dev',
|
name: 'dev',
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { common, server } from '../options'
|
import { common, server } from '../options'
|
||||||
import { showBanner } from '../utils'
|
import { showBanner } from '../utils/banner'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'start',
|
name: 'start',
|
||||||
|
|
|
@ -7,4 +7,4 @@ export const imports = _imports
|
||||||
export { default as NuxtCommand } from './command'
|
export { default as NuxtCommand } from './command'
|
||||||
export { default as setup } from './setup'
|
export { default as setup } from './setup'
|
||||||
export { default as run } from './run'
|
export { default as run } from './run'
|
||||||
export { loadNuxtConfig } from './utils'
|
export { loadNuxtConfig } from './utils/config'
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
import consola from 'consola'
|
||||||
|
import prettyBytes from 'pretty-bytes'
|
||||||
|
import env from 'std-env'
|
||||||
|
import chalk from 'chalk'
|
||||||
|
import { successBox } from './formatting'
|
||||||
|
|
||||||
|
export function showBanner(nuxt) {
|
||||||
|
if (env.test) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (env.minimalCLI) {
|
||||||
|
for (const listener of nuxt.server.listeners) {
|
||||||
|
consola.info('Listening on: ' + listener.url)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const titleLines = []
|
||||||
|
const messageLines = []
|
||||||
|
|
||||||
|
// Name and version
|
||||||
|
titleLines.push(`${chalk.green.bold('Nuxt.js')} ${nuxt.constructor.version}`)
|
||||||
|
|
||||||
|
// Running mode
|
||||||
|
titleLines.push(`Running in ${nuxt.options.dev ? chalk.bold.blue('development') : chalk.bold.green('production')} mode (${chalk.bold(nuxt.options.mode)})`)
|
||||||
|
|
||||||
|
if (nuxt.options._typescript && nuxt.options._typescript.runtime) {
|
||||||
|
titleLines.push(`TypeScript support is ${chalk.green.bold('enabled')}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://nodejs.org/api/process.html#process_process_memoryusage
|
||||||
|
const { heapUsed, rss } = process.memoryUsage()
|
||||||
|
titleLines.push(`Memory usage: ${chalk.bold(prettyBytes(heapUsed))} (RSS: ${prettyBytes(rss)})`)
|
||||||
|
|
||||||
|
// Listeners
|
||||||
|
for (const listener of nuxt.server.listeners) {
|
||||||
|
messageLines.push(chalk.bold('Listening on: ') + chalk.underline.blue(listener.url))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add custom badge messages
|
||||||
|
if (nuxt.options.cli.badgeMessages.length) {
|
||||||
|
messageLines.push('', ...nuxt.options.cli.badgeMessages)
|
||||||
|
}
|
||||||
|
|
||||||
|
process.stdout.write(successBox(messageLines.join('\n'), titleLines.join('\n')))
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
import path from 'path'
|
||||||
|
import consola from 'consola'
|
||||||
|
import defaultsDeep from 'lodash/defaultsDeep'
|
||||||
|
import { defaultNuxtConfigFile, getDefaultNuxtConfig } from '@nuxt/config'
|
||||||
|
import { clearRequireCache, scanRequireTree } from '@nuxt/utils'
|
||||||
|
import esm from 'esm'
|
||||||
|
|
||||||
|
export async function loadNuxtConfig(argv) {
|
||||||
|
const rootDir = path.resolve(argv._[0] || '.')
|
||||||
|
let nuxtConfigFile
|
||||||
|
let options = {}
|
||||||
|
|
||||||
|
try {
|
||||||
|
nuxtConfigFile = require.resolve(path.resolve(rootDir, argv['config-file']))
|
||||||
|
} catch (e) {
|
||||||
|
if (e.code !== 'MODULE_NOT_FOUND') {
|
||||||
|
throw (e)
|
||||||
|
} else if (argv['config-file'] !== defaultNuxtConfigFile) {
|
||||||
|
consola.fatal('Could not load config file: ' + argv['config-file'])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nuxtConfigFile) {
|
||||||
|
if (nuxtConfigFile.endsWith('.ts')) {
|
||||||
|
options = require(nuxtConfigFile) || {}
|
||||||
|
} else {
|
||||||
|
clearRequireCache(nuxtConfigFile)
|
||||||
|
options = esm(module, { cache: false, cjs: { cache: false } })(nuxtConfigFile) || {}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.default) {
|
||||||
|
options = options.default
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof options === 'function') {
|
||||||
|
try {
|
||||||
|
options = await options()
|
||||||
|
if (options.default) {
|
||||||
|
options = options.default
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
consola.error(error)
|
||||||
|
consola.fatal('Error while fetching async configuration')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep _nuxtConfigFile for watching
|
||||||
|
options._nuxtConfigFile = nuxtConfigFile
|
||||||
|
|
||||||
|
// Keep all related files for watching
|
||||||
|
options._nuxtConfigFiles = Array.from(scanRequireTree(nuxtConfigFile))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof options.rootDir !== 'string') {
|
||||||
|
options.rootDir = rootDir
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nuxt Mode
|
||||||
|
options.mode =
|
||||||
|
(argv.spa && 'spa') || (argv.universal && 'universal') || options.mode
|
||||||
|
|
||||||
|
// Server options
|
||||||
|
options.server = defaultsDeep({
|
||||||
|
port: argv.port || undefined,
|
||||||
|
host: argv.hostname || undefined,
|
||||||
|
socket: argv['unix-socket'] || undefined
|
||||||
|
}, options.server || {}, getDefaultNuxtConfig().server)
|
||||||
|
|
||||||
|
return options
|
||||||
|
}
|
|
@ -1,23 +1,10 @@
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import consola from 'consola'
|
|
||||||
import esm from 'esm'
|
|
||||||
import exit from 'exit'
|
import exit from 'exit'
|
||||||
import defaultsDeep from 'lodash/defaultsDeep'
|
|
||||||
import { defaultNuxtConfigFile, getDefaultNuxtConfig } from '@nuxt/config'
|
|
||||||
import { lock } from '@nuxt/utils'
|
import { lock } from '@nuxt/utils'
|
||||||
import chalk from 'chalk'
|
import chalk from 'chalk'
|
||||||
import prettyBytes from 'pretty-bytes'
|
|
||||||
import env from 'std-env'
|
import env from 'std-env'
|
||||||
import { successBox, warningBox } from './formatting'
|
import { warningBox } from './formatting'
|
||||||
|
|
||||||
const esmOptions = {
|
|
||||||
cache: false,
|
|
||||||
cjs: {
|
|
||||||
cache: true,
|
|
||||||
vars: true,
|
|
||||||
namedExports: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const eventsMapping = {
|
export const eventsMapping = {
|
||||||
add: { icon: '+', color: 'green', action: 'Created' },
|
add: { icon: '+', color: 'green', action: 'Created' },
|
||||||
|
@ -25,103 +12,6 @@ export const eventsMapping = {
|
||||||
unlink: { icon: '-', color: 'red', action: 'Removed' }
|
unlink: { icon: '-', color: 'red', action: 'Removed' }
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function loadNuxtConfig(argv) {
|
|
||||||
const rootDir = path.resolve(argv._[0] || '.')
|
|
||||||
let nuxtConfigFile
|
|
||||||
let options = {}
|
|
||||||
|
|
||||||
try {
|
|
||||||
nuxtConfigFile = require.resolve(path.resolve(rootDir, argv['config-file']))
|
|
||||||
} catch (e) {
|
|
||||||
if (e.code !== 'MODULE_NOT_FOUND') {
|
|
||||||
throw (e)
|
|
||||||
} else if (argv['config-file'] !== defaultNuxtConfigFile) {
|
|
||||||
consola.fatal('Could not load config file: ' + argv['config-file'])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nuxtConfigFile) {
|
|
||||||
options = (nuxtConfigFile.endsWith('.ts') ? require(nuxtConfigFile) : esm(module, esmOptions)(nuxtConfigFile)) || {}
|
|
||||||
if (options.default) {
|
|
||||||
options = options.default
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof options === 'function') {
|
|
||||||
try {
|
|
||||||
options = await options()
|
|
||||||
if (options.default) {
|
|
||||||
options = options.default
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
consola.error(error)
|
|
||||||
consola.fatal('Error while fetching async configuration')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keep _nuxtConfigFile for watching
|
|
||||||
options._nuxtConfigFile = nuxtConfigFile
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof options.rootDir !== 'string') {
|
|
||||||
options.rootDir = rootDir
|
|
||||||
}
|
|
||||||
|
|
||||||
// Nuxt Mode
|
|
||||||
options.mode =
|
|
||||||
(argv.spa && 'spa') || (argv.universal && 'universal') || options.mode
|
|
||||||
|
|
||||||
// Server options
|
|
||||||
options.server = defaultsDeep({
|
|
||||||
port: argv.port || undefined,
|
|
||||||
host: argv.hostname || undefined,
|
|
||||||
socket: argv['unix-socket'] || undefined
|
|
||||||
}, options.server || {}, getDefaultNuxtConfig().server)
|
|
||||||
|
|
||||||
return options
|
|
||||||
}
|
|
||||||
|
|
||||||
export function showBanner(nuxt) {
|
|
||||||
if (env.test) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (env.minimalCLI) {
|
|
||||||
for (const listener of nuxt.server.listeners) {
|
|
||||||
consola.info('Listening on: ' + listener.url)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const titleLines = []
|
|
||||||
const messageLines = []
|
|
||||||
|
|
||||||
// Name and version
|
|
||||||
titleLines.push(`${chalk.green.bold('Nuxt.js')} ${nuxt.constructor.version}`)
|
|
||||||
|
|
||||||
// Running mode
|
|
||||||
titleLines.push(`Running in ${nuxt.options.dev ? chalk.bold.blue('development') : chalk.bold.green('production')} mode (${chalk.bold(nuxt.options.mode)})`)
|
|
||||||
|
|
||||||
if (nuxt.options._typescript && nuxt.options._typescript.runtime) {
|
|
||||||
titleLines.push(`TypeScript support is ${chalk.green.bold('enabled')}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://nodejs.org/api/process.html#process_process_memoryusage
|
|
||||||
const { heapUsed, rss } = process.memoryUsage()
|
|
||||||
titleLines.push(`Memory usage: ${chalk.bold(prettyBytes(heapUsed))} (RSS: ${prettyBytes(rss)})`)
|
|
||||||
|
|
||||||
// Listeners
|
|
||||||
for (const listener of nuxt.server.listeners) {
|
|
||||||
messageLines.push(chalk.bold('Listening on: ') + chalk.underline.blue(listener.url))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add custom badge messages
|
|
||||||
if (nuxt.options.cli.badgeMessages.length) {
|
|
||||||
messageLines.push('', ...nuxt.options.cli.badgeMessages)
|
|
||||||
}
|
|
||||||
|
|
||||||
process.stdout.write(successBox(messageLines.join('\n'), titleLines.join('\n')))
|
|
||||||
}
|
|
||||||
|
|
||||||
export function formatPath(filePath) {
|
export function formatPath(filePath) {
|
||||||
if (!filePath) {
|
if (!filePath) {
|
||||||
return
|
return
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { consola } from '../utils'
|
import { consola } from '../utils'
|
||||||
import * as utils from '../../src/utils'
|
import { showBanner } from '../../src/utils/banner'
|
||||||
|
|
||||||
jest.mock('std-env', () => ({
|
jest.mock('std-env', () => ({
|
||||||
test: false,
|
test: false,
|
||||||
|
@ -15,7 +15,7 @@ describe('cli/utils', () => {
|
||||||
{ url: 'second' }
|
{ url: 'second' }
|
||||||
]
|
]
|
||||||
|
|
||||||
utils.showBanner({
|
showBanner({
|
||||||
options: {
|
options: {
|
||||||
cli: {}
|
cli: {}
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import { getDefaultNuxtConfig } from '@nuxt/config'
|
import { getDefaultNuxtConfig } from '@nuxt/config'
|
||||||
import { consola } from '../utils'
|
import { consola } from '../utils'
|
||||||
|
import { loadNuxtConfig } from '../../src/utils/config'
|
||||||
import * as utils from '../../src/utils'
|
import * as utils from '../../src/utils'
|
||||||
|
import { showBanner } from '../../src/utils/banner'
|
||||||
import * as fmt from '../../src/utils/formatting'
|
import * as fmt from '../../src/utils/formatting'
|
||||||
|
|
||||||
jest.mock('std-env', () => ({
|
jest.mock('std-env', () => ({
|
||||||
|
@ -18,7 +20,7 @@ describe('cli/utils', () => {
|
||||||
universal: true
|
universal: true
|
||||||
}
|
}
|
||||||
|
|
||||||
const options = await utils.loadNuxtConfig(argv)
|
const options = await loadNuxtConfig(argv)
|
||||||
expect(options.rootDir).toBe(process.cwd())
|
expect(options.rootDir).toBe(process.cwd())
|
||||||
expect(options.mode).toBe('universal')
|
expect(options.mode).toBe('universal')
|
||||||
expect(options.server.host).toBe('localhost')
|
expect(options.server.host).toBe('localhost')
|
||||||
|
@ -33,7 +35,7 @@ describe('cli/utils', () => {
|
||||||
spa: true
|
spa: true
|
||||||
}
|
}
|
||||||
|
|
||||||
const options = await utils.loadNuxtConfig(argv)
|
const options = await loadNuxtConfig(argv)
|
||||||
expect(options.testOption).toBe(true)
|
expect(options.testOption).toBe(true)
|
||||||
expect(options.rootDir).toBe('/some/path')
|
expect(options.rootDir).toBe('/some/path')
|
||||||
expect(options.mode).toBe('spa')
|
expect(options.mode).toBe('spa')
|
||||||
|
@ -48,7 +50,7 @@ describe('cli/utils', () => {
|
||||||
'config-file': '../fixtures/nuxt.doesnt-exist.js'
|
'config-file': '../fixtures/nuxt.doesnt-exist.js'
|
||||||
}
|
}
|
||||||
|
|
||||||
const options = await utils.loadNuxtConfig(argv)
|
const options = await loadNuxtConfig(argv)
|
||||||
expect(options.testOption).not.toBeDefined()
|
expect(options.testOption).not.toBeDefined()
|
||||||
|
|
||||||
expect(consola.fatal).toHaveBeenCalledTimes(1)
|
expect(consola.fatal).toHaveBeenCalledTimes(1)
|
||||||
|
@ -64,7 +66,7 @@ describe('cli/utils', () => {
|
||||||
'unix-socket': '/var/run/async.sock'
|
'unix-socket': '/var/run/async.sock'
|
||||||
}
|
}
|
||||||
|
|
||||||
const options = await utils.loadNuxtConfig(argv)
|
const options = await loadNuxtConfig(argv)
|
||||||
expect(options.testOption).toBe(true)
|
expect(options.testOption).toBe(true)
|
||||||
expect(options.mode).toBe('supercharged')
|
expect(options.mode).toBe('supercharged')
|
||||||
expect(options.server.host).toBe('async-host')
|
expect(options.server.host).toBe('async-host')
|
||||||
|
@ -78,7 +80,7 @@ describe('cli/utils', () => {
|
||||||
'config-file': '../fixtures/nuxt.async-error.js'
|
'config-file': '../fixtures/nuxt.async-error.js'
|
||||||
}
|
}
|
||||||
|
|
||||||
const options = await utils.loadNuxtConfig(argv)
|
const options = await loadNuxtConfig(argv)
|
||||||
expect(options.testOption).not.toBeDefined()
|
expect(options.testOption).not.toBeDefined()
|
||||||
|
|
||||||
expect(consola.error).toHaveBeenCalledTimes(1)
|
expect(consola.error).toHaveBeenCalledTimes(1)
|
||||||
|
@ -130,7 +132,7 @@ describe('cli/utils', () => {
|
||||||
{ url: 'second' }
|
{ url: 'second' }
|
||||||
]
|
]
|
||||||
|
|
||||||
utils.showBanner({
|
showBanner({
|
||||||
options: {
|
options: {
|
||||||
cli: {
|
cli: {
|
||||||
badgeMessages
|
badgeMessages
|
||||||
|
|
|
@ -96,8 +96,14 @@ export function getNuxtConfig(_options) {
|
||||||
options._nuxtConfigFile = path.resolve(options.rootDir, `${defaultNuxtConfigFile}.js`)
|
options._nuxtConfigFile = path.resolve(options.rootDir, `${defaultNuxtConfigFile}.js`)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Watch for _nuxtConfigFile changes
|
if (!options._nuxtConfigFiles) {
|
||||||
options.watch.push(options._nuxtConfigFile)
|
options._nuxtConfigFiles = [
|
||||||
|
options._nuxtConfigFile
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Watch for config file changes
|
||||||
|
options.watch.push(...options._nuxtConfigFiles)
|
||||||
|
|
||||||
// Protect rootDir against buildDir
|
// Protect rootDir against buildDir
|
||||||
guardDir(options, 'rootDir', 'buildDir')
|
guardDir(options, 'rootDir', 'buildDir')
|
||||||
|
|
|
@ -5,6 +5,9 @@ Object {
|
||||||
"ErrorPage": null,
|
"ErrorPage": null,
|
||||||
"__normalized__": true,
|
"__normalized__": true,
|
||||||
"_nuxtConfigFile": "/var/nuxt/test/nuxt.config.js",
|
"_nuxtConfigFile": "/var/nuxt/test/nuxt.config.js",
|
||||||
|
"_nuxtConfigFiles": Array [
|
||||||
|
"/var/nuxt/test/nuxt.config.js",
|
||||||
|
],
|
||||||
"appTemplatePath": "/var/nuxt/test/.nuxt/views/app.template.html",
|
"appTemplatePath": "/var/nuxt/test/.nuxt/views/app.template.html",
|
||||||
"build": Object {
|
"build": Object {
|
||||||
"_publicPath": "/_nuxt/",
|
"_publicPath": "/_nuxt/",
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
export function clearRequireCache(id) {
|
||||||
|
const entry = require.cache[id]
|
||||||
|
if (!entry || id.includes('node_modules')) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.parent) {
|
||||||
|
const i = entry.parent.children.findIndex(e => e.id === id)
|
||||||
|
if (i > -1) {
|
||||||
|
entry.parent.children.splice(i, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const child of entry.children) {
|
||||||
|
clearRequireCache(child.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
delete require.cache[id]
|
||||||
|
}
|
||||||
|
|
||||||
|
export function scanRequireTree(id, files = new Set()) {
|
||||||
|
const entry = require.cache[id]
|
||||||
|
if (!entry || id.includes('node_modules') || files.has(id)) {
|
||||||
|
return files
|
||||||
|
}
|
||||||
|
|
||||||
|
files.add(entry.id)
|
||||||
|
|
||||||
|
for (const child of entry.children) {
|
||||||
|
scanRequireTree(child.id, files)
|
||||||
|
}
|
||||||
|
|
||||||
|
return files
|
||||||
|
}
|
|
@ -6,3 +6,4 @@ export * from './route'
|
||||||
export * from './serialize'
|
export * from './serialize'
|
||||||
export * from './task'
|
export * from './task'
|
||||||
export * from './timer'
|
export * from './timer'
|
||||||
|
export * from './cjs'
|
||||||
|
|
|
@ -7,6 +7,7 @@ import * as route from '../src/route'
|
||||||
import * as serialize from '../src/serialize'
|
import * as serialize from '../src/serialize'
|
||||||
import * as task from '../src/task'
|
import * as task from '../src/task'
|
||||||
import * as timer from '../src/timer'
|
import * as timer from '../src/timer'
|
||||||
|
import * as cjs from '../src/cjs'
|
||||||
|
|
||||||
describe('util: entry', () => {
|
describe('util: entry', () => {
|
||||||
test('should export all methods from utils folder', () => {
|
test('should export all methods from utils folder', () => {
|
||||||
|
@ -18,7 +19,8 @@ describe('util: entry', () => {
|
||||||
...route,
|
...route,
|
||||||
...serialize,
|
...serialize,
|
||||||
...task,
|
...task,
|
||||||
...timer
|
...timer,
|
||||||
|
...cjs
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in New Issue