-
-
Notifications
You must be signed in to change notification settings - Fork 1k
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
feat(ui-devkit): Support module path mappings for UI extensions #1994
feat(ui-devkit): Support module path mappings for UI extensions #1994
Conversation
const tsconfigFilePath = path.join(outputPath, 'tsconfig.json'); | ||
const indexFilePath = path.join(outputPath, '/src/index.html'); | ||
if (fs.existsSync(tsconfigFilePath) && fs.existsSync(indexFilePath)) { | ||
addModulePathMappingIfDefined(tsconfigFilePath, modulePathMapping); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because the mapping is not a file and cannot be watched, we need to ensure it gets updated every time the local server starts again/a file is changed.
This is an excellent addition and an overall great PR as usual :) This exact issue has come up a few times, so thanks for coming up with a solution. Before merging, I've got an idea I'd like to explore. Would it be possible to, instead of defining a new kind of extension just for the mapping, to include its functionality in the existing export const uiExtensions: AdminUiExtension = {
id: 'common-ui', // this is important
modulePathMapping: {
'@common-module/ui': 'common-ui/ui-shared.module.ts',
},
extensionPath: path.join(__dirname, 'ui'),
ngModules: [
{
type: 'shared' as const,
ngModuleFileName: 'ui-shared.module.ts',
ngModuleName: 'CommonSharedUiModule',
},
],
}; Advantages are
Actually I hard a further thought:
export const uiExtensions: AdminUiExtension = {
id: 'common-ui', // this is important
extensionPath: path.join(__dirname, 'ui'),
ngModules: [
{
type: 'shared' as const,
ngModuleFileName: 'ui-shared.module.ts',
ngModuleName: 'CommonSharedUiModule',
pathMapping: '@common-module/ui',
},
],
}; What do you think? Are either of those doable? |
59d67b8
to
d266c61
Compare
@michaelbromley Those were great suggestions, I also prefer everything configured in only one place. I just went one step further and ditched the need for an explicit I've updated the PR description accordingly. |
@@ -280,3 +368,7 @@ export interface BrandingOptions { | |||
largeLogoPath?: string; | |||
faviconPath?: string; | |||
} | |||
|
|||
export interface AdminUiExtensionWithId extends AdminUiExtension { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not very elegant but that was the only way I found to make id
required but not pathAlias
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's fine - it communicates the type and it's internal anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I absolutely love it! Great work!
One small thing to change on the docs, then we're good to go!
@@ -280,3 +368,7 @@ export interface BrandingOptions { | |||
largeLogoPath?: string; | |||
faviconPath?: string; | |||
} | |||
|
|||
export interface AdminUiExtensionWithId extends AdminUiExtension { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's fine - it communicates the type and it's internal anyway.
* @example | ||
* ```ts | ||
* // packages/common-ui-module/src/ui/ui-shared.module.ts | ||
* import { NgModule } from '@angular/core'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In order to not break the docs, every @
character in a code block needs to be escaped.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done! Is there a way to check the final docs as they appear on the website? I can see the md
file generated after running yarn docs:generate-typescript-docs
locally but I don't think I have the right tool to visualise it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks!
Unfortunately not at the moment. I plan to move the doc gen stuff back into the monorepo soon when we update our website.
🥳 |
Context
Given the composable and extensible nature of Vendure, a monorepo codebase where each package encapsulates a custom plugin is most likely a common practice. Often times, different plugins that include Admin UI extensions share similar behaviour/UX and could depend on the same set of custom Angular components, services, etc. However, considering how Admin UI extensions are compiled - the
extensionPath
directory gets copied as is to the output and is "self-contained" so to speak - referencing code outside the folder and, more importantly, modules declared in other extensions is tricky and unintuitive. Sure, you can giveid
s toAdminUiExtension
s and use relative imports but that requires knowledge of the inner workings ofui-devkit
and is a brittle approach as the implementation details might change at some point, rendering the solution obsolete (and broken). Additionally, relative paths make sense in the context of theui-devkit
build process but not to the project's main TypeScript configuration since the path will always be invalid. As a result, IDE features such as code completion and quick navigation, as well as linting do not work. An alternative is to create an NPM package for the common module, but besides all the hassle (build/packaging process, private NPM registry, CI, etc) it doesn't give you the same developer experience where changes can be immediately verified.Proposed solution
Allow developers to specify module path mapping for UI extensions so they are able to import code from other bundled extensions, while at the same time maintaining compatibility with the project's main
tsconfig.json
.Description of changes
@vendure/ui-devkit
to support a newpathAlias
property inAdminUiExtension
objects, which represents an alias for the module so it can be referenced inimport
statements from other UI extensions. The path is resolved relative to theextensions
directory of the compiledadmin-ui
folder, whose children are every declared UI extension. A full example has been added as a docstring.scaffold.ts
to check if any extensions havepathAlias
defined; if that's the case, update thepaths
property of the bundledtsconfig.json
file to map eachpathAlias
to its corresponding compiled folder. Since the attribute is resolved relative tobaseUrl
- the root of the compiledadmin-ui
folder - each path is prepended withsrc/extensions/
.scaffold.ts
.Notes