vite-plugin-vendure-dashboard.ts 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. import tailwindcss from '@tailwindcss/vite';
  2. import { TanStackRouterVite } from '@tanstack/router-plugin/vite';
  3. import react from '@vitejs/plugin-react';
  4. import path from 'path';
  5. import { PluginOption } from 'vite';
  6. import { PathAdapter } from './types.js';
  7. import { PackageScannerConfig } from './utils/compiler.js';
  8. import { adminApiSchemaPlugin } from './vite-plugin-admin-api-schema.js';
  9. import { configLoaderPlugin } from './vite-plugin-config-loader.js';
  10. import { viteConfigPlugin } from './vite-plugin-config.js';
  11. import { dashboardMetadataPlugin } from './vite-plugin-dashboard-metadata.js';
  12. import { gqlTadaPlugin } from './vite-plugin-gql-tada.js';
  13. import { dashboardTailwindSourcePlugin } from './vite-plugin-tailwind-source.js';
  14. import { themeVariablesPlugin, ThemeVariablesPluginOptions } from './vite-plugin-theme.js';
  15. import { transformIndexHtmlPlugin } from './vite-plugin-transform-index.js';
  16. import { uiConfigPlugin, UiConfigPluginOptions } from './vite-plugin-ui-config.js';
  17. /**
  18. * @description
  19. * Options for the {@link vendureDashboardPlugin} Vite plugin.
  20. */
  21. export type VitePluginVendureDashboardOptions = {
  22. /**
  23. * @description
  24. * The path to the Vendure server configuration file.
  25. */
  26. vendureConfigPath: string | URL;
  27. /**
  28. * @description
  29. * The {@link PathAdapter} allows you to customize the resolution of paths
  30. * in the compiled Vendure source code which is used as part of the
  31. * introspection step of building the dashboard.
  32. *
  33. * It enables support for more complex repository structures, such as
  34. * monorepos, where the Vendure server configuration file may not
  35. * be located in the root directory of the project.
  36. *
  37. * If you get compilation errors like "Error loading Vendure config: Cannot find module",
  38. * you probably need to provide a custom `pathAdapter` to resolve the paths correctly.
  39. *
  40. * @example
  41. * ```ts
  42. * vendureDashboardPlugin({
  43. * tempCompilationDir: join(__dirname, './__vendure-dashboard-temp'),
  44. * pathAdapter: {
  45. * getCompiledConfigPath: ({ inputRootDir, outputPath, configFileName }) => {
  46. * const projectName = inputRootDir.split('/libs/')[1].split('/')[0];
  47. * const pathAfterProject = inputRootDir.split(`/libs/${projectName}`)[1];
  48. * const compiledConfigFilePath = `${outputPath}/${projectName}${pathAfterProject}`;
  49. * return path.join(compiledConfigFilePath, configFileName);
  50. * },
  51. * transformTsConfigPathMappings: ({ phase, patterns }) => {
  52. * // "loading" phase is when the compiled Vendure code is being loaded by
  53. * // the plugin, in order to introspect the configuration of your app.
  54. * if (phase === 'loading') {
  55. * return patterns.map((p) =>
  56. * p.replace('libs/', '').replace(/.ts$/, '.js'),
  57. * );
  58. * }
  59. * return patterns;
  60. * },
  61. * },
  62. * // ...
  63. * }),
  64. * ```
  65. */
  66. pathAdapter?: PathAdapter;
  67. /**
  68. * @description
  69. * The name of the exported variable from the Vendure server configuration file, e.g. `config`.
  70. * This is only required if the plugin is unable to auto-detect the name of the exported variable.
  71. */
  72. vendureConfigExport?: string;
  73. /**
  74. * @description
  75. * The path to the directory where the generated GraphQL Tada files will be output.
  76. */
  77. gqlOutputPath?: string;
  78. tempCompilationDir?: string;
  79. disableTansStackRouterPlugin?: boolean;
  80. /**
  81. * @description
  82. * Allows you to customize the location of node_modules & glob patterns used to scan for potential
  83. * Vendure plugins installed as npm packages. If not provided, the compiler will attempt to guess
  84. * the location based on the location of the `@vendure/core` package.
  85. */
  86. pluginPackageScanner?: PackageScannerConfig;
  87. } & UiConfigPluginOptions &
  88. ThemeVariablesPluginOptions;
  89. /**
  90. * @description
  91. * This is a Vite plugin which configures a set of plugins required to build the Vendure Dashboard.
  92. */
  93. export function vendureDashboardPlugin(options: VitePluginVendureDashboardOptions): PluginOption[] {
  94. const tempDir = options.tempCompilationDir ?? path.join(import.meta.dirname, './.vendure-dashboard-temp');
  95. const normalizedVendureConfigPath = getNormalizedVendureConfigPath(options.vendureConfigPath);
  96. const packageRoot = getDashboardPackageRoot();
  97. const linguiConfigPath = path.join(packageRoot, 'lingui.config.js');
  98. if (process.env.IS_LOCAL_DEV !== 'true') {
  99. process.env.LINGUI_CONFIG = linguiConfigPath;
  100. }
  101. return [
  102. // TODO: solve https://github.com/kentcdodds/babel-plugin-macros/issues/87
  103. // lingui(),
  104. ...(options.disableTansStackRouterPlugin
  105. ? []
  106. : [
  107. TanStackRouterVite({
  108. autoCodeSplitting: true,
  109. routeFileIgnorePattern: '.graphql.ts|components|hooks|utils',
  110. routesDirectory: path.join(packageRoot, 'src/app/routes'),
  111. generatedRouteTree: path.join(packageRoot, 'src/app/routeTree.gen.ts'),
  112. }),
  113. ]),
  114. react({
  115. // babel: {
  116. // plugins: ['@lingui/babel-plugin-lingui-macro'],
  117. // },
  118. }),
  119. themeVariablesPlugin({ theme: options.theme }),
  120. dashboardTailwindSourcePlugin(),
  121. tailwindcss(),
  122. configLoaderPlugin({
  123. vendureConfigPath: normalizedVendureConfigPath,
  124. outputPath: tempDir,
  125. pathAdapter: options.pathAdapter,
  126. pluginPackageScanner: options.pluginPackageScanner,
  127. }),
  128. viteConfigPlugin({ packageRoot }),
  129. adminApiSchemaPlugin(),
  130. dashboardMetadataPlugin(),
  131. uiConfigPlugin(options),
  132. ...(options.gqlOutputPath
  133. ? [gqlTadaPlugin({ gqlTadaOutputPath: options.gqlOutputPath, tempDir, packageRoot })]
  134. : []),
  135. transformIndexHtmlPlugin(),
  136. ];
  137. }
  138. /**
  139. * @description
  140. * Returns the path to the root of the `@vendure/dashboard` package.
  141. */
  142. function getDashboardPackageRoot(): string {
  143. const fileUrl = import.meta.resolve('@vendure/dashboard');
  144. const packagePath = fileUrl.startsWith('file:') ? new URL(fileUrl).pathname : fileUrl;
  145. return fixWindowsPath(path.join(packagePath, '../../../'));
  146. }
  147. /**
  148. * Get the normalized path to the Vendure config file given either a string or URL.
  149. */
  150. export function getNormalizedVendureConfigPath(vendureConfigPath: string | URL): string {
  151. const stringPath = typeof vendureConfigPath === 'string' ? vendureConfigPath : vendureConfigPath.href;
  152. if (stringPath.startsWith('file:')) {
  153. return fixWindowsPath(new URL(stringPath).pathname);
  154. }
  155. return fixWindowsPath(stringPath);
  156. }
  157. function fixWindowsPath(filePath: string): string {
  158. // Fix Windows paths that might start with a leading slash
  159. if (process.platform === 'win32') {
  160. // Remove leading slash before drive letter on Windows
  161. if (/^[/\\][A-Za-z]:/.test(filePath)) {
  162. return filePath.substring(1);
  163. }
  164. }
  165. return filePath;
  166. }