-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Module Improvements #10
Comments
Great points 👍 Having a dedicated utility repo/package for modules is definitely a must-have and will make module development even easier. My thoughts: Class vs. FunctionI'd stick to the create function (personal preference). Would vote against having both approaches/ways built-in because it'll "split" the module developers and doesn't add any real value. But concerning this, it'd make sense to talk to the module maintainers as well so we all can decide on one way to do it. Package name
Place of the moduleProbably an own dedicated repo (maybe even under the Old ModuleContainer codeWe can't remove it for now as it'd be a breaking change, but for 3.X I'd do that. Question of ScopeThe utilities are 'only' for modules (not for replacing, say @nuxt/webpack with an own implementation of @nuxt/parcel), or should they cover that as well? Misc
Likely because it wasn't written down anywhere / suggested to do so 🤷♂️ |
I'm hoping this is the correct issue to add my module feature request to... I would like to see a couple of additional properties added to the module container scope that allow module authors to access both the default Nuxt config and the project Right now the only way of accessing the config is via As a module author, I would like to be able to override some of the default configuration of Nuxt while still allowing consumers of my module to override them via their project For example, I continuously find myself setting // nuxt.config.js
export default {
srcDir: "src",
router: {
linkActiveClass: "link-active",
linkExactActiveClass: "link-active-exact"
},
server: {
host: "0.0.0.0"
}
} Since I find myself doing this on every project, I decided to create a module that sets these values by default: export default function SomeModule() {
this.options.srcDir = "src"
this.options.server.host = "0.0.0.0"
this.options.router.linkActiveClass = "link-active"
this.options.router.linkExactActiveClass = "link-active-exact"
} The problem with this approach is obvious: you are not able to override any of these values via a project's export default {
modules: ["some-module"],
server: {
host: "127.0.0.1" // will be overridden by "some-module"
}
} This presents a problem for option merging strategies within the module container scope since I posted this question in the Discord channel and @manniL kindly responded with a solution using the
export default function SomeModule() {
this.options.srcDir = this.projectConfig.src || "src"
Object.assign(this.options.router, {
linkActiveClass: "link-active",
linkExactActiveClass: "link-active-exact"
}, this.projectConfig.router)
Object.assign(this.options.server, {
host: "0.0.0.0"
}, this.projectConfig.server)
} Feedback welcome 😃 |
Really nice suggestion @wagerfield I guess this could be a possibility to export something like a |
Thanks @atinux, Perhaps a Whatever the solution is—it would have to be flexible enough to allow module authors to granularly control the config merging strategies for different keys. For example, you might want to override some part of the default Nuxt config in your module and not allow consumers of the module to override it in their project config...while in the same module override the default Nuxt config and allow module consumers to override it themselves if desired. Perhaps the function signature of the
Where:
export default function SomeModule() {
// Can be overridden in nuxt.config.js since freeze is omitted
this.config("srcDir", "src")
// Can also be overridden in nuxt.config.js
// Notice that an object is passed here with key values
this.config("server", {
https: true,
port: 8080
})
// Cannot be overridden in nuxt.config.js since freeze is true
this.config("server.host", "0.0.0.0", true)
// Cannot be overridden in nuxt.config.js
this.config("router", {
linkActiveClass: "link-active",
linkExactActiveClass: "link-active-exact"
}, true)
} To you suggestion above, if there were to be any named keys or param names in a |
Nice idea about config overriding/preset @wagerfield. But config handling is out of module scope. The order of a nuxt project bootstrap is: 1.CLI > 2.Read Config > 3.Normalize Options > 4.Run Modules > 5.Ready (Including Listen, ...) All current modules assume that shape of I suggest moving this enhancement to #16 (Improve Config) by allowing presets in {
presets: [
'nework',
'@company/defaults'
]
} Presets can have an interface just like PS: As described in original RFC I suggest keeping |
With regards to Class vs Function, I dont mind either or both as long we dont have to make any knee falls for them in regards to usability. This is in reference to the current NuxtCommand implementation in The name |
Having read over everyone's comments again, here's are some more thoughts on this... I think the new module SDK/API should live in its own package within the monorepo—not in a separate repo. It's ultimately apart of Nuxt, so it makes the most sense to me for it to live alongside the other packages—making it easier to test and update the docs, change log etc. with each new version. Of the package suggestions from @pi0 I like I also prefer the wrapper function over extending a class and agree with @manniL that you should only be able to do one in order to maintain consistency in modules authored by the community and the team. Having said that, do we need to wrap the module options in a function at all? Could you not just follow suit of Vue component options and simply export an object that Nuxt then wraps during setup when modules are registered in the Nuxt config? Here's my proposal extending from your earlier one @pi0: import { addPlugin, addModule } from '@nuxt/module'
import pkg from './package.json'
const moduleConfig = {
router: {
linkActiveClass: "link-active",
linkExactActiveClass: "link-active-exact"
},
server: {
host: "0.0.0.0"
}
}
export default {
name: 'PWA', // required
meta: pkg, // required
// Could be a String or String[] to allow modules
// to register multiple options keys (like the PWA module)
options: ['icon', 'manifest', 'meta', 'workbox'], // String[]
options: 'sitemap', // String
// Extend a single Nuxt config or an array of Nuxt configs
// Useful for creating modules that configure Nuxt a certain way
// Can be an plain config object that is declared locally or imported
// Or a string path that uses node's resolver to require it
// Or an array of objects and string paths
extends: ["./config", "some-nuxt-config-preset", moduleConfig],
hooks: {
nuxt: {
init(nuxt) {
// 'this' would be bound to the result of 'wrapping' the module object
// eg. this === createModule(module)
// where 'createModule' is called by Nuxt during setup
// and 'module' is this module definition object
console.log(this) // { name: 'PWA', options... }
// this.options would return a 'resolved' object with just the module options (if set)
// if options is a string it will return the options value assigned to it
// if options is an array of strings it will return an object with each of those key values
console.log(this.options) // { icon: undefined, manifest: { ... }, meta: { ... }, workbox: false }
// nuxt.config would return the loaded Nuxt config, undefined otherwise
console.log(nuxt.config) // { srcDir: "src", css: ["normalize.css"] }
// nuxt.options would return what this.options does currently
console.log(nuxt.options) // { srcDir: "src", css: ["normalize.css"], icon: {}, manifest: {} ... }
}
},
build: {
before(nuxt, builder) {
addPlugin(nuxt, './plugin.js', { /* options */ })
addModule(nuxt, './module.js', { /* options */ })
}
}
}
} I have expanded the hooks into nested functions in the same way that they are documented here: https://nuxtjs.org/api/configuration-hooks Personally I prefer this as I think it's cleaner than To collate all that has been discussed so far, here is a table of keys that could serve as some initial documentation of the Module object API:
|
@wagerfield Nice write-up. Thanks. I appreciate it. Points I can extract from it as TODO for RFC:
Regardin the place of
|
TLDR
A powerful engine is not usable without a good interface. Nuxt modular refactor made a long time ago but it is still lacking a good SDK.
History
The initial proposal for modules started with a project called nuxt-helpers, a wrapper function around export inside
nuxt.config.js
. Nuxt was hackable enough to inject any parts of it.But we wanted a more stable and official way to access and modify Nuxt internals and hack things beyond webpack
extend
.Nuxt core has been broken into smaller modules with their specific tasks that inherit a global
options
object and communicate with each usingNuxt
class (The hub).Then we needed to create an entry point for modules that can start hacking Nuxt from zero to build and start. We thought about different ways like an exported object that contains strict options but it was against the creativity of module authors. With a simple function, module author is free to get the nuxt instance and hook-up into any part of built-ins. Actually, this pattern is powerful more than enough. I've never thought we should change it. Modules like nuxt7 built on top of this simple pattern can customize anything!
So the problem
Modules, while being free to touch any internal by using
nuxt
reference should be somehow isolated not only because of providing them utilities likeaddPlugin()
but also because nuxt internals (or it's dependencies like webpack 3 => webpack 4) may be changed at any time. The bad decision (most of mine) was usingModuleContainer
for utility container. This has several disadvantages:ModuleContainer
is bound to the nuxt runtime (ie, currently running nuxt version). Modules don't know who is running them and what utilities are available for them! This is why after initial version almost nothing new supported inModuleContainer
and instead module authors had to write their own.moduleContainer
context. Splitting module into smaller logical functions is like a pain in a**!this.nuxt.hook
guard to lazy require and only configure webpack options when we are building project but in reality, no-one did this (Maybe because it was hard?)nuxt-start
requires all module packages to be installed. Only the built-only ones.The solution
The module utilities (SDK) should be provided as a separated package (Maybe even a dedicated repository in
nuxt-community
). Other than regular utilities this package can provide a HOC (wrapper function) and Base class that finally evaluates into a plain function usable by Nuxt engine. This guarantees both old and new modules can work with old and new versions of Nuxt while module authors are just encouraged to use SDK instead of using legacyModuleContainer
helpers. Module authors can create their HOC functions too.Usage
With class
With wrapper function
Personally, I like the wrapper pattern as it is closer to the Vue exports, is simpler to write (no
export()
call) and most importantly it is much better for implementing lazy-requires that are basically context-less pure functions. But we may implement both :)Chore
@nuxt/sdk
|@nuxt/module
|@nuxt/module-utilities
?@nuxt/core
on the latest version of it?The text was updated successfully, but these errors were encountered: