diff --git a/package.json b/package.json index 76cdf5abde..b111a14b02 100644 --- a/package.json +++ b/package.json @@ -8,8 +8,8 @@ "scripts": { "link": "lerna link", "nuxt": "jiti ./scripts/nuxt", - "build": "yarn workspaces run build", - "stub": "yarn workspaces run stub", + "build": "yarn workspaces run build --silent", + "stub": "yarn workspaces run stub --silent", "play": "yarn nuxt dev playground", "lint": "eslint --ext .vue,.ts,.js .", "test": "yarn lint", diff --git a/packages/kit/package.json b/packages/kit/package.json new file mode 100644 index 0000000000..6dcc1406b1 --- /dev/null +++ b/packages/kit/package.json @@ -0,0 +1,32 @@ +{ + "name": "@nuxt/kit", + "version": "0.0.0", + "repository": "nuxt/framework", + "license": "MIT", + "main": "./dist/index.js", + "files": [ + "dist" + ], + "scripts": { + "build": "jiti ../../scripts/build .", + "genconfig": "jiti ./scripts/genconfig", + "stub": "yarn build --stub", + "prepublishOnly": "yarn build && yarn genconfig" + }, + "dependencies": { + "create-require": "^1.1.1", + "defu": "^3.2.2", + "dotenv": "^8.2.0", + "jiti": "^1.6.4", + "rc9": "^1.2.0", + "scule": "^0.1.1", + "std-env": "^2.3.0", + "ufo": "^0.6.10", + "untyped": "^0.2.2" + }, + "build": { + "entries": { + "index": {} + } + } +} diff --git a/packages/kit/scripts/genconfig.ts b/packages/kit/scripts/genconfig.ts new file mode 100644 index 0000000000..63d96caf25 --- /dev/null +++ b/packages/kit/scripts/genconfig.ts @@ -0,0 +1,19 @@ +import { resolve } from 'path' +import { mkdir, writeFile } from 'fs/promises' +import { resolveSchema, generateTypes, generateMarkdown } from 'untyped' + +async function main () { + const genDir = resolve(__dirname, '../dist/config') + const srcConfig = await import('../src/config/schema').then(r => r.default) + + const defaults = { rootDir: '//' } + const schema = resolveSchema(srcConfig, defaults) + + await mkdir(genDir).catch(() => { }) + await writeFile(resolve(genDir, 'config.md'), generateMarkdown(schema)) + await writeFile(resolve(genDir, 'schema.json'), JSON.stringify(schema, null, 2)) + await writeFile(resolve(genDir, 'defaults.json'), JSON.stringify(defaults, null, 2)) + await writeFile(resolve(genDir, 'config.d.ts'), 'export default ' + generateTypes(schema, 'Config')) +} + +main().catch(console.error) diff --git a/packages/kit/src/config/env.ts b/packages/kit/src/config/env.ts new file mode 100644 index 0000000000..5d278a1c90 --- /dev/null +++ b/packages/kit/src/config/env.ts @@ -0,0 +1,97 @@ +import { resolve } from 'path' +import { existsSync, promises as fsp } from 'fs' +import dotenv from 'dotenv' + +export interface LoadDotEnvOptions { + rootDir: string + dotenvFile: string + expand: boolean + env: typeof process.env +} + +export async function loadEnv (rootDir) { + // Load env + const env = await loalDotenv({ + rootDir, + dotenvFile: '.env', + env: process.env, + expand: true + }) + + // Fill process.env so it is accessible in nuxt.config + for (const key in env) { + if (!key.startsWith('_') && process.env[key] === undefined) { + process.env[key] = env[key] + } + } +} + +export async function loalDotenv (opts: LoadDotEnvOptions) { + const env = Object.create(null) + + const dotenvFile = resolve(opts.rootDir, opts.dotenvFile) + + if (await existsSync(dotenvFile)) { + const parsed = dotenv.parse(await fsp.readFile(dotenvFile, 'utf-8')) + Object.assign(env, parsed) + } + + // Apply process.env + if (!opts.env._applied) { + Object.assign(env, opts.env) + env._applied = true + } + + // Interpolate env + if (opts.expand) { + expand(env) + } + + return env +} + +// Based on https://github.com/motdotla/dotenv-expand +function expand (target, source = {}, parse = v => v) { + function getValue (key) { + // Source value 'wins' over target value + return source[key] !== undefined ? source[key] : target[key] + } + + function interpolate (value, parents = []) { + if (typeof value !== 'string') { + return value + } + const matches = value.match(/(.?\${?(?:[a-zA-Z0-9_:]+)?}?)/g) || [] + return parse(matches.reduce((newValue, match) => { + const parts = /(.?)\${?([a-zA-Z0-9_:]+)?}?/g.exec(match) + const prefix = parts[1] + + let value, replacePart + + if (prefix === '\\') { + replacePart = parts[0] + value = replacePart.replace('\\$', '$') + } else { + const key = parts[2] + replacePart = parts[0].substring(prefix.length) + + // Avoid recursion + if (parents.includes(key)) { + console.warn(`Please avoid recursive environment variables ( loop: ${parents.join(' > ')} > ${key} )`) + return '' + } + + value = getValue(key) + + // Resolve recursive interpolations + value = interpolate(value, [...parents, key]) + } + + return value !== undefined ? newValue.replace(replacePart, value) : newValue + }, value)) + } + + for (const key in target) { + target[key] = interpolate(getValue(key)) + } +} diff --git a/packages/kit/src/config/load.ts b/packages/kit/src/config/load.ts new file mode 100644 index 0000000000..d605a5c029 --- /dev/null +++ b/packages/kit/src/config/load.ts @@ -0,0 +1,57 @@ +import { resolve } from 'path' +import defu from 'defu' +import jiti from 'jiti' +import { applyDefaults } from 'untyped' +import * as rc from 'rc9' +import nuxtConfigSchema from './schema' + +export interface LoadNuxtConfigOptions { + rootDir?: string + configFile?: string + config?: any +} + +export function loadNuxtConfig (opts: LoadNuxtConfigOptions) { + const rootDir = resolve(process.cwd(), opts.rootDir || '.') + + const _require = jiti(rootDir) + + let configFile + try { + configFile = _require.resolve(resolve(rootDir, opts.configFile || 'nuxt.config')) + } catch (e) { + if (e.code !== 'MODULE_NOT_FOUND') { + throw (e) + } + // Skip if cannot resolve + configFile = undefined + } + + let nuxtConfig: any = {} + + if (configFile) { + // clearRequireCache(configFile) TODO + nuxtConfig = _require(configFile) || {} + if (nuxtConfig.default) { + nuxtConfig = nuxtConfig.default + } + nuxtConfig = { ...nuxtConfig } + } + + // Combine configs + // Priority: configOverrides > nuxtConfig > .nuxtrc > .nuxtrc (global) + nuxtConfig = defu( + opts.config, + nuxtConfig, + rc.read({ name: '.nuxtrc', dir: rootDir }), + rc.readUser('.nuxtrc') + ) + + // Set rootDir + if (!nuxtConfig.rootDir) { + nuxtConfig.rootDir = rootDir + } + + // Resolve and apply defaults + return applyDefaults(nuxtConfigSchema, nuxtConfig) +} diff --git a/packages/kit/src/config/schema/_app.ts b/packages/kit/src/config/schema/_app.ts new file mode 100644 index 0000000000..3a2394302b --- /dev/null +++ b/packages/kit/src/config/schema/_app.ts @@ -0,0 +1,141 @@ +import { resolve, join } from 'path' +import { existsSync, readdirSync } from 'fs' +import defu from 'defu' +import { isRelative, joinURL, hasProtocol } from 'ufo' + +export default { + /** + * Vue.js configuration + */ + vue: { + config: { + silent: { $resolve: (val, get) => val ?? get('dev') }, + performance: { $resolve: (val, get) => val ?? get('dev') } + } + }, + + app: { + $resolve: (val, get) => { + const useCDN = hasProtocol(get('build.publicPath'), true) && !get('dev') + const isRelativePublicPath = isRelative(get('build.publicPath')) + return defu(val, { + basePath: get('router.base'), + assetsPath: isRelativePublicPath ? get('build.publicPath') : useCDN ? '/' : joinURL(get('router.base'), get('build.publicPath')), + cdnURL: useCDN ? get('build.publicPath') : null + }) + } + }, + + /** + * Uses {srcDir}/app.html if exists by default otherwise nuxt default + */ + appTemplatePath: { + $resolve: (val, get) => { + if (val) { + return resolve(get('srcDir'), val) + } + if (existsSync(join(get('srcDir'), 'app.html'))) { + return join(get('srcDir'), 'app.html') + } + return resolve(get('buildDir'), 'views/app.template.html') + } + }, + + store: { + $resolve: (val, get) => val !== false && + existsSync(join(get('srcDir'), get('dir.store'))) && + readdirSync(join(get('srcDir'), get('dir.store'))) + .find(filename => filename !== 'README.md' && filename[0] !== '.') + }, + + /** + * debug errorss + */ + debug: { + $resolve: (val, get) => val ?? get('dev') + }, + + vueMeta: null, + + head: { + meta: [], + link: [], + style: [], + script: [] + }, + + fetch: { + server: true, + client: true + }, + + plugins: [], + + extendPlugins: null, + + css: [], + + layouts: {}, + + ErrorPage: null, + + loading: { + color: 'black', + failedColor: 'red', + height: '2px', + throttle: 200, + duration: 5000, + continuous: false, + rtl: false, + css: true + }, + + loadingIndicator: { + $resolve: (val, get) => { + if (typeof val === 'string') { + val = { name: val } + } + return { + name: 'default', + color: get('loading.color') || '#D3D3D3', + color2: '#F5F5F5', + background: (get('manifest') && get('manifest.theme_color')) || 'white', + dev: get('dev'), + loading: get('messages.loading'), + ...val + } + } + }, + + pageTransition: { + $resolve: val => typeof val === 'string' ? { name: val } : val, + name: 'page', + mode: 'out-in', + appear: { $resolve: (val, get) => (get('render.ssr') === false) ? true : Boolean(val) }, + appearClass: 'appear', + appearActiveClass: 'appear-active', + appearToClass: 'appear-to' + }, + + layoutTransition: { + $resolve: val => typeof val === 'string' ? { name: val } : val, + name: 'layout', + mode: 'out-in' + }, + + features: { + store: true, + layouts: true, + meta: true, + middleware: true, + transitions: true, + deprecations: true, + validate: true, + asyncData: true, + fetch: true, + clientOnline: true, + clientPrefetch: true, + componentAliases: true, + componentClientOnly: true + } +} diff --git a/packages/kit/src/config/schema/_common.ts b/packages/kit/src/config/schema/_common.ts new file mode 100644 index 0000000000..4ad783514f --- /dev/null +++ b/packages/kit/src/config/schema/_common.ts @@ -0,0 +1,166 @@ +import { join, resolve } from 'path' +import env from 'std-env' +import createRequire from 'create-require' +import { pascalCase } from 'scule' +import jiti from 'jiti' + +export default { + rootDir: { + $resolve: val => typeof val === 'string' ? resolve(val) : process.cwd() + }, + + srcDir: { + $resolve: (val, get) => resolve(get('rootDir'), val || '.') + }, + + buildDir: { + $resolve: (val, get) => resolve(get('rootDir'), val || '.nuxt') + }, + + dev: Boolean(env.dev), + test: Boolean(env.test), + debug: undefined, + env: { + $resolve: (val) => { + val = { ...val } + for (const key in process.env) { + if (key.startsWith('NUXT_ENV_')) { + val[key] = process.env[key] + } + } + return val + } + }, + + createRequire: { + $resolve: (val: any) => { + val = process.env.NUXT_CREATE_REQUIRE || val || + (typeof jest !== 'undefined' ? 'native' : 'jiti') + if (val === 'jiti') { + return p => jiti(typeof p === 'string' ? p : p.filename) + } + if (val === 'native') { + return p => createRequire(typeof p === 'string' ? p : p.filename) + } + return val + } + }, + + target: { + $resolve: val => ['server', 'static'].includes(val) ? val : 'server' + }, + + ssr: true, + + mode: { + $resolve: (val, get) => val || (get('ssr') ? 'spa' : 'universal'), + $schema: { deprecated: '`mode` option is deprecated' } + }, + + modern: undefined, + + modules: [], + buildModules: [], + _modules: [], + + globalName: { + $resolve: val => (typeof val === 'string' && /^[a-zA-Z]+$/.test(val)) ? val.toLocaleLowerCase() : 'nuxt' + }, + + globals: { + id: globalName => `__${globalName}`, + nuxt: globalName => `$${globalName}`, + context: globalName => `__${globalName.toUpperCase()}__`, + pluginPrefix: globalName => globalName, + readyCallback: globalName => `on${pascalCase(globalName)}Ready`, + loadedCallback: globalName => `_on${pascalCase(globalName)}Loaded` + }, + + serverMiddleware: { + $resolve: (val: any) => { + if (!val) { + return [] + } + if (!Array.isArray(val)) { + return Object.entries(val).map(([path, handler]) => ({ path, handler })) + } + return val + } + }, + + _nuxtConfigFile: { + $resolve: (val, get) => resolve(get('rootDir'), val || 'nuxt.config.js') + }, + + _nuxtConfigFiles: { + $resolve: (val, get) => [].concat(get('_nuxtConfigFile'), val).filter(Boolean) + }, + + modulesDir: { + $default: ['node_modules'], + $resolve: (val, get) => val.map(dir => resolve(get('rootDir'), dir)) + }, + + dir: { + assets: 'assets', + app: 'app', + layouts: 'layouts', + middleware: 'middleware', + pages: 'pages', + static: 'static', + store: 'store' + }, + + extensions: { + $resolve: val => ['js', 'mjs', 'ts', 'tsx', 'vue'].concat(val).filter(Boolean) + }, + + styleExtensions: ['css', 'pcss', 'postcss', 'styl', 'stylus', 'scss', 'sass', 'less'], + + alias: { + $resolve: (val, get) => ({ + '~~': get('rootDir'), + '@@': get('rootDir'), + '~': get('srcDir'), + '@': get('srcDir'), + [get('dir.assets')]: join(get('srcDir'), get('dir.assets')), + [get('dir.static')]: join(get('srcDir', get('dir.static'))), + ...val + }) + }, + + ignoreOptions: undefined, + ignorePrefix: '-', + ignore: { + $resolve: (val, get) => [ + '**/*.test.*', + '**/*.spec.*', + get('ignorePrefix') && `**/${get('ignorePrefix')}*.*` + ].concat(val).filter(Boolean) + }, + + watch: { + $resolve: (_val, get) => [].concat(get._nuxtConfigFiles).filter(Boolean) + }, + + watchers: { + rewatchOnRawEvents: undefined, + webpack: { + aggregateTimeout: 1000 + }, + chokidar: { + ignoreInitial: true + } + }, + + editor: undefined, + + hooks: null, + + privateRuntimeConfig: {}, + publicRuntimeConfig: { + app: { + $resolve: (val, get) => ({ ...get('app'), ...(val || {}) }) + } + } +} diff --git a/packages/kit/src/config/schema/build.ts b/packages/kit/src/config/schema/build.ts new file mode 100644 index 0000000000..1f069dd3e9 --- /dev/null +++ b/packages/kit/src/config/schema/build.ts @@ -0,0 +1,159 @@ +import env from 'std-env' +import { hasProtocol } from 'ufo' + +export default { + quiet: Boolean(env.ci || env.test), + analyze: false, + profile: process.argv.includes('--profile'), + extractCSS: false, + cssSourceMap: { + $resolve: (val, get) => val ?? get('dev') + }, + ssr: undefined, + parallel: { + $resolve: (val, get) => get('build.extractCSS') ? false : Boolean(val) + }, + cache: false, + standalone: false, + publicPath: { + $resolve: (val, get) => { + if (hasProtocol(val, true) && get('dev')) { val = null } + return (val || '/_nuxt/').replace(/([^/])$/, '$1/') + } + }, + serverURLPolyfill: 'url', + filenames: { + app: ({ isDev, isModern }) => isDev ? `[name]${isModern ? '.modern' : ''}.js` : `[contenthash:7]${isModern ? '.modern' : ''}.js`, + chunk: ({ isDev, isModern }) => isDev ? `[name]${isModern ? '.modern' : ''}.js` : `[contenthash:7]${isModern ? '.modern' : ''}.js`, + css: ({ isDev }) => isDev ? '[name].css' : 'css/[contenthash:7].css', + img: ({ isDev }) => isDev ? '[path][name].[ext]' : 'img/[name].[contenthash:7].[ext]', + font: ({ isDev }) => isDev ? '[path][name].[ext]' : 'fonts/[name].[contenthash:7].[ext]', + video: ({ isDev }) => isDev ? '[path][name].[ext]' : 'videos/[name].[contenthash:7].[ext]' + }, + loaders: { + // $resolve: (val, get) => { + // const styleLoaders = [ + // 'css', 'cssModules', 'less', + // 'sass', 'scss', 'stylus', 'vueStyle' + // ] + // for (const name of styleLoaders) { + // const loader = val[name] + // if (loader && loader.sourceMap === undefined) { + // loader.sourceMap = Boolean(get('build.cssSourceMap')) + // } + // } + // }, + file: { esModule: false }, + fontUrl: { esModule: false, limit: 1000 }, + imgUrl: { esModule: false, limit: 1000 }, + pugPlain: {}, + vue: { + productionMode: { $resolve: (val, get) => val ?? get('dev') }, + transformAssetUrls: { + video: 'src', + source: 'src', + object: 'src', + embed: 'src' + } + }, + css: { esModule: false }, + cssModules: { + esModule: false, + modules: { + localIdentName: '[local]_[hash:base64:5]' + } + }, + less: {}, + sass: { + sassOptions: { + indentedSyntax: true + } + }, + scss: {}, + stylus: {}, + vueStyle: {} + }, + styleResources: {}, + plugins: [], + terser: {}, + hardSource: false, + aggressiveCodeRemoval: false, + optimizeCSS: { + $resolve: (val, get) => val ?? (get('build.extractCSS') ? {} : false) + }, + optimization: { + runtimeChunk: 'single', + minimize: { $resolve: (val, get) => val ?? get('dev') }, + minimizer: undefined, + splitChunks: { + chunks: 'all', + automaticNameDelimiter: '/', + cacheGroups: {} + } + }, + splitChunks: { + layouts: false, + pages: true, + commons: true + }, + corejs: 'auto', + babel: { + configFile: false, + babelrc: false, + presets: {}, + cacheDirectory: { + $resolve: (val, get) => val ?? get('dev') + } + }, + transpile: { + $resolve: val => [].concat(val).filter(Boolean) + }, + postcss: { + preset: { + // https://cssdb.org/#staging-process + stage: 2 + } + }, + html: { + minify: { + collapseBooleanAttributes: true, + decodeEntities: true, + minifyCSS: true, + minifyJS: true, + processConditionalComments: true, + removeEmptyAttributes: true, + removeRedundantAttributes: true, + trimCustomFragments: true, + useShortDoctype: true + } + }, + + template: undefined, + templates: [], + + watch: [], + devMiddleware: { + stats: 'none' + }, + hotMiddleware: {}, + + vendor: { + $meta: { + deprecated: 'vendor has been deprecated since nuxt 2' + } + }, + + stats: { + $resolve: (val, get) => (val === 'none' || get('build.quite')) ? false : val, + excludeAssets: [ + /.map$/, + /index\..+\.html$/, + /vue-ssr-(client|modern)-manifest.json/ + ] + }, + friendlyErrors: true, + additionalExtensions: [], + warningIgnoreFilters: [], + + followSymlinks: false +} diff --git a/packages/kit/src/config/schema/cli.ts b/packages/kit/src/config/schema/cli.ts new file mode 100644 index 0000000000..0e79115bd1 --- /dev/null +++ b/packages/kit/src/config/schema/cli.ts @@ -0,0 +1,4 @@ +export default { + badgeMessages: [], + bannerColor: 'green' +} diff --git a/packages/kit/src/config/schema/generate.ts b/packages/kit/src/config/schema/generate.ts new file mode 100644 index 0000000000..a9caa545ba --- /dev/null +++ b/packages/kit/src/config/schema/generate.ts @@ -0,0 +1,29 @@ +import { resolve } from 'path' +import { joinURL } from 'ufo' + +export default { + dir: { + $resolve: (val = 'dist', get) => resolve(get('rootDir'), val) + }, + routes: [], + exclude: [], + concurrency: 500, + interval: 0, + subFolders: true, + fallback: { $resolve: val => val === true ? '400.html' : (val || '200.html') }, + crawler: true, + manifest: true, + nojekyll: true, + cache: { + ignore: [], + globbyOptions: { + gitignore: true + } + }, + staticAssets: { + dir: 'static', + base: { $resolve: (val, get) => val || joinURL(get('app.assetsPath'), get('generate.dir')) }, + versionBase: { $resolve: (val, get) => val || joinURL(get('generate.base'), get('generate.version')) }, + version: { $resolve: val => val || (String(Math.round(Date.now() / 1000))) } + } +} diff --git a/packages/kit/src/config/schema/index.ts b/packages/kit/src/config/schema/index.ts new file mode 100644 index 0000000000..5e24730c3e --- /dev/null +++ b/packages/kit/src/config/schema/index.ts @@ -0,0 +1,40 @@ + +import _app from './_app' +import _common from './_common' +import build from './build' +import messages from './messages' +import render from './render' +import router from './router' +import server from './server' +import cli from './cli' +import generate from './generate' + +/* +TODO for top level normalizations: (nuxt2) +- transition => pageTransition +- export => generate +- gzip => compressor +- Apply preset +- render.etag.hash should be a function +- deprecated devModules +- set consola level to 0 if build.quite is true +- Ad-hoc: loading-screen, components and telemtry +- build.indicator and build.loadingScreen +- build.crossorigin => render.crossorigin +- render.csp.unsafeInlineCompatiblity => render.csp.unsafeInlineCompatibility +- guards: rootDir:buildDir rootDir:generate.dir srcDir:buildDir srcDir:generate.dir +- _publicPath (original value of publicPath) +- options.build.babel.presets (array) warn @nuxtjs/babel-preset-app => @nuxt/babel-preset-app +*/ + +export default { + ..._app, + ..._common, + build, + messages, + render, + router, + server, + cli, + generate +} diff --git a/packages/kit/src/config/schema/messages.ts b/packages/kit/src/config/schema/messages.ts new file mode 100644 index 0000000000..ad1ff51de1 --- /dev/null +++ b/packages/kit/src/config/schema/messages.ts @@ -0,0 +1,10 @@ +export default { + loading: 'Loading...', + error_404: 'This page could not be found', + server_error: 'Server error', + nuxtjs: 'Nuxt', + back_to_home: 'Back to the home page', + server_error_details: 'An error occurred in the application and your page could not be served. If you are the application owner, check your logs for details.', + client_error: 'Error', + client_error_details: 'An error occurred while rendering the page. Check developer tools console for details.' +} diff --git a/packages/kit/src/config/schema/render.ts b/packages/kit/src/config/schema/render.ts new file mode 100644 index 0000000000..2c09cfc65b --- /dev/null +++ b/packages/kit/src/config/schema/render.ts @@ -0,0 +1,58 @@ +export default { + bundleRenderer: { + shouldPrefetch: () => false, + shouldPreload: (_fileWithoutQuery, asType) => ['script', 'style'].includes(asType), + /** + * enabled by default for development + */ + runInNewContext: { $resolve: (val, get) => val ?? get('dev') } + }, + crossorigin: undefined, + resourceHints: true, + ssr: undefined, + ssrLog: { $resolve: (val, get) => get('dev') ? Boolean(val) : false }, + http2: { + push: false, + shouldPush: null, + pushAssets: null + }, + static: { + prefix: true + }, + compressor: { + threshold: 0 + }, + etag: { + hash: false, + weak: false + }, + csp: { + $resolve: (val, get) => { + if (!val) { return false } + return { + hashAlgorithm: 'sha256', + allowedSources: undefined, + policies: undefined, + addMeta: Boolean(get('target') === 'static'), + unsafeInlineCompatibility: false, + reportOnly: get('debug'), + ...val + } + } + }, + dist: { + index: false, + maxAge: '1y' + }, + // https://github.com/nuxt/serve-placeholder + fallback: { + dist: {}, + static: { + skipUnknown: true, + handlers: { + '.htm': false, + '.html': false + } + } + } +} diff --git a/packages/kit/src/config/schema/router.ts b/packages/kit/src/config/schema/router.ts new file mode 100644 index 0000000000..2db41b6bbb --- /dev/null +++ b/packages/kit/src/config/schema/router.ts @@ -0,0 +1,31 @@ +import { normalizeURL, withTrailingSlash } from 'ufo' + +export default { + mode: 'history', + base: { + $resolve: (val = '/') => withTrailingSlash(normalizeURL(val)) + }, + _routerBaseSpecified: { + $resolve: (_val, get) => typeof get('router.base') === 'string' + }, + routes: [], + routeNameSplitter: '-', + middleware: { + $resolve: val => Array.isArray(val) ? val : [val].filter(Boolean) + }, + linkActiveClass: 'nuxt-link-active', + linkExactActiveClass: 'nuxt-link-exact-active', + linkPrefetchedClass: false, + extendRoutes: null, + scrollBehavior: { + $schema: { + deprecated: 'router.scrollBehavior` property is deprecated in favor of using `~/app/router.scrollBehavior.js` file, learn more: https://nuxtjs.org/api/configuration-router#scrollbehavior' + } + }, + parseQuery: false, + stringifyQuery: false, + fallback: false, + prefetchLinks: true, + prefetchPayloads: true, + trailingSlash: undefined +} diff --git a/packages/kit/src/config/schema/server.ts b/packages/kit/src/config/schema/server.ts new file mode 100644 index 0000000000..a476ab909a --- /dev/null +++ b/packages/kit/src/config/schema/server.ts @@ -0,0 +1,7 @@ +export default { + https: false, + port: process.env.NUXT_PORT || process.env.PORT || process.env.npm_package_config_nuxt_port || 3000, + host: process.env.NUXT_HOST || process.env.HOST || process.env.npm_package_config_nuxt_host || 'localhost', + socket: process.env.UNIX_SOCKET || process.env.npm_package_config_unix_socket, + timing: (val: any) => val ? ({ total: true, ...val }) : false +} diff --git a/packages/kit/src/index.ts b/packages/kit/src/index.ts new file mode 100644 index 0000000000..95d535bbc5 --- /dev/null +++ b/packages/kit/src/index.ts @@ -0,0 +1,2 @@ +export * from './config/load' +export * from './config/env' diff --git a/packages/nuxt3/src/config/config/_app.ts b/packages/nuxt3/src/config/config/_app.ts deleted file mode 100644 index 903ff8cf48..0000000000 --- a/packages/nuxt3/src/config/config/_app.ts +++ /dev/null @@ -1,172 +0,0 @@ -import type { App } from 'vue' -import type { MetaInfo, VueMetaOptions } from 'vue-meta' - -type Plugin = string | { mode?: 'all' | 'client' | 'server', src: string, ssr?: boolean } - -interface AppOptions { - css: string[] - head: MetaInfo | (() => MetaInfo) - ErrorPage: null | string - extendPlugins: null | ((plugins: Plugin[]) => Plugin[]) - features: { - store: boolean - layouts: boolean - meta: boolean - middleware: boolean - transitions: boolean - deprecations: boolean - validate: boolean - asyncData: boolean - fetch: boolean - clientOnline: boolean - clientPrefetch: boolean - clientUseUrl: boolean - componentAliases: boolean - componentClientOnly: boolean - } - fetch: { - server: boolean - client: boolean - } - layouts: {} - layoutTransition: { - name: string - mode?: string | 'out-in' - } - loading: string | false | { - color?: string - continuous?: boolean - css?: boolean - duration?: number - failedColor?: string - height?: string - rtl?: boolean - throttle?: number - } - loadingIndicator: string | false | { - background?: string - color?: string - color2?: string - name?: string - } - pageTransition: { - name: string - mode?: string | 'out-in' - appear?: boolean - appearClass?: string - appearActiveClass?: string - appearToClass?: string - } - plugins: Array - vue: { - config: Partial - } - vueMeta: null | VueMetaOptions -} - -export default (): AppOptions => ({ - vue: { - config: { - performance: undefined // = dev - } - }, - - vueMeta: null, - - head: { - meta: [], - link: [], - style: [], - script: [] - }, - - fetch: { - server: true, - client: true - }, - - plugins: [], - - extendPlugins: null, - - css: [], - - layouts: {}, - - ErrorPage: null, - - loading: { - color: 'black', - failedColor: 'red', - height: '2px', - throttle: 200, - duration: 5000, - continuous: false, - rtl: false, - css: true - }, - - loadingIndicator: 'default', - - pageTransition: { - name: 'page', - mode: 'out-in', - appear: false, - appearClass: 'appear', - appearActiveClass: 'appear-active', - appearToClass: 'appear-to' - }, - - layoutTransition: { - name: 'layout', - mode: 'out-in' - }, - - features: { - store: true, - layouts: true, - meta: true, - middleware: true, - transitions: true, - deprecations: true, - validate: true, - asyncData: true, - fetch: true, - clientOnline: true, - clientPrefetch: true, - clientUseUrl: false, - componentAliases: true, - componentClientOnly: true - } -}) - -// type NormalizedConfiguration> = T & { -// pageTransition?: Exclude -// layoutTransition?: Exclude -// extensions?: Exclude -// } - -// export function normalizeAppConfig(options: O): asserts options is NormalizedConfiguration { -// (options as NormalizedConfiguration).__normalized__ = true - -// // Normalize options -// if (options.loading === true) { -// delete options.loading -// } - -// if (options.router && typeof options.router.base === 'string') { -// (options as NormalizedConfiguration)._routerBaseSpecified = true -// } - -// if (typeof options.pageTransition === 'string') { -// options.pageTransition = { name: options.pageTransition } -// } - -// if (typeof options.layoutTransition === 'string') { -// options.layoutTransition = { name: options.layoutTransition } -// } - -// if (typeof options.extensions === 'string') { -// options.extensions = [options.extensions] -// } -// } diff --git a/packages/nuxt3/src/config/config/_common.ts b/packages/nuxt3/src/config/config/_common.ts deleted file mode 100644 index 1f861c7888..0000000000 --- a/packages/nuxt3/src/config/config/_common.ts +++ /dev/null @@ -1,221 +0,0 @@ -import type { WatchOptions as ChokidarWatchOptions } from 'chokidar' -import type express from 'express' -import type { configHooksT } from 'hookable' -import ignore from 'ignore' -import capitalize from 'lodash/capitalize' -import env from 'std-env' -import type { Configuration as WebpackConfiguration } from 'webpack' -import Hookable from 'hookable' -import { TARGETS, MODES, Target, Mode } from '../../utils' - -import { APP_DIR } from '../../consts' -import type { NormalizedConfiguration } from '../options' - -type IgnoreOptions = Parameters[0] -type IgnoreInstance = ReturnType - -interface ExtendFunctionContext { - isClient: boolean - isDev: boolean - isLegacy: boolean - isModern: boolean - isServer: boolean - // TODO - // loaders: NuxtOptionsLoaders -} - -type ExtendFunction = (config: WebpackConfiguration, ctx: ExtendFunctionContext) => void - -interface NuxtHooks extends configHooksT { - - build?: { - before?(builder: any, buildOptions: any): void - compile?(params: { name: 'client' | 'server', compiler: any }): void - compiled?(params: { name: 'client' | 'server', compiler: any, stats: any }): void - done?(builder: any): void - extendRoutes?(routes: any, resolve: any): void - templates?(params: { templateFiles: any, templateVars: any, resolve: any }): void - } - close?(nuxt: any): void - error?(error: Error): void - generate?: { - before?(generator: any, generateOptions: any): void - distCopied?(generator: any): void - distRemoved?(generator: any): void - done?(generator: any): void - extendRoutes?(routes: any): void - page?(params: { route: any, path: any, html: any }): void - routeCreated?(route: any, path: any, errors: any): void - routeFailed?(route: any, errors: any): void - } - listen?(server: any, params: { host: string, port: number | string }): void - modules?: { - before?(moduleContainer: any, options: any): void - done?(moduleContainer: any): void - } - ready?(nuxt: any): void - render?: { - before?(renderer: any, options: any): void - done?(renderer: any): void - errorMiddleware?(app: express.Application): void - resourcesLoaded?(resources: any): void - route?(url: string, result: any, context: any): void - routeContext?(context: any): void - routeDone?(url: string, result: any, context: any): void - beforeResponse?(url: string, result: any, context: any): void - setupMiddleware?(app: express.Application): void - } -} - -interface ModuleThis { - extendBuild(fn: ExtendFunction): void - options: NormalizedConfiguration - nuxt: any // TBD - [key: string]: any // TBD -} - -export type ModuleHandler = (this: ModuleThis, moduleOptions: T) => Promise | void - -export type NuxtModule = string | ModuleHandler | [string | ModuleHandler, any] - -export type ServerMiddleware = string | { path: string, prefix?: boolean, handler: string | express.NextFunction } | express.NextFunction - -interface CommonConfiguration { - _majorVersion: Number - _modules: NuxtModule[] - _nuxtConfigFile?: string - alias: Record - appDir: string, - buildDir: string, - vite: boolean, - buildModules: NuxtModule[] - createRequire?: (module: NodeJS.Module) => NodeJS.Require - debug?: boolean - dev: boolean - dir: { [key in 'app' | 'assets' | 'layouts' | 'middleware' | 'pages' | 'static' | 'store']: string } - editor: undefined - env: NodeJS.ProcessEnv - extensions: string[] - globalName?: string, - globals: { - id: (globalName: string) => string - nuxt: (globalName: string) => string - context: (globalName: string) => string - pluginPrefix: (globalName: string) => string - readyCallback: (globalName: string) => string - loadedCallback: (globalName: string) => string - } - hooks: null | ((hook: Hookable['hook']) => void) | NuxtHooks - ignoreOptions?: IgnoreOptions - ignorePrefix: string - ignore: Array - // TODO: remove in Nuxt 3 - mode: Mode - modern?: boolean | 'client' | 'server' - modules: NuxtModule[] - privateRuntimeConfig: Record | ((env: NodeJS.ProcessEnv) => Record) - publicRuntimeConfig: Record | ((env: NodeJS.ProcessEnv) => Record) - serverMiddleware: Array | Record - ssr: boolean - target: Target - test: boolean - srcDir?: string - modulesDir: string[] - styleExtensions: string[] - watch: string[] - watchers: { - webpack: WebpackConfiguration['watchOptions'] - chokidar: ChokidarWatchOptions - } -} - -export default (): CommonConfiguration => ({ - _majorVersion: undefined, - // Env - dev: Boolean(env.dev), - test: Boolean(env.test), - debug: undefined, // = dev - env: {}, - - createRequire: undefined, - - // Target - target: TARGETS.server, - - // Rendering - ssr: true, - - // TODO: remove in Nuxt 3 - // Mode - mode: MODES.universal, - modern: undefined, - - // Modules - modules: [], - buildModules: [], - _modules: [], - - globalName: undefined, - globals: { - id: globalName => `__${globalName}`, - nuxt: globalName => `$${globalName}`, - context: globalName => `__${globalName.toUpperCase()}__`, - pluginPrefix: globalName => globalName, - readyCallback: globalName => `on${capitalize(globalName)}Ready`, - loadedCallback: globalName => `_on${capitalize(globalName)}Loaded` - }, - - // Server - serverMiddleware: [], - - // Dirs and extensions - _nuxtConfigFile: undefined, - srcDir: undefined, - buildDir: '.nuxt', - vite: false, - modulesDir: [ - 'node_modules' - ], - appDir: APP_DIR, - dir: { - assets: 'assets', - app: 'app', - layouts: 'layouts', - middleware: 'middleware', - pages: 'pages', - static: 'static', - store: 'store' - }, - extensions: [], - styleExtensions: ['css', 'pcss', 'postcss', 'styl', 'stylus', 'scss', 'sass', 'less'], - alias: {}, - - // Ignores - ignoreOptions: undefined, - ignorePrefix: '_', - ignore: [ - '**/*.test.*', - '**/*.spec.*' - ], - - // Watch - watch: [], - watchers: { - webpack: { - aggregateTimeout: 1000 - }, - chokidar: { - ignoreInitial: true - } - }, - - // Editor - editor: undefined, - - // Hooks - hooks: null, - - // runtimeConfig - privateRuntimeConfig: {}, - publicRuntimeConfig: {} -}) diff --git a/packages/nuxt3/src/config/config/build.ts b/packages/nuxt3/src/config/config/build.ts deleted file mode 100644 index 6a44cf0cbc..0000000000 --- a/packages/nuxt3/src/config/config/build.ts +++ /dev/null @@ -1,369 +0,0 @@ -import env from 'std-env' -import type { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer' - -import type { TransformOptions, PluginItem } from '@babel/core' -import type { Options as AutoprefixerOptions } from 'autoprefixer' -import type { Options as FileLoaderOptions } from 'file-loader' -import type { Options as HtmlMinifierOptions } from 'html-minifier' -import type * as Less from 'less' -import type { Options as SassOptions } from 'sass' -import type { Plugin as PostcssPlugin } from 'postcss' -import type { Options as PugOptions } from 'pug' -import type { TerserPluginOptions } from 'terser-webpack-plugin' -import type { VueLoaderOptions } from 'vue-loader' -import type { - Configuration as WebpackConfiguration, WebpackPluginFunction - -} from 'webpack' -import type { Options as WebpackDevMiddlewareOptions } from 'webpack-dev-middleware' -import type { MiddlewareOptions as WebpackHotMiddlewareOptions, ClientOptions as WebpackHotMiddlewareClientOptions } from 'webpack-hot-middleware' - -interface WebpackEnv { - isClient: boolean - isDev: boolean - isLegacy: boolean - isModern: boolean - isServer: boolean -} - -interface BabelPresetEnv { - envName: 'client' | 'modern' | 'server' -} -interface Warning { - message: string - name: string -} - -interface BabelOptions extends Pick> { - cacheCompression?: boolean - cacheDirectory?: boolean - cacheIdentifier?: string - customize?: string | null - presets?: ((env: BabelPresetEnv & WebpackEnv, defaultPreset: [string, object]) => PluginItem[] | void) | PluginItem[] | null - plugins?: ((env: BabelPresetEnv & WebpackEnv) => NonNullable) | TransformOptions['plugins'] -} - -type CssLoaderUrlFunction = (url: string, resourcePath: string) => boolean -type CssLoaderImportFunction = (parsedImport: string, resourcePath: string) => boolean -type CssLoaderMode = 'global' | 'local' -interface CssLoaderModulesOptions { - context?: string - getLocalIdent?: (context: string, localIdentName: string, localName: string, options: CssLoaderModulesOptions) => string - hashPrefix?: string - localIdentName?: string - localIdentRegExp?: string | RegExp - mode?: CssLoaderMode -} -interface CssLoaderOptions { - import?: boolean | CssLoaderImportFunction - importLoaders?: number - localsConvention?: 'asIs' | 'camelCase' | 'camelCaseOnly' | 'dashes' | 'dashesOnly' - modules?: boolean | CssLoaderMode | CssLoaderModulesOptions - onlyLocals?: boolean - sourceMap?: boolean - url?: boolean | CssLoaderUrlFunction -} - -interface UrlLoaderOptions { - esModule?: boolean - // TODO - fallback?: any // WebpackLoader - limit?: boolean | number | string - mimetype?: string -} - -interface PostcssOrderPresetFunctions { - cssnanoLast: (names: string[]) => string[] - presetEnvAndCssnanoLast: (names: string[]) => string[] - presetEnvLast: (names: string[]) => string[] -} -type PostcssOrderPreset = keyof PostcssOrderPresetFunctions -interface PostcssVariableMap { - customMedia: Record - customProperties: Record - customSelectors: Record - environmentVariables?: Record -} - -interface PostcssConfiguration { - order?: PostcssOrderPreset | string[] | ((names: string[], presets: PostcssOrderPresetFunctions) => string[]) - plugins?: { - [key: string]: false | { [key: string]: any } - } | ((loader: any) => PostcssPlugin[]) | Array<[string | PostcssPlugin, any] | string | PostcssPlugin> - preset?: { - autoprefixer?: false | AutoprefixerOptions - browsers?: string - exportTo?: string | string[] | Partial | ((map: PostcssVariableMap) => Partial) - features?: { - [key: string]: boolean | { [key: string]: any } - } - importFrom?: string | string[] | Partial | (() => Partial) - insertAfter?: { [key: string]: PostcssPlugin } - insertBefore?: { [key: string]: PostcssPlugin } - preserve?: boolean - stage?: 0 | 1 | 2 | 3 | 4 | false - } -} - -interface Loaders { - css?: CssLoaderOptions - cssModules?: CssLoaderOptions - file?: FileLoaderOptions - fontUrl?: UrlLoaderOptions - imgUrl?: UrlLoaderOptions - less?: Less.Options - pugPlain?: PugOptions - sass?: SassOptions - scss?: SassOptions - stylus?: any // TBD - vue?: VueLoaderOptions - vueStyle?: { - manualInject?: boolean - ssrId?: boolean - shadowMode?: boolean - } -} - -export interface Template { - /** - * Source file. Can be absolute or relative. - */ - src: string, - /** - * Destination file within `.nuxt` filter. This filename should be relative to the project `.nuxt` dir - */ - dst: string, - /** - * Options are provided to template as `options` key - */ - options?: Record -} - -export default () => ({ - /** - * @private - */ - _publicPath: '/_nuxt/', - - additionalExtensions: [] as string[], - aggressiveCodeRemoval: false, - /** - * Use [webpack-bundle-analyzer](https://github.com/webpack-contrib/webpack-bundle-analyzer) to let you visualize your bundles and how to optimize them. - * @default false - */ - analyze: false as boolean | BundleAnalyzerPlugin.Options, - babel: { - configFile: false, - babelrc: false, - cacheDirectory: undefined - } as BabelOptions, - /** - * Enable cache of [terser-webpack-plugin](https://github.com/webpack-contrib/terser-webpack-plugin#options) and [cache-loader](https://github.com/webpack-contrib/cache-loader#cache-loader) - * - * ⚠️ Experimental - * @default false - */ - cache: false, - corejs: undefined as undefined | 'auto' | 2 | 3, - crossorigin: undefined as undefined | string, - /** - * Enables CSS Source Map support. - * @default true for dev and `false` for production - */ - cssSourceMap: undefined as undefined | boolean, - devMiddleware: {} as WebpackDevMiddlewareOptions, - devtools: undefined as undefined | boolean, - extend: null as null | (( - config: WebpackConfiguration, - ctx: { - loaders: Loaders - } & WebpackEnv - ) => void), - /** - * Enables Common CSS Extraction using Vue Server Renderer guidelines. - * - * Using [extract-css-chunks-webpack-plugin](https://github.com/faceyspacey/extract-css-chunks-webpack-plugin/) under the hood, all your CSS will be extracted into separate files, usually one per component. This allows caching your CSS and JavaScript separately and is worth a try in case you have a lot of global or shared CSS. - * - * @default false - */ - extractCSS: false as boolean | Record, - /** - * Customize bundle filenames. - */ - filenames: { - app: ({ isDev, isModern }: WebpackEnv) => isDev ? `[name]${isModern ? '.modern' : ''}.js` : `[name].[contenthash:7]${isModern ? '.modern' : ''}.js`, - chunk: ({ isDev, isModern }: WebpackEnv) => isDev ? `[name]${isModern ? '.modern' : ''}.js` : `[name].[contenthash:7]${isModern ? '.modern' : ''}.js`, - css: ({ isDev }: WebpackEnv) => isDev ? '[name].css' : '[name].[contenthash:7].css', - img: ({ isDev }: WebpackEnv) => isDev ? '[path][name].[ext]' : 'img/[name].[contenthash:7].[ext]', - font: ({ isDev }: WebpackEnv) => isDev ? '[path][name].[ext]' : 'fonts/[name].[contenthash:7].[ext]', - video: ({ isDev }: WebpackEnv) => isDev ? '[path][name].[ext]' : 'videos/[name].[contenthash:7].[ext]' - }, - /** - * By default, the build process does not scan files inside symlinks. This boolean includes them, thus allowing usage of symlinks inside folders such as the "pages" folder, for example. - * @default false - */ - followSymlinks: false, - /** - * Enables or disables the overlay provided by [FriendlyErrorsWebpackPlugin](https://github.com/nuxt/friendly-errors-webpack-plugin) - * @default true - */ - friendlyErrors: true, - hardSource: false, - hotMiddleware: {} as WebpackHotMiddlewareOptions & { client?: WebpackHotMiddlewareClientOptions }, - html: { - /** - * Configuration for the [html-minifier plugin](https://github.com/kangax/html-minifier) used to minify HTML files created during the build process (will be applied for all modes). - */ - minify: { - collapseBooleanAttributes: true, - decodeEntities: true, - minifyCSS: true, - minifyJS: true, - processConditionalComments: true, - removeEmptyAttributes: true, - removeRedundantAttributes: true, - trimCustomFragments: true, - useShortDoctype: true - } - } as { minify: HtmlMinifierOptions }, - indicator: { - position: 'bottom-right', - backgroundColor: '#2E495E', - color: '#00C48D' - } as boolean | { position: string, backgroundColor: string, color: string }, - /** - * Customize options of Nuxt.js integrated webpack loaders. - */ - loaders: { - /** - * Mor details at https://github.com/webpack-contrib/file-loader#options - */ - file: {}, - fontUrl: { limit: 1000 }, - imgUrl: { limit: 1000 }, - pugPlain: {}, - vue: { - transformAssetUrls: { - video: 'src', - source: 'src', - object: 'src', - embed: 'src' - } - }, - css: { - esModule: false - }, - cssModules: { - esModule: false, - modules: { - localIdentName: '[local]_[hash:base64:5]' - } - }, - less: {}, - sass: { - sassOptions: { - indentedSyntax: true - } - }, - scss: {}, - // tODO - stylus: {}, - vueStyle: {} - } as Loaders, - loadingScreen: {} as Record | false, - optimizeCSS: undefined as Record | false | undefined, - optimization: { - minimize: undefined as boolean | undefined, - minimizer: undefined, - splitChunks: { - chunks: 'all', - name: undefined, - cacheGroups: { - default: { - name: undefined - } - } - } - } as WebpackConfiguration['optimization'], - /** - * Enable [thread-loader](https://github.com/webpack-contrib/thread-loader#thread-loader) in webpack building - * - * ⚠️ Experimental - * @default false - */ - parallel: false, - plugins: [] as WebpackPluginFunction[], - postcss: { - preset: { - // https://cssdb.org/#staging-process - stage: 2 - } - } as string[] | boolean | PostcssConfiguration | (() => PostcssConfiguration), - /** - * Enable the profiler in [WebpackBar](https://github.com/nuxt/webpackbar#profile) - * @default false unless enabled by command line argument `--profile` - */ - profile: process.argv.includes('--profile'), - /** - * Nuxt.js lets you upload your dist files to your CDN for maximum performances, simply set the `publicPath` to your CDN. - * @default '/_nuxt/' - * @example - ``` - export default { - build: { - publicPath: 'https://cdn.nuxtjs.org' - } - } - ``` - Then, when launching nuxt build, upload the content of .nuxt/dist/client directory to your CDN and voilà! - */ - publicPath: '/_nuxt/', - /** - * Suppresses most of the build output log - * @default true when a CI or test environment is detected by [std-env](https://github.com/nuxt-contrib/std-env) - */ - quiet: Boolean(env.ci || env.test), - /** - * @default 'url' - */ - serverURLPolyfill: 'url', - splitChunks: { - layouts: false, - pages: true, - commons: true - }, - /** - * Creates special webpack bundle for SSR renderer. - * @default true for universal mode and `false` for spa mode - */ - ssr: undefined as undefined | boolean, - /** - * - */ - standalone: false, - stats: { - excludeAssets: [ - /.map$/, - /index\..+\.html$/, - /vue-ssr-(client|modern)-manifest.json/ - ] - } as 'none' | false | { excludeAssets: RegExp[] }, - styleResources: {}, - template: undefined, - /** - * Nuxt.js allows you provide your own templates which will be rendered based on Nuxt configuration. This feature is specially useful for using with modules. - */ - templates: [] as Template[], - /** - * Terser plugin options. Set to `false` to disable this plugin. See https://github.com/webpack-contrib/terser-webpack-plugin - */ - terser: {} as TerserPluginOptions | boolean, - // Name of NPM packages to be transpiled - transpile: [] as Array string | RegExp | undefined)>, - warningIgnoreFilters: [] as Array<(warn: Warning) => boolean>, - /** - * You can provide your custom files to watch and regenerate after changes. This feature is specially useful for using with modules. - */ - watch: [] as string[] - -}) diff --git a/packages/nuxt3/src/config/config/cli.ts b/packages/nuxt3/src/config/config/cli.ts deleted file mode 100644 index 48ad13cfea..0000000000 --- a/packages/nuxt3/src/config/config/cli.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Color } from 'chalk' - -export interface CliOptions { - badgeMessages: string[] - bannerColor: typeof Color -} - -export default (): CliOptions => ({ - badgeMessages: [], - bannerColor: 'green' -}) diff --git a/packages/nuxt3/src/config/config/generate.ts b/packages/nuxt3/src/config/config/generate.ts deleted file mode 100644 index b7e103bc44..0000000000 --- a/packages/nuxt3/src/config/config/generate.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { GlobbyOptions } from 'globby' - -type GenerateRoute = string | { route: string, payload: any } - -type GenerateRoutesFunction = () => (Promise | GenerateRoute[]) -type GenerateRoutesFunctionWithCallback = (callback: (err: Error, routes: GenerateRoute[]) => void) => void - -export interface GenerateOptions { - cache?: false | { - ignore?: string[] | Function, - globbyOptions?: GlobbyOptions - } - concurrency: number - crawler: boolean - devtools?: boolean - dir: string - exclude: RegExp[] - fallback: boolean | string - interval: number - routes: GenerateRoute[] | GenerateRoutesFunction | GenerateRoutesFunctionWithCallback - staticAssets: { - base?: string - versionBase?: string - dir?: string - version?: string - } - subFolders: boolean -} - -export default (): GenerateOptions => ({ - dir: 'dist', - routes: [], - exclude: [], - concurrency: 500, - interval: 0, - subFolders: true, - fallback: '200.html', - crawler: true, - staticAssets: { - base: undefined, // Default: "/_nuxt/static: - versionBase: undefined, // Default: "_nuxt/static/{version}"" - dir: 'static', - version: undefined // Default: "{timeStampSec}" - } -}) diff --git a/packages/nuxt3/src/config/config/index.ts b/packages/nuxt3/src/config/config/index.ts deleted file mode 100644 index 89987c7a6e..0000000000 --- a/packages/nuxt3/src/config/config/index.ts +++ /dev/null @@ -1,32 +0,0 @@ - -import _app from './_app' -import _common from './_common' - -import build from './build' -import messages from './messages' -import modes from './modes' -import render from './render' -import router from './router' -import server from './server' -import cli from './cli' -import generate, { GenerateOptions } from './generate' - -export const defaultNuxtConfigFile = 'nuxt.config' - -export const getDefaultNuxtConfig = () => - ({ - ..._app(), - ..._common(), - build: build(), - messages: messages(), - modes: modes(), - render: render(), - router: router(), - server: server({ env: process.env }) as ReturnType | boolean, - cli: cli(), - generate: generate(), - export: undefined as undefined | GenerateOptions, - telemetry: undefined as undefined | boolean - }) - -export type DefaultConfiguration = ReturnType diff --git a/packages/nuxt3/src/config/config/messages.ts b/packages/nuxt3/src/config/config/messages.ts deleted file mode 100644 index 0c3694c2b5..0000000000 --- a/packages/nuxt3/src/config/config/messages.ts +++ /dev/null @@ -1,12 +0,0 @@ -export default () => ({ - loading: 'Loading...', - error_404: 'This page could not be found', - server_error: 'Server error', - nuxtjs: 'Nuxt.js', - back_to_home: 'Back to the home page', - server_error_details: - 'An error occurred in the application and your page could not be served. If you are the application owner, check your logs for details.', - client_error: 'Error', - client_error_details: - 'An error occurred while rendering the page. Check developer tools console for details.' -}) diff --git a/packages/nuxt3/src/config/config/modes.ts b/packages/nuxt3/src/config/config/modes.ts deleted file mode 100644 index 93402f21e8..0000000000 --- a/packages/nuxt3/src/config/config/modes.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { MODES } from '../../utils' - -export default () => ({ - [MODES.universal]: { - build: { - ssr: true - }, - render: { - ssr: true - } - } as const, - [MODES.spa]: { - build: { - ssr: false - }, - render: { - ssr: false - } - } as const -}) diff --git a/packages/nuxt3/src/config/config/render.ts b/packages/nuxt3/src/config/config/render.ts deleted file mode 100644 index 85153e9e9d..0000000000 --- a/packages/nuxt3/src/config/config/render.ts +++ /dev/null @@ -1,112 +0,0 @@ -// TODO: Refactor @nuxt/server related options into `server.js` -import type { ServerResponse, IncomingMessage } from 'http' -import type { CompressionOptions } from 'compression' -import type { ServeStaticOptions } from 'serve-static' -import type etag from 'etag' -import type { ServerMiddleware } from './_common' - -interface PreloadFile { - asType: 'script' | 'style' | 'font' - extension: string - file: string - fileWithoutQuery: string -} - -type ServePlaceholderHandler = 'default' | 'css' | 'html' | 'js' | 'json' | 'map' | 'plain' | 'image' -interface ServePlaceholderOptions { - handlers?: Record - mimes?: Record - noCache?: boolean - placeholders?: Record - skipUnknown?: boolean - statusCode?: false | number -} - -type CspPolicyName = 'child-src' | 'connect-src' | 'default-src' | 'font-src' | 'frame-src' | 'img-src' | 'manifest-src' | 'media-src' | 'object-src' | 'prefetch-src' | 'script-src' | 'script-src-elem' | 'script-src-attr' | 'style-src' | 'style-src-elem' | 'style-src-attr' | 'worker-src' | 'base-uri' | 'plugin-types' | 'sandbox' | 'form-action' | 'frame-ancestors' | 'navigate-to' | 'report-uri' | 'report-to' | 'block-all-mixed-content' | 'referrer' | 'require-sri-for' | 'trusted-types' | 'upgrade-insecure-requests' - -interface RenderOptions { - bundleRenderer: { - shouldPrefetch: (fileWithoutQuery: string, asType: string) => boolean - shouldPreload: (fileWithoutQuery: string, asType: string) => boolean - runInNewContext?: boolean - } - compressor: CompressionOptions | ServerMiddleware | false - crossorigin?: 'anonymous' | 'use-credentials' | '' - csp: boolean | { - addMeta?: boolean - allowedSources?: string[] - hashAlgorithm?: string - policies?: Record - reportOnly?: boolean - unsafeInlineCompatibility?: boolean - } - dist: ServeStaticOptions - etag: false | etag.Options & { - hash?: (html: string) => string - } - fallback?: { - dist?: ServePlaceholderOptions - static?: ServePlaceholderOptions - } - /** - * @deprecated - */ - gzip?: CompressionOptions | ServerMiddleware | false - http2?: { - push?: boolean - shouldPush?: boolean | null - pushAssets?: null | (( - req: IncomingMessage, - res: ServerResponse, - publicPath: string, - preloadFiles: PreloadFile[] - ) => string[]) - } - injectScripts?: boolean - resourceHints: boolean - ssr?: boolean - ssrLog?: boolean | 'collapsed' - static: ServeStaticOptions & { prefix?: string } -} - -export default (): RenderOptions => ({ - bundleRenderer: { - shouldPrefetch: () => false, - shouldPreload: (_fileWithoutQuery, asType) => ['script', 'style'].includes(asType), - runInNewContext: undefined - }, - crossorigin: undefined, - resourceHints: true, - ssr: undefined, - ssrLog: undefined, - http2: { - push: false, - shouldPush: null, - pushAssets: null - }, - static: {}, - compressor: { - threshold: 0 - }, - etag: { - weak: false - }, - csp: false, - dist: { - // Don't serve index.html template - index: false, - // 1 year in production - maxAge: '1y' - }, - // https://github.com/nuxt/serve-placeholder - fallback: { - dist: {}, - static: { - skipUnknown: true, - handlers: { - '.htm': false, - '.html': false - } - } - } -}) diff --git a/packages/nuxt3/src/config/config/router.ts b/packages/nuxt3/src/config/config/router.ts deleted file mode 100644 index f7228ae837..0000000000 --- a/packages/nuxt3/src/config/config/router.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { RouteRecordRaw, RouterScrollBehavior } from 'vue-router' - -type UnionToIntersection = (T extends any ? (k: T) => void : never) extends ((k: infer U) => void) ? U : never -type RouteConfig = UnionToIntersection - -export interface Route extends Pick> { - children?: Route[] - chunkName?: string - chunkNames?: Record - component?: RouteConfig['component'] | string -} -interface Middleware { } - -export interface RouterConfigurationNormalized { - base: string - extendRoutes?(routes: Route[], resolve: (...pathSegments: string[]) => string): void - fallback: boolean - linkActiveClass: string | false - linkExactActiveClass: string | false - linkPrefetchedClass: string | false - middleware: Middleware[] - mode: 'history' | 'hash' - parseQuery: boolean - prefetchLinks: boolean - prefetchPayloads: boolean - routes: Route[] - routeNameSplitter: string - scrollBehavior: null | RouterScrollBehavior - stringifyQuery: boolean - trailingSlash?: boolean -} - -export default (): RouterConfigurationNormalized => ({ - mode: 'history', - base: '/', - routes: [], - routeNameSplitter: '-', - middleware: [], - linkActiveClass: 'nuxt-link-active', - linkExactActiveClass: 'nuxt-link-exact-active', - linkPrefetchedClass: false, - extendRoutes: null, - scrollBehavior: null, - parseQuery: false, - stringifyQuery: false, - fallback: false, - prefetchLinks: true, - prefetchPayloads: true, - trailingSlash: undefined -}) diff --git a/packages/nuxt3/src/config/config/server.ts b/packages/nuxt3/src/config/config/server.ts deleted file mode 100644 index 5c8e7a77a3..0000000000 --- a/packages/nuxt3/src/config/config/server.ts +++ /dev/null @@ -1,31 +0,0 @@ -export interface ServerProcessEnv extends NodeJS.ProcessEnv { - NUXT_HOST?: string - NUXT_PORT?: string - HOST?: string - PORT?: string - UNIX_SOCKET?: string - // eslint-disable-next-line camelcase - npm_package_config_nuxt_port?: string - // eslint-disable-next-line camelcase - npm_package_config_nuxt_host?: string - // eslint-disable-next-line camelcase - npm_package_config_unix_socket?: string -} - -export default ({ env = {} }: { env?: ServerProcessEnv } = {}) => ({ - https: false as false | { - cert?: string | Buffer - key?: string | Buffer - }, - port: env.NUXT_PORT || - env.PORT || - env.npm_package_config_nuxt_port || - 3000, - host: env.NUXT_HOST || - env.HOST || - env.npm_package_config_nuxt_host || - 'localhost', - socket: env.UNIX_SOCKET || - env.npm_package_config_unix_socket, - timing: false as false | { total: boolean } -}) diff --git a/packages/nuxt3/src/config/index.ts b/packages/nuxt3/src/config/index.ts deleted file mode 100644 index 8acbffd7b3..0000000000 --- a/packages/nuxt3/src/config/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export { defaultNuxtConfigFile, getDefaultNuxtConfig } from './config' -export { getNuxtConfig, Configuration, NormalizedConfiguration } from './options' -export { loadNuxtConfig } from './load' diff --git a/packages/nuxt3/src/config/load.ts b/packages/nuxt3/src/config/load.ts deleted file mode 100644 index 61b24fc440..0000000000 --- a/packages/nuxt3/src/config/load.ts +++ /dev/null @@ -1,196 +0,0 @@ -import path from 'path' -import fs from 'fs' -import defu from 'defu' -import consola from 'consola' -import dotenv from 'dotenv' -import jiti from 'jiti' -import _createRequire from 'create-require' -import destr from 'destr' -import * as rc from 'rc9' -import { clearRequireCache, scanRequireTree } from '../utils' - -import { LoadOptions } from '../core/load' -import { CliConfiguration, Configuration } from '../config/options' -import { defaultNuxtConfigFile } from './config' - -// @ts-ignore -const isJest = typeof jest !== 'undefined' - -const interopDefault = (obj: any) => { - return 'default' in obj ? obj.default : obj -} - -export interface EnvConfig { - dotenv?: string - env?: NodeJS.ProcessEnv & { _applied?: boolean } - expand?: boolean -} - -export async function loadNuxtConfig ({ - rootDir = '.', - envConfig = {}, - configFile = defaultNuxtConfigFile, - configContext = {}, - configOverrides = {}, - createRequire = (module: NodeJS.Module) => isJest ? _createRequire(module.filename) : jiti(module.filename) -}: LoadOptions = {}) { - rootDir = path.resolve(rootDir) - - let options: CliConfiguration = {} - - try { - configFile = require.resolve(path.resolve(rootDir, configFile)) - } catch (e) { - if (e.code !== 'MODULE_NOT_FOUND') { - throw (e) - } else if (configFile !== defaultNuxtConfigFile) { - consola.fatal('Config file not found: ' + configFile) - } - // Skip configFile if cannot resolve - configFile = undefined - } - - // Load env - envConfig = { - dotenv: '.env', - env: process.env, - expand: true, - ...envConfig - } - - const env = loadEnv(envConfig, rootDir) - - // Fill process.env so it is accessible in nuxt.config - for (const key in env) { - if (!key.startsWith('_') && envConfig.env[key] === undefined) { - envConfig.env[key] = env[key] - } - } - - if (configFile) { - // Clear cache - clearRequireCache(configFile) - const _require = createRequire(module) - let _config: Configuration | ((ctx: Record) => Promise) = interopDefault(_require(configFile) || {}) - - if (typeof _config === 'function') { - try { - _config = interopDefault(await _config(configContext)) - } catch (error) { - consola.error(error) - consola.fatal('Error while fetching async configuration') - } - } - - // Don't mutate options export - options = { ..._config } - - // Keep _nuxtConfigFile for watching - options._nuxtConfigFile = configFile - - // Keep all related files for watching - options._nuxtConfigFiles = Array.from(scanRequireTree(configFile)) - if (!options._nuxtConfigFiles.includes(configFile)) { - options._nuxtConfigFiles.unshift(configFile) - } - } - - if (typeof options.rootDir !== 'string') { - options.rootDir = rootDir - } - - // Load Combine configs - // Priority: configOverrides > nuxtConfig > .nuxtrc > .nuxtrc (global) - options = defu( - configOverrides, - options, - rc.read({ name: '.nuxtrc', dir: options.rootDir }), - rc.readUser('.nuxtrc') - ) - - // Load env to options._env - options._env = env - options._envConfig = envConfig - if (configContext) { configContext.env = env } - - // Expand and interpolate runtimeConfig from _env - if (envConfig.expand) { - for (const c of ['publicRuntimeConfig', 'privateRuntimeConfig']) { - if (options[c]) { - if (typeof options[c] === 'function') { - options[c] = options[c](env) - } - expand(options[c], env, destr) - } - } - } - - return options -} - -function loadEnv (envConfig: EnvConfig, rootDir = process.cwd()) { - const env = Object.create(null) - - // Read dotenv - if (envConfig.dotenv) { - envConfig.dotenv = path.resolve(rootDir, envConfig.dotenv) - if (fs.existsSync(envConfig.dotenv)) { - const parsed = dotenv.parse(fs.readFileSync(envConfig.dotenv, 'utf-8')) - Object.assign(env, parsed) - } - } - - // Apply process.env - if (!envConfig.env._applied) { - Object.assign(env, envConfig.env) - envConfig.env._applied = true - } - - // Interpolate env - if (envConfig.expand) { - expand(env) - } - - return env -} - -// Based on https://github.com/motdotla/dotenv-expand -function expand (target: Record, source: Record = {}, parse = (v: string) => v) { - function getValue (key: string) { - // Source value 'wins' over target value - return source[key] !== undefined ? source[key] : target[key] - } - - function interpolate (value: string): string { - if (typeof value !== 'string') { - return value - } - const matches = value.match(/(.?\${?(?:[a-zA-Z0-9_:]+)?}?)/g) || [] - return parse(matches.reduce((newValue, match) => { - const parts = /(.?)\${?([a-zA-Z0-9_:]+)?}?/g.exec(match) - const prefix = parts[1] - - let value: string - let replacePart: string - - if (prefix === '\\') { - replacePart = parts[0] - value = replacePart.replace('\\$', '$') - } else { - const key = parts[2] - replacePart = parts[0].substring(prefix.length) - - value = getValue(key) - - // Resolve recursive interpolations - value = interpolate(value) - } - - return newValue.replace(replacePart, value) - }, value)) - } - - for (const key in target) { - target[key] = interpolate(getValue(key)) - } -} diff --git a/packages/nuxt3/src/config/options.ts b/packages/nuxt3/src/config/options.ts deleted file mode 100644 index 1c2f9d17d4..0000000000 --- a/packages/nuxt3/src/config/options.ts +++ /dev/null @@ -1,453 +0,0 @@ -import path from 'path' -import fs from 'fs' -import consola from 'consola' -import defu from 'defu' -import defaultsDeep from 'lodash/defaultsDeep' -import pick from 'lodash/pick' -import uniq from 'lodash/uniq' -import destr from 'destr' -import { TARGETS, MODES, guardDir, isNonEmptyString, isPureObject, isUrl, getMainModule, urlJoin, getPKG } from '../utils' -import type { EnvConfig } from '../config/load' -import { DefaultConfiguration, defaultNuxtConfigFile, getDefaultNuxtConfig } from './config' -import { deleteProp, mergeConfigs, setProp, overrideProp, Optional } from './transformers' - -interface InputConfiguration { - documentPath?: string - layoutTransition?: string | DefaultConfiguration['layoutTransition'] - loading?: true | false | DefaultConfiguration['loading'] - manifest?: { - // eslint-disable-next-line camelcase - theme_color?: string - } - pageTransition?: string | DefaultConfiguration['pageTransition'] - rootDir?: string - store?: boolean -} - -export interface Configuration extends InputConfiguration, Optional> { } - -export interface CliConfiguration extends Configuration { - // cli - _build?: boolean - _cli?: boolean - _export?: boolean - _generate?: boolean - _start?: boolean - _ready?: boolean - _legacyGenerate?: boolean - _env?: NodeJS.ProcessEnv - _envConfig?: EnvConfig - _nuxtConfigFiles?: string[] -} - -export function getNuxtConfig (_options: Configuration) { - // Prevent duplicate calls - if ('__normalized__' in _options) { - return _options - } - - return normalizeConfig(_options as CliConfiguration) -} - -function normalizeConfig (_options: CliConfiguration) { - // Clone options to prevent unwanted side-effects - const _config: CliConfiguration = Object.assign({}, _options) - - setProp(_config, '__normalized__', true as const) - - // Normalize options - if (_config.loading === true) { - deleteProp(_config, 'loading') - } - - setProp(_config, '_routerBaseSpecified', _config.router && typeof _config.router.base === 'string') - - overrideProp(_config, 'pageTransition', typeof _config.pageTransition === 'string' ? { name: _config.pageTransition } : _config.pageTransition) - overrideProp(_config, 'layoutTransition', typeof _config.layoutTransition === 'string' ? { name: _config.layoutTransition } : _config.layoutTransition) - - if (typeof _config.extensions === 'string') { - _config.extensions = [_config.extensions] - } - - overrideProp(_config, 'globalName', - (isNonEmptyString(_config.globalName) && /^[a-zA-Z]+$/.test(_config.globalName)) - ? _config.globalName.toLowerCase() - // use `` for preventing replacing to nuxt-edge - : 'nuxt' - ) - - // Resolve rootDir - overrideProp(_config, 'rootDir', - isNonEmptyString(_config.rootDir) ? path.resolve(_config.rootDir) : process.cwd() - ) - - // Apply defaults by ${buildDir}/dist/build.config.js - // TODO: Unsafe operation. - // const buildDir = _config.buildDir || defaults.buildDir - // const buildConfig = resolve(_config.rootDir, buildDir, 'build.config.js') - // if (existsSync(buildConfig)) { - // defaultsDeep(_config, require(buildConfig)) - // } - - // Fall back to default if publicPath is falsy - if (_config.build && !_config.build.publicPath) { - _config.build.publicPath = undefined - } - - // Apply defaults - const options = mergeConfigs(_config, getDefaultNuxtConfig()) - - // Target - if (!Object.values(TARGETS).includes(options.target)) { - consola.warn(`Unknown target: ${options.target}. Falling back to server`) - options.target = 'server' - } - - // SSR root option - if (options.ssr === false) { - options.mode = MODES.spa - } - - // Apply mode preset - const modePreset = options.modes[options.mode || MODES.universal] - - if (!modePreset) { - consola.warn(`Unknown mode: ${options.mode}. Falling back to ${MODES.universal}`) - } - defaultsDeep(options, modePreset || options.modes[MODES.universal]) - - // Sanitize router.base - if (!/\/$/.test(options.router.base)) { - options.router.base += '/' - } - - // Alias export to generate - // TODO: switch to export by default for nuxt3 - if (options.export) { - options.generate = defu(options.export, options.generate) - } - exports.export = options.generate - - // Check srcDir and generate.dir existence - const hasSrcDir = isNonEmptyString(options.srcDir) - const hasGenerateDir = isNonEmptyString(options.generate.dir) - - // Resolve srcDir - overrideProp(options, 'srcDir', hasSrcDir - ? path.resolve(options.rootDir, options.srcDir) - : options.rootDir) - - // Resolve buildDir - overrideProp(options, 'buildDir', path.resolve(options.rootDir, options.buildDir)) - - // Aliases - const { rootDir, srcDir, dir: { assets: assetsDir, static: staticDir } } = options - overrideProp(options, 'alias', { - '~~': rootDir, - '@@': rootDir, - '~': srcDir, - '@': srcDir, - [assetsDir]: path.join(srcDir, assetsDir), - [staticDir]: path.join(srcDir, staticDir), - ...options.alias - }) - - // Default value for _nuxtConfigFile - overrideProp(options, '_nuxtConfigFile', options._nuxtConfigFile || path.resolve(options.rootDir, `${defaultNuxtConfigFile}.js`)) - - setProp(options, '_nuxtConfigFiles', (options as any)._nuxtConfigFiles || [ - options._nuxtConfigFile - ]) - - // Watch for config file changes - options.watch.push(...options._nuxtConfigFiles) - - // Protect rootDir against buildDir - guardDir(options, 'rootDir', 'buildDir') - - if (hasGenerateDir) { - // Resolve generate.dir - options.generate.dir = path.resolve(options.rootDir, options.generate.dir) - - // Protect rootDir against buildDir - guardDir(options, 'rootDir', 'generate.dir') - } - - if (hasSrcDir) { - // Protect srcDir against buildDir - guardDir(options, 'srcDir', 'buildDir') - - if (hasGenerateDir) { - // Protect srcDir against generate.dir - guardDir(options, 'srcDir', 'generate.dir') - } - } - - // Populate modulesDir - options.modulesDir = uniq( - getMainModule().paths.concat( - [].concat(options.modulesDir).map(dir => path.resolve(options.rootDir, dir)) - ) - ) - - const mandatoryExtensions = ['js', 'mjs', 'ts', 'tsx', 'vue', 'jsx'] - - overrideProp(options, 'extensions', mandatoryExtensions - .filter(ext => !options.extensions.includes(ext)) - .concat(options.extensions)) - - // If app.html is defined, set the template path to the user template - if (options.documentPath === undefined) { - options.documentPath = path.resolve(options.buildDir, 'views/app.template.html') // NITRO/Nuxt2 compat - const userDocumentPath = path.join(options.srcDir, 'document.html') - if (fs.existsSync(userDocumentPath)) { - options.documentPath = userDocumentPath - } else { - options.watch.push(userDocumentPath) - } - } else { - options.documentPath = path.resolve(options.srcDir, options.documentPath) - } - - overrideProp(options.build, 'publicPath', options.build.publicPath.replace(/([^/])$/, '$1/')) - overrideProp(options.build, '_publicPath', options.build._publicPath.replace(/([^/])$/, '$1/')) - - // Ignore publicPath on dev - if (options.dev && isUrl(options.build.publicPath)) { - options.build.publicPath = options.build._publicPath - } - - // If store defined, update store options to true unless explicitly disabled - if ( - options.store !== false && - fs.existsSync(path.join(options.srcDir, options.dir.store)) && - fs.readdirSync(path.join(options.srcDir, options.dir.store)) - .find(filename => filename !== 'README.md' && filename[0] !== '.') - ) { - options.store = true - } - - // SPA loadingIndicator - if (options.loadingIndicator) { - // Normalize loadingIndicator - if (!isPureObject(options.loadingIndicator)) { - options.loadingIndicator = { name: options.loadingIndicator } - } - - // Apply defaults - options.loadingIndicator = Object.assign( - { - name: 'default', - color: (options.loading && typeof options.loading !== 'string' && typeof options.loading !== 'boolean' && options.loading.color) || '#D3D3D3', - color2: '#F5F5F5', - background: (options.manifest && options.manifest.theme_color) || 'white', - dev: options.dev, - loading: options.messages.loading - }, - options.loadingIndicator - ) - } - - // Debug errors - overrideProp(options, 'debug', options.debug ?? options.dev) - - // Validate that etag.hash is a function, if not unset it - if (options.render.etag) { - const { hash } = options.render.etag - if (hash) { - const isFn = hash instanceof Function - if (!isFn) { - options.render.etag.hash = undefined - - if (options.dev) { - consola.warn(`render.etag.hash should be a function, received ${typeof hash} instead`) - } - } - } - } - - // Apply default hash to CSP option - if (options.render.csp) { - options.render.csp = defu(options.render.csp, { - hashAlgorithm: 'sha256', - allowedSources: undefined, - policies: undefined, - addMeta: Boolean(options.target === TARGETS.static), - unsafeInlineCompatibility: false, - reportOnly: options.debug - }) - } - - // cssSourceMap - overrideProp(options.build, 'cssSourceMap', options.build.cssSourceMap ?? options.dev) - - // babel cacheDirectory - const babelConfig = options.build.babel - overrideProp(options.build.babel, 'cacheDirectory', babelConfig.cacheDirectory ?? options.dev) - - // Vue config - const vueConfig = options.vue.config - - overrideProp(options.vue.config, 'performance', vueConfig.performance !== undefined ? vueConfig.performance : options.dev) - - // merge custom env with variables - const eligibleEnvVariables = pick(process.env, Object.keys(process.env).filter(k => k.startsWith('NUXT_ENV_'))) - overrideProp(options, 'env', Object.assign(options.env, eligibleEnvVariables)) - - // Normalize ignore - overrideProp(options, 'ignore', options.ignore ? Array.from(options.ignore) : []) - - // Append ignorePrefix glob to ignore - if (typeof options.ignorePrefix === 'string') { - options.ignore.push(`${options.ignorePrefix}*`) - } - - // Compression middleware legacy - if (options.render.gzip) { - consola.warn('render.gzip is deprecated and will be removed in a future version! Please switch to render.compressor') - options.render.compressor = options.render.gzip - delete options.render.gzip - } - - // If no server-side rendering, add appear true transition - if (options.render.ssr === false && options.pageTransition) { - options.pageTransition.appear = true - } - - overrideProp(options.render, 'ssrLog', options.dev - ? options.render.ssrLog === undefined || options.render.ssrLog - : false) - - // We assume the SPA fallback path is 404.html (for GitHub Pages, Surge, etc.) - overrideProp(options.generate, 'fallback', options.generate.fallback === true ? '404.html' : options.generate.fallback) - - if (options.build.stats === 'none' || options.build.quiet === true) { - options.build.stats = false - } - - // @pi0 - surely this can go - // // Vendor backward compatibility with nuxt 1.x - // if (typeof options.build.vendor !== 'undefined') { - // delete options.build.vendor - // consola.warn('vendor has been deprecated due to webpack4 optimization') - // } - - // Disable CSS extraction due to incompatibility with thread-loader - if (options.build.extractCSS && options.build.parallel) { - options.build.parallel = false - consola.warn('extractCSS cannot work with parallel build due to limited work pool in thread-loader') - } - - // build.extractCSS.allChunks has no effect - if (typeof options.build.extractCSS !== 'boolean' && typeof options.build.extractCSS.allChunks !== 'undefined') { - consola.warn('build.extractCSS.allChunks has no effect from v2.0.0. Please use build.optimization.splitChunks settings instead.') - } - - // Enable minimize for production builds - if (options.build.optimization.minimize === undefined) { - options.build.optimization.minimize = !options.dev - } - - if (options.build.optimizeCSS === undefined) { - options.build.optimizeCSS = options.build.extractCSS ? {} : false - } - - const { loaders } = options.build - // const vueLoader = loaders.vue - // if (vueLoader.productionMode === undefined) { - // vueLoader.productionMode = !options.dev - // } - const styleLoaders: Array = [ - 'css', 'cssModules', 'less', - 'sass', 'scss', 'stylus', 'vueStyle' - ] - for (const name of styleLoaders) { - const loader = loaders[name] - if (loader && loader.sourceMap === undefined) { - loader.sourceMap = Boolean(options.build.cssSourceMap) - } - } - - overrideProp(options.build, 'transpile', Array.from(options.build.transpile || [])) - options.build.transpile = [].concat(options.build.transpile || []) - options.build.transpile.push('app') - - if (options.build.quiet === true) { - consola.level = 0 - } - - // Use runInNewContext for dev mode by default - const { bundleRenderer } = options.render - overrideProp(options.render.bundleRenderer, 'runInNewContext', bundleRenderer.runInNewContext ?? options.dev) - - // const { timing } = options.server - if (options.server && typeof options.server !== 'boolean' && options.server.timing) { - overrideProp(options.server, 'timing', { total: true, ...options.server.timing }) - } - - overrideProp(options, 'serverMiddleware', - Array.isArray(options.serverMiddleware) - ? options.serverMiddleware - : Object.entries(options.serverMiddleware) - .map(([path, handler]) => ({ path, handler })) - ) - - // Generate staticAssets - const { staticAssets } = options.generate - overrideProp(options.generate.staticAssets, 'version', options.generate.staticAssets.version || String(Math.round(Date.now() / 1000))) - - if (!staticAssets.base) { - const publicPath = isUrl(options.build.publicPath) ? '' : options.build.publicPath // "/_nuxt" or custom CDN URL - staticAssets.base = urlJoin(publicPath, staticAssets.dir) - } - if (!staticAssets.versionBase) { - staticAssets.versionBase = urlJoin(staticAssets.base, staticAssets.version) - } - - // createRequire factory - if (options.createRequire === undefined) { - const createRequire = require('create-require') - options.createRequire = module => createRequire(module.filename) - } - - // ----- Builtin modules ----- - - // Loading screen - // Force disable for production and programmatic users - if (!options.dev || !options._cli || !getPKG('@nuxt/loading-screen')) { - options.build.loadingScreen = false - } - if (options.build.loadingScreen) { - options._modules.push(['@nuxt/loading-screen', options.build.loadingScreen]) - } else { - // When loadingScreen is disabled we should also disable build indicator - options.build.indicator = false - } - - // Components Module - // TODO: Webpack5 support - // if (!options._start && getPKG('@nuxt/components')) { - // options._modules.push('@nuxt/components') - // } - - // Nuxt Telemetry - if ( - options.telemetry !== false && - !options.test && - !destr(process.env.NUXT_TELEMETRY_DISABLED) && - getPKG('@nuxt/telemetry') - ) { - options._modules.push('@nuxt/telemetry') - } - - options._majorVersion = 3 - - if (options.vite && !options.dev) { - options.vite = false - consola.warn('Vite does not support production builds yet! Using webpack...') - } - - return options -} - -export type NormalizedConfiguration = ReturnType diff --git a/packages/nuxt3/src/config/transformers.ts b/packages/nuxt3/src/config/transformers.ts deleted file mode 100644 index 00daa9cdbc..0000000000 --- a/packages/nuxt3/src/config/transformers.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { defaultsDeep } from 'lodash' - -export type Optional = { - -readonly [K in keyof T]?: T[K] extends Function ? T[K] : T[K] extends RegExp ? T[K] : T[K] extends Promise ? T[K] : T[K] extends Array ? Array : Optional -} - -export function setProp, K extends string, V> (obj: O, key: K, value: V): asserts obj is O & { [key in K]: V } { - (obj as { [key in K]: V })[key] = value -} - -type Override, K extends keyof O, V> = K extends Array ? { - [P in keyof O]: K extends P ? O[P] extends Array ? Array : K | O[P] : O[P] -} : O & { [key in K]: V } - -export function overrideProp, K extends keyof O, V> (obj: O, key: K, value: V): asserts obj is Override { - (obj as { [key in K]: V })[key] = value -} - -export function deleteProp, K extends string> (obj: O, key: K): asserts obj is Exclude { - delete obj[key] -} - -type MergeArrays = S extends Array ? T extends Array ? Array : T | Array : T | S -type MergeObjects, T extends Record> = Omit & { - // eslint-disable-next-line no-use-before-define - -readonly [K in keyof S & keyof T]: Merge -} -type Merge = S extends Array ? MergeArrays : S extends Function ? S | T : S extends RegExp ? S | T : S extends Promise ? S | T : T extends Function ? S | T : S extends Record ? T extends Record ? MergeObjects : S | T : MergeArrays - -// let b: Merged<{ test: string, another: number[] }, { third: () => void, another: string[] }> = {} as any -// b.another -// let c = b -// c. - -// T extends Array ? Array> : T - -export function mergeConfigs, Source extends Record> (dest: Dest, source: Source): Merge { - return defaultsDeep(dest, source) -} diff --git a/packages/nuxt3/src/core/index.ts b/packages/nuxt3/src/core/index.ts index 707114361d..acdb747ab0 100644 --- a/packages/nuxt3/src/core/index.ts +++ b/packages/nuxt3/src/core/index.ts @@ -1,5 +1,4 @@ export { default as Module } from './module' export { default as Nuxt } from './nuxt' export { default as Resolver } from './resolver' -export { loadNuxtConfig } from '../config' export { loadNuxt } from './load' diff --git a/packages/nuxt3/src/core/load.ts b/packages/nuxt3/src/core/load.ts index 8d99c62d00..492dfed93b 100644 --- a/packages/nuxt3/src/core/load.ts +++ b/packages/nuxt3/src/core/load.ts @@ -1,53 +1,17 @@ -import { EnvConfig } from '../config/load' -import { loadNuxtConfig } from '../config' +import { resolve } from 'path' +import { loadNuxtConfig } from '@nuxt/kit' import Nuxt from './nuxt' - -const OVERRIDES = { - dry: { dev: false, server: false }, - dev: { dev: true, _build: true }, - build: { dev: false, server: false, _build: true }, - start: { dev: false, _start: true } +export interface LoadNuxtOptions { } -export interface LoadOptions { - for?: keyof typeof OVERRIDES - ready?: boolean +export async function loadNuxt (opts: LoadNuxtOptions) { + const options = await loadNuxtConfig(opts) - rootDir?: string - envConfig?: EnvConfig - configFile?: string - configContext?: Record, - configOverrides?: Record, - createRequire?: (module: NodeJS.Module) => NodeJS.Require -} - -export async function loadNuxt (loadOptions: LoadOptions | LoadOptions['for']) { - // Normalize loadOptions - if (typeof loadOptions === 'string') { - loadOptions = { for: loadOptions } - } - const { ready = true } = loadOptions - const _for = loadOptions.for || 'dry' - - // Get overrides - const override = OVERRIDES[_for] - - // Unsupported purpose - if (!override) { - throw new Error('Unsupported for: ' + _for) - } - - // Load Config - const config = await loadNuxtConfig(loadOptions) - - // Apply config overrides - Object.assign(config, override) - - // Initiate Nuxt - const nuxt = new Nuxt(config) - if (ready) { - await nuxt.ready() - } + // Temp + options.appDir = resolve(__dirname, '../app') + options._majorVersion = 3 + const nuxt = new Nuxt(options) + await nuxt.ready() return nuxt } diff --git a/packages/nuxt3/src/core/module.ts b/packages/nuxt3/src/core/module.ts index 4a30dc277a..09a2e6c85f 100644 --- a/packages/nuxt3/src/core/module.ts +++ b/packages/nuxt3/src/core/module.ts @@ -3,10 +3,8 @@ import fs from 'fs' import hash from 'hash-sum' import consola from 'consola' -import type { NormalizedConfiguration } from '../config' import { chainFn, sequence } from '../utils' -import type { NuxtModule, ModuleHandler } from '../config/config/_common' import Nuxt from './nuxt' interface TemplateInput { @@ -24,7 +22,7 @@ export default class ModuleContainer { requiredModules: Record - handler: ModuleHandler + handler }> constructor (nuxt: Nuxt) { @@ -127,29 +125,29 @@ export default class ModuleContainer { this.options.ErrorPage = `~/${relativeBuildDir}/${dst}` } - addServerMiddleware (middleware: NormalizedConfiguration['serverMiddleware'][number]) { + addServerMiddleware (middleware) { this.options.serverMiddleware.push(middleware) } - extendBuild (fn: NormalizedConfiguration['build']['extend']) { + extendBuild (fn) { this.options.build.extend = chainFn(this.options.build.extend, fn) } - extendRoutes (fn: NormalizedConfiguration['router']['extendRoutes']) { + extendRoutes (fn) { this.options.router.extendRoutes = chainFn( this.options.router.extendRoutes, fn ) } - requireModule (moduleOpts: NuxtModule) { + requireModule (moduleOpts) { return this.addModule(moduleOpts) } - async addModule (moduleOpts: NuxtModule) { - let src: string | ModuleHandler + async addModule (moduleOpts) { + let src let options: Record - let handler: ModuleHandler | ModuleHandler & { meta: { name: string } } + let handler // Type 1: String or Function if (typeof moduleOpts === 'string' || typeof moduleOpts === 'function') { @@ -224,7 +222,7 @@ export default class ModuleContainer { return } } - this.requiredModules[key] = { src, options, handler: handler as ModuleHandler } + this.requiredModules[key] = { src, options, handler } } } diff --git a/packages/nuxt3/src/core/nuxt.ts b/packages/nuxt3/src/core/nuxt.ts index 7a6575e332..205c7b243f 100644 --- a/packages/nuxt3/src/core/nuxt.ts +++ b/packages/nuxt3/src/core/nuxt.ts @@ -4,8 +4,6 @@ import isPlainObject from 'lodash/isPlainObject' import consola from 'consola' import Hookable from 'hookable' -import { getNuxtConfig, Configuration, NormalizedConfiguration } from '../config' - import { version } from '../../package.json' import ModuleContainer from './module' @@ -25,7 +23,7 @@ export default class Nuxt extends Hookable { _initCalled?: boolean error?: Error & { statusCode?: number, headers?: IncomingHttpHeaders } - options: NormalizedConfiguration + options: any resolver: Resolver moduleContainer: ModuleContainer server?: any @@ -33,11 +31,10 @@ export default class Nuxt extends Hookable { render?: any['app'] showReady?: () => void - constructor (options: Configuration = {}) { + constructor (options) { super(consola) - // Assign options and apply defaults - this.options = getNuxtConfig(options) + this.options = options // Create instance of core components this.resolver = new Resolver(this) diff --git a/scripts/build.ts b/scripts/build.ts index c9a1f02dea..a2d6bf414e 100644 --- a/scripts/build.ts +++ b/scripts/build.ts @@ -56,7 +56,7 @@ async function main () { if (entry.bundle) { const input = resolve(ctx.rootDir, entry.input) stubbed.push(entry.output) - const output = resolve(ctx.rootDir, entry.output) + '.js' + const output = resolve(ctx.rootDir, entry.output) + '.ts' await mkdir(dirname(output)).catch(() => { }) await writeFile(output, entry.format === 'cjs' ? `module.exports = require('jiti')()('${input}')` @@ -118,7 +118,7 @@ ${buildEntries.map(entry => `${chalk.bold(entry.path)} ).join('\n')}`) const usedDependencies = new Set() - const unusedDependencies = new Set(Object.keys(pkg.dependencies)) + const unusedDependencies = new Set(Object.keys(pkg.dependencies || {})) const implicitDependnecies = new Set() for (const id of usedImports) { unusedDependencies.delete(id) diff --git a/tsconfig.json b/tsconfig.json index edaf2632bf..7e9b960e58 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,6 +2,7 @@ "compilerOptions": { "esModuleInterop": true, "target": "ESNext", + "module": "ESNext", "moduleResolution": "Node", "strict": false, "allowJs": true, diff --git a/yarn.lock b/yarn.lock index 7de448eb29..3983adaf4a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10672,6 +10672,11 @@ unset-value@^1.0.0: has-value "^0.3.1" isobject "^3.0.0" +untyped@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/untyped/-/untyped-0.2.2.tgz#b8c83d2e8f890cd45108495929bb5574b274fa1a" + integrity sha512-AZPhUm/dr/2mnK1mwE67FuV3hbKers34Vp0b8xz26b9JwPg62bvVJGHyWU2rC05acW6tO6OZHfqE5m1J/JjtPg== + upath@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/upath/-/upath-2.0.1.tgz#50c73dea68d6f6b990f51d279ce6081665d61a8b"