feat: expose webpack config (#7029)

This commit is contained in:
Pooya Parsa 2020-03-02 19:15:00 +01:00 committed by GitHub
parent a3fdba885e
commit 903c3de5b6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 879 additions and 9 deletions

View File

@ -51,7 +51,8 @@
"bin": "bin/nuxt.js",
"files": [
"bin",
"dist"
"dist",
"webpack.config.js"
],
"scripts": {
"postinstall": "opencollective || exit 0"

View File

@ -1,3 +1,4 @@
export * from '@nuxt/core'
export * from '@nuxt/builder'
export * from '@nuxt/generator'
export { getWebpackConfig } from '@nuxt/cli'

View File

@ -0,0 +1,4 @@
module.exports = function () {
const { getWebpackConfig } = require('.')
return getWebpackConfig()
}

View File

@ -53,7 +53,7 @@ export default class NuxtCommand extends Hookable {
}
if (typeof this.cmd.run !== 'function') {
return
throw new TypeError('Invalid command! Commands should at least implement run() function.')
}
let cmdError

View File

@ -3,6 +3,7 @@ const commands = {
dev: () => import('./dev'),
build: () => import('./build'),
generate: () => import('./generate'),
webpack: () => import('./webpack'),
help: () => import('./help')
}

View File

@ -0,0 +1,114 @@
import util from 'util'
import consola from 'consola'
import get from 'lodash/get'
import { common } from '../options'
export default {
name: 'webpack',
description: 'Inspect Nuxt webpack config',
usage: 'webpack [query...]',
options: {
...common,
name: {
alias: 'n',
type: 'string',
default: 'client',
description: 'Webpack bundle name: server, client, modern'
},
depth: {
alias: 'd',
type: 'string',
default: 2,
description: 'Inspection depth'
},
colors: {
type: 'boolean',
default: process.stdout.isTTY,
description: 'Output with ANSI colors'
},
dev: {
type: 'boolean',
default: false,
description: 'Inspect development mode webpack config'
}
},
async run (cmd) {
const { name } = cmd.argv
const queries = [...cmd.argv._]
const config = await cmd.getNuxtConfig({ dev: cmd.argv.dev, server: false })
const nuxt = await cmd.getNuxt(config)
const builder = await cmd.getBuilder(nuxt)
const { bundleBuilder } = builder
const webpackConfig = bundleBuilder.getWebpackConfig(name)
let queryError
const match = queries.reduce((result, query) => {
const m = advancedGet(result, query)
if (m === undefined) {
queryError = query
return result
}
return m
}, webpackConfig)
const serialized = formatObj(match, {
depth: parseInt(cmd.argv.depth),
colors: cmd.argv.colors
})
consola.log(serialized + '\n')
if (serialized.includes('[Object]' || serialized.includes('[Array'))) {
consola.info('You can use `--depth` or add more queries to inspect `[Object]` and `[Array]` fields.')
}
if (queryError) {
consola.warn(`No match in webpack config for \`${queryError}\``)
}
}
}
function advancedGet (obj = {}, query = '') {
let result = obj
if (!query || !result) {
return result
}
const [l, r] = query.split('=')
if (!Array.isArray(result)) {
return typeof result === 'object' ? get(result, l) : result
}
result = result.filter((i) => {
const v = get(i, l)
if (!v) {
return
}
if (
(v === r) ||
(typeof v.test === 'function' && v.test(r)) ||
(typeof v.match === 'function' && v.match(r)) ||
(r && r.match(v))
) {
return true
}
})
if (result.length === 1) {
return result[0]
}
return result.length ? result : undefined
}
function formatObj (obj, formatOptions) {
if (!util.formatWithOptions) {
return util.format(obj)
}
return util.formatWithOptions(formatOptions, obj)
}

View File

@ -12,3 +12,4 @@ export { default as NuxtCommand } from './command'
export { default as setup } from './setup'
export { default as run } from './run'
export { loadNuxtConfig } from './utils/config'
export { getWebpackConfig } from './utils/webpack'

View File

@ -0,0 +1,11 @@
import { core as importCore, builder as importBuilder } from '../imports'
export async function getWebpackConfig (name = 'client', loadOptions = {}) {
const { loadNuxt } = await importCore()
const { getBuilder } = await importBuilder()
const nuxt = await loadNuxt(loadOptions)
const builder = await getBuilder(nuxt)
const { bundleBuilder } = builder
return bundleBuilder.getWebpackConfig(name)
}

View File

@ -0,0 +1,685 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`webpack getWebpackConfig() 1`] = `
"Object {
\\"loader\\": \\"vue-loader\\",
\\"options\\": Object {
\\"productionMode\\": true,
\\"transformAssetUrls\\": Object {
\\"embed\\": \\"src\\",
\\"object\\": \\"src\\",
\\"source\\": \\"src\\",
\\"video\\": \\"src\\",
},
},
\\"test\\": /\\\\.vue$/i,
}"
`;
exports[`webpack nuxt webpack devtool 1`] = `
"false
"
`;
exports[`webpack nuxt webpack module.rules 1`] = `
"Array [
Object {
\\"loader\\": \\"vue-loader\\",
\\"options\\": Object {
\\"productionMode\\": true,
\\"transformAssetUrls\\": Object {
\\"embed\\": \\"src\\",
\\"object\\": \\"src\\",
\\"source\\": \\"src\\",
\\"video\\": \\"src\\",
},
},
\\"test\\": /\\\\.vue$/i,
},
Object {
\\"oneOf\\": Array [
Object {
\\"resourceQuery\\": /^\\\\?vue/i,
\\"use\\": Array [
Object {
\\"loader\\": \\"pug-plain-loader\\",
\\"options\\": Object {},
},
],
},
Object {
\\"use\\": Array [
\\"raw-loader\\",
Object {
\\"loader\\": \\"pug-plain-loader\\",
\\"options\\": Object {},
},
],
},
],
\\"test\\": /\\\\.pug$/i,
},
Object {
\\"exclude\\": [Function exclude],
\\"test\\": /\\\\.jsx?$/i,
\\"use\\": Array [
Object {
\\"loader\\": \\"<nuxtDir>/node_modules/babel-loader/lib/index.js\\",
\\"options\\": Object {
\\"babelrc\\": false,
\\"cacheDirectory\\": false,
\\"configFile\\": false,
\\"envName\\": \\"client\\",
\\"presets\\": Array [
Array [
\\"<nuxtDir>/packages/babel-preset-app/src/index.js\\",
Object {},
],
],
},
},
],
},
Object {
\\"oneOf\\": Array [
Object {
\\"resourceQuery\\": /module/,
\\"use\\": Array [
Object {
\\"loader\\": \\"vue-style-loader\\",
\\"options\\": Object {
\\"sourceMap\\": false,
},
},
Object {
\\"loader\\": \\"css-loader\\",
\\"options\\": Object {
\\"importLoaders\\": 2,
\\"modules\\": Object {
\\"localIdentName\\": \\"[local]_[hash:base64:5]\\",
},
\\"onlyLocals\\": false,
\\"sourceMap\\": false,
},
},
Object {
\\"loader\\": \\"postcss-loader\\",
\\"options\\": Object {
\\"order\\": \\"presetEnvAndCssnanoLast\\",
\\"plugins\\": Array [
[Function anonymous],
[Function anonymous],
[Function anonymous],
[Function anonymous],
],
\\"sourceMap\\": false,
},
},
],
},
Object {
\\"use\\": Array [
Object {
\\"loader\\": \\"vue-style-loader\\",
\\"options\\": Object {
\\"sourceMap\\": false,
},
},
Object {
\\"loader\\": \\"css-loader\\",
\\"options\\": Object {
\\"importLoaders\\": 2,
\\"onlyLocals\\": false,
\\"sourceMap\\": false,
},
},
Object {
\\"loader\\": \\"postcss-loader\\",
\\"options\\": Object {
\\"order\\": \\"presetEnvAndCssnanoLast\\",
\\"plugins\\": Array [
[Function anonymous],
[Function anonymous],
[Function anonymous],
[Function anonymous],
],
\\"sourceMap\\": false,
},
},
],
},
],
\\"test\\": /\\\\.css$/i,
},
Object {
\\"oneOf\\": Array [
Object {
\\"resourceQuery\\": /module/,
\\"use\\": Array [
Object {
\\"loader\\": \\"vue-style-loader\\",
\\"options\\": Object {
\\"sourceMap\\": false,
},
},
Object {
\\"loader\\": \\"css-loader\\",
\\"options\\": Object {
\\"importLoaders\\": 2,
\\"modules\\": Object {
\\"localIdentName\\": \\"[local]_[hash:base64:5]\\",
},
\\"onlyLocals\\": false,
\\"sourceMap\\": false,
},
},
Object {
\\"loader\\": \\"postcss-loader\\",
\\"options\\": Object {
\\"order\\": \\"presetEnvAndCssnanoLast\\",
\\"plugins\\": Array [
[Function anonymous],
[Function anonymous],
[Function anonymous],
[Function anonymous],
],
\\"sourceMap\\": false,
},
},
],
},
Object {
\\"use\\": Array [
Object {
\\"loader\\": \\"vue-style-loader\\",
\\"options\\": Object {
\\"sourceMap\\": false,
},
},
Object {
\\"loader\\": \\"css-loader\\",
\\"options\\": Object {
\\"importLoaders\\": 2,
\\"onlyLocals\\": false,
\\"sourceMap\\": false,
},
},
Object {
\\"loader\\": \\"postcss-loader\\",
\\"options\\": Object {
\\"order\\": \\"presetEnvAndCssnanoLast\\",
\\"plugins\\": Array [
[Function anonymous],
[Function anonymous],
[Function anonymous],
[Function anonymous],
],
\\"sourceMap\\": false,
},
},
],
},
],
\\"test\\": /\\\\.p(ost)?css$/i,
},
Object {
\\"oneOf\\": Array [
Object {
\\"resourceQuery\\": /module/,
\\"use\\": Array [
Object {
\\"loader\\": \\"vue-style-loader\\",
\\"options\\": Object {
\\"sourceMap\\": false,
},
},
Object {
\\"loader\\": \\"css-loader\\",
\\"options\\": Object {
\\"importLoaders\\": 2,
\\"modules\\": Object {
\\"localIdentName\\": \\"[local]_[hash:base64:5]\\",
},
\\"onlyLocals\\": false,
\\"sourceMap\\": false,
},
},
Object {
\\"loader\\": \\"postcss-loader\\",
\\"options\\": Object {
\\"order\\": \\"presetEnvAndCssnanoLast\\",
\\"plugins\\": Array [
[Function anonymous],
[Function anonymous],
[Function anonymous],
[Function anonymous],
],
\\"sourceMap\\": false,
},
},
Object {
\\"loader\\": \\"less-loader\\",
\\"options\\": Object {
\\"sourceMap\\": false,
},
},
],
},
Object {
\\"use\\": Array [
Object {
\\"loader\\": \\"vue-style-loader\\",
\\"options\\": Object {
\\"sourceMap\\": false,
},
},
Object {
\\"loader\\": \\"css-loader\\",
\\"options\\": Object {
\\"importLoaders\\": 2,
\\"onlyLocals\\": false,
\\"sourceMap\\": false,
},
},
Object {
\\"loader\\": \\"postcss-loader\\",
\\"options\\": Object {
\\"order\\": \\"presetEnvAndCssnanoLast\\",
\\"plugins\\": Array [
[Function anonymous],
[Function anonymous],
[Function anonymous],
[Function anonymous],
],
\\"sourceMap\\": false,
},
},
Object {
\\"loader\\": \\"less-loader\\",
\\"options\\": Object {
\\"sourceMap\\": false,
},
},
],
},
],
\\"test\\": /\\\\.less$/i,
},
Object {
\\"oneOf\\": Array [
Object {
\\"resourceQuery\\": /module/,
\\"use\\": Array [
Object {
\\"loader\\": \\"vue-style-loader\\",
\\"options\\": Object {
\\"sourceMap\\": false,
},
},
Object {
\\"loader\\": \\"css-loader\\",
\\"options\\": Object {
\\"importLoaders\\": 2,
\\"modules\\": Object {
\\"localIdentName\\": \\"[local]_[hash:base64:5]\\",
},
\\"onlyLocals\\": false,
\\"sourceMap\\": false,
},
},
Object {
\\"loader\\": \\"postcss-loader\\",
\\"options\\": Object {
\\"order\\": \\"presetEnvAndCssnanoLast\\",
\\"plugins\\": Array [
[Function anonymous],
[Function anonymous],
[Function anonymous],
[Function anonymous],
],
\\"sourceMap\\": false,
},
},
Object {
\\"loader\\": \\"sass-loader\\",
\\"options\\": Object {
\\"sassOptions\\": Object {
\\"indentedSyntax\\": true,
},
\\"sourceMap\\": false,
},
},
],
},
Object {
\\"use\\": Array [
Object {
\\"loader\\": \\"vue-style-loader\\",
\\"options\\": Object {
\\"sourceMap\\": false,
},
},
Object {
\\"loader\\": \\"css-loader\\",
\\"options\\": Object {
\\"importLoaders\\": 2,
\\"onlyLocals\\": false,
\\"sourceMap\\": false,
},
},
Object {
\\"loader\\": \\"postcss-loader\\",
\\"options\\": Object {
\\"order\\": \\"presetEnvAndCssnanoLast\\",
\\"plugins\\": Array [
[Function anonymous],
[Function anonymous],
[Function anonymous],
[Function anonymous],
],
\\"sourceMap\\": false,
},
},
Object {
\\"loader\\": \\"sass-loader\\",
\\"options\\": Object {
\\"sassOptions\\": Object {
\\"indentedSyntax\\": true,
},
\\"sourceMap\\": false,
},
},
],
},
],
\\"test\\": /\\\\.sass$/i,
},
Object {
\\"oneOf\\": Array [
Object {
\\"resourceQuery\\": /module/,
\\"use\\": Array [
Object {
\\"loader\\": \\"vue-style-loader\\",
\\"options\\": Object {
\\"sourceMap\\": false,
},
},
Object {
\\"loader\\": \\"css-loader\\",
\\"options\\": Object {
\\"importLoaders\\": 2,
\\"modules\\": Object {
\\"localIdentName\\": \\"[local]_[hash:base64:5]\\",
},
\\"onlyLocals\\": false,
\\"sourceMap\\": false,
},
},
Object {
\\"loader\\": \\"postcss-loader\\",
\\"options\\": Object {
\\"order\\": \\"presetEnvAndCssnanoLast\\",
\\"plugins\\": Array [
[Function anonymous],
[Function anonymous],
[Function anonymous],
[Function anonymous],
],
\\"sourceMap\\": false,
},
},
Object {
\\"loader\\": \\"sass-loader\\",
\\"options\\": Object {
\\"sourceMap\\": false,
},
},
],
},
Object {
\\"use\\": Array [
Object {
\\"loader\\": \\"vue-style-loader\\",
\\"options\\": Object {
\\"sourceMap\\": false,
},
},
Object {
\\"loader\\": \\"css-loader\\",
\\"options\\": Object {
\\"importLoaders\\": 2,
\\"onlyLocals\\": false,
\\"sourceMap\\": false,
},
},
Object {
\\"loader\\": \\"postcss-loader\\",
\\"options\\": Object {
\\"order\\": \\"presetEnvAndCssnanoLast\\",
\\"plugins\\": Array [
[Function anonymous],
[Function anonymous],
[Function anonymous],
[Function anonymous],
],
\\"sourceMap\\": false,
},
},
Object {
\\"loader\\": \\"sass-loader\\",
\\"options\\": Object {
\\"sourceMap\\": false,
},
},
],
},
],
\\"test\\": /\\\\.scss$/i,
},
Object {
\\"oneOf\\": Array [
Object {
\\"resourceQuery\\": /module/,
\\"use\\": Array [
Object {
\\"loader\\": \\"vue-style-loader\\",
\\"options\\": Object {
\\"sourceMap\\": false,
},
},
Object {
\\"loader\\": \\"css-loader\\",
\\"options\\": Object {
\\"importLoaders\\": 2,
\\"modules\\": Object {
\\"localIdentName\\": \\"[local]_[hash:base64:5]\\",
},
\\"onlyLocals\\": false,
\\"sourceMap\\": false,
},
},
Object {
\\"loader\\": \\"postcss-loader\\",
\\"options\\": Object {
\\"order\\": \\"presetEnvAndCssnanoLast\\",
\\"plugins\\": Array [
[Function anonymous],
[Function anonymous],
[Function anonymous],
[Function anonymous],
],
\\"sourceMap\\": false,
},
},
Object {
\\"loader\\": \\"stylus-loader\\",
\\"options\\": Object {
\\"sourceMap\\": false,
},
},
],
},
Object {
\\"use\\": Array [
Object {
\\"loader\\": \\"vue-style-loader\\",
\\"options\\": Object {
\\"sourceMap\\": false,
},
},
Object {
\\"loader\\": \\"css-loader\\",
\\"options\\": Object {
\\"importLoaders\\": 2,
\\"onlyLocals\\": false,
\\"sourceMap\\": false,
},
},
Object {
\\"loader\\": \\"postcss-loader\\",
\\"options\\": Object {
\\"order\\": \\"presetEnvAndCssnanoLast\\",
\\"plugins\\": Array [
[Function anonymous],
[Function anonymous],
[Function anonymous],
[Function anonymous],
],
\\"sourceMap\\": false,
},
},
Object {
\\"loader\\": \\"stylus-loader\\",
\\"options\\": Object {
\\"sourceMap\\": false,
},
},
],
},
],
\\"test\\": /\\\\.styl(us)?$/i,
},
Object {
\\"test\\": /\\\\.(png|jpe?g|gif|svg|webp)$/i,
\\"use\\": Array [
Object {
\\"loader\\": \\"url-loader\\",
\\"options\\": Object {
\\"limit\\": 1000,
\\"name\\": \\"img/[contenthash:7].[ext]\\",
},
},
],
},
Object {
\\"test\\": /\\\\.(woff2?|eot|ttf|otf)(\\\\?.*)?$/i,
\\"use\\": Array [
Object {
\\"loader\\": \\"url-loader\\",
\\"options\\": Object {
\\"limit\\": 1000,
\\"name\\": \\"fonts/[contenthash:7].[ext]\\",
},
},
],
},
Object {
\\"test\\": /\\\\.(webm|mp4|ogv)$/i,
\\"use\\": Array [
Object {
\\"loader\\": \\"file-loader\\",
\\"options\\": Object {
\\"name\\": \\"videos/[contenthash:7].[ext]\\",
},
},
],
},
]
"
`;
exports[`webpack nuxt webpack module.rules loader=.*-loader 1`] = `
"Object {
\\"loader\\": \\"vue-loader\\",
\\"options\\": Object {
\\"productionMode\\": true,
\\"transformAssetUrls\\": Object {
\\"embed\\": \\"src\\",
\\"object\\": \\"src\\",
\\"source\\": \\"src\\",
\\"video\\": \\"src\\",
},
},
\\"test\\": /\\\\.vue$/i,
}
"
`;
exports[`webpack nuxt webpack module.rules loader=vue- 1`] = `
"Object {
\\"loader\\": \\"vue-loader\\",
\\"options\\": Object {
\\"productionMode\\": true,
\\"transformAssetUrls\\": Object {
\\"embed\\": \\"src\\",
\\"object\\": \\"src\\",
\\"source\\": \\"src\\",
\\"video\\": \\"src\\",
},
},
\\"test\\": /\\\\.vue$/i,
}
"
`;
exports[`webpack nuxt webpack module.rules test=.jsx 1`] = `
"Object {
\\"exclude\\": [Function exclude],
\\"test\\": /\\\\.jsx?$/i,
\\"use\\": Array [
Object {
\\"loader\\": \\"<nuxtDir>/node_modules/babel-loader/lib/index.js\\",
\\"options\\": Object {
\\"babelrc\\": false,
\\"cacheDirectory\\": false,
\\"configFile\\": false,
\\"envName\\": \\"client\\",
\\"presets\\": Array [
Array [
\\"<nuxtDir>/packages/babel-preset-app/src/index.js\\",
Object {},
],
],
},
},
],
}
"
`;
exports[`webpack nuxt webpack nuxt webpack module rules test=.pug oneOf use.0=raw 1`] = `
"Object {
\\"use\\": Array [
\\"raw-loader\\",
Object {
\\"loader\\": \\"pug-plain-loader\\",
\\"options\\": Object {},
},
],
}
"
`;
exports[`webpack nuxt webpack resolve alias 1`] = `
"Object {
\\"@\\": \\"<nuxtDir>/resolve\\",
\\"@@\\": \\"<nuxtDir>/resolve\\",
\\"assets\\": \\"<nuxtDir>/resolve/assets\\",
\\"static\\": \\"<nuxtDir>/resolve/static\\",
\\"vue-meta\\": \\"<nuxtDir>/node_modules/vue-meta/dist/vue-meta.esm.browser.js\\",
\\"~\\": \\"<nuxtDir>/resolve\\",
\\"~~\\": \\"<nuxtDir>/resolve\\",
}
"
`;

View File

@ -26,7 +26,7 @@ describe('cli', () => {
const nodeEnv = process.env.NODE_ENV
process.env.NODE_ENV = ''
getCommand.mockImplementationOnce(() => Promise.resolve({}))
getCommand.mockImplementationOnce(() => Promise.resolve({ run () { } }))
await run(['dev'])
expect(process.env.NODE_ENV).toBe('development')
@ -37,7 +37,7 @@ describe('cli', () => {
const nodeEnv = process.env.NODE_ENV
process.env.NODE_ENV = ''
getCommand.mockImplementationOnce(() => Promise.resolve({}))
getCommand.mockImplementationOnce(() => Promise.resolve({ run () { } }))
await run(['', '', 'build'])
expect(process.env.NODE_ENV).toBe('production')

View File

@ -0,0 +1,50 @@
import path from 'path'
import util from 'util'
import consola from 'consola'
import prettyFormat from 'pretty-format'
import { NuxtCommand, getWebpackConfig } from '../..'
import webpackCommand from '../../src/commands/webpack'
const replaceAll = (str, a, b) => str.split(a).join(b)
const nuxtDir = path.join(__dirname, '../../../..')
const maskDir = str => replaceAll(str, nuxtDir, '<nuxtDir>')
const tests = [
'devtool',
'resolve alias',
'module.rules',
'module.rules test=.jsx',
'module.rules loader=vue-',
'module.rules loader=.*-loader',
'nuxt webpack module rules test=.pug oneOf use.0=raw'
]
describe('webpack', () => {
beforeAll(() => {
process.stdout.isTTY = false
util.formatWithOptions = (opts, obj) => prettyFormat(obj)
})
afterEach(() => {
jest.resetAllMocks()
})
test.posix('getWebpackConfig()', async () => {
const webpackConfig = await getWebpackConfig('Client')
expect(maskDir(prettyFormat(webpackConfig.module.rules[0]))).toMatchSnapshot()
})
test.posix('nuxt webpack no match', async () => {
const cmd = NuxtCommand.from(webpackCommand, ['module.rules', 'loader=foobar'])
await cmd.run()
expect(maskDir(consola.warn.mock.calls[0][0])).toBe('No match in webpack config for `loader=foobar`')
})
for (const testCase of tests) {
test.posix('nuxt webpack ' + testCase, async () => {
const cmd = NuxtCommand.from(webpackCommand, testCase.split(' '))
await cmd.run()
expect(maskDir(consola.log.mock.calls[0][0])).toMatchSnapshot()
})
}
})

View File

@ -106,4 +106,6 @@ export function isIndexFileAndFolder (pluginFiles) {
return pluginFiles.some(isIndex)
}
export const getMainModule = () => require.main
export const getMainModule = () => {
return require.main || (module && module.main) || module
}

View File

@ -34,7 +34,7 @@ export class WebpackBundler {
}
getWebpackConfig (name) {
const Config = WebpackConfigs[name] // eslint-disable-line import/namespace
const Config = WebpackConfigs[name.toLowerCase()] // eslint-disable-line import/namespace
if (!Config) {
throw new Error(`Unsupported webpack config ${name}`)
}

View File

@ -1,3 +1,3 @@
export { default as Client } from './client'
export { default as Modern } from './modern'
export { default as Server } from './server'
export { default as client } from './client'
export { default as modern } from './modern'
export { default as server } from './server'