@vendure/dashboard 3.2.2 → 3.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/dist/plugin/utils/ast-utils.d.ts +10 -0
  2. package/dist/plugin/utils/ast-utils.js +96 -0
  3. package/dist/plugin/utils/ast-utils.spec.d.ts +1 -0
  4. package/dist/plugin/utils/ast-utils.spec.js +120 -0
  5. package/dist/plugin/{config-loader.d.ts → utils/config-loader.d.ts} +22 -8
  6. package/dist/plugin/utils/config-loader.js +325 -0
  7. package/dist/plugin/{schema-generator.d.ts → utils/schema-generator.d.ts} +5 -0
  8. package/dist/plugin/{schema-generator.js → utils/schema-generator.js} +6 -0
  9. package/dist/plugin/{ui-config.js → utils/ui-config.js} +2 -2
  10. package/dist/plugin/vite-plugin-admin-api-schema.js +2 -2
  11. package/dist/plugin/vite-plugin-config-loader.d.ts +2 -3
  12. package/dist/plugin/vite-plugin-config-loader.js +18 -9
  13. package/dist/plugin/vite-plugin-dashboard-metadata.js +12 -14
  14. package/dist/plugin/vite-plugin-gql-tada.js +2 -2
  15. package/dist/plugin/vite-plugin-ui-config.js +3 -2
  16. package/package.json +8 -6
  17. package/src/app/app-providers.tsx +8 -8
  18. package/src/app/main.tsx +1 -1
  19. package/src/app/routes/_authenticated/_assets/assets.graphql.ts +26 -0
  20. package/src/app/routes/_authenticated/_assets/assets.tsx +2 -2
  21. package/src/app/routes/_authenticated/_assets/assets_.$id.tsx +156 -0
  22. package/src/app/routes/_authenticated/_orders/components/customer-address-selector.tsx +104 -0
  23. package/src/app/routes/_authenticated/_orders/components/edit-order-table.tsx +228 -0
  24. package/src/app/routes/_authenticated/_orders/components/money-gross-net.tsx +18 -0
  25. package/src/app/routes/_authenticated/_orders/components/order-address.tsx +2 -1
  26. package/src/app/routes/_authenticated/_orders/components/order-line-custom-fields-form.tsx +38 -0
  27. package/src/app/routes/_authenticated/_orders/components/order-table-totals.tsx +53 -0
  28. package/src/app/routes/_authenticated/_orders/components/order-table.tsx +8 -49
  29. package/src/app/routes/_authenticated/_orders/components/shipping-method-selector.tsx +65 -0
  30. package/src/app/routes/_authenticated/_orders/orders.graphql.ts +187 -1
  31. package/src/app/routes/_authenticated/_orders/orders.tsx +39 -18
  32. package/src/app/routes/_authenticated/_orders/orders_.$id.tsx +31 -9
  33. package/src/app/routes/_authenticated/_orders/orders_.draft.$id.tsx +418 -0
  34. package/src/app/routes/_authenticated/_products/products.tsx +1 -1
  35. package/src/app/routes/_authenticated.tsx +12 -1
  36. package/src/lib/components/data-table/add-filter-menu.tsx +61 -0
  37. package/src/lib/components/data-table/data-table-column-header.tsx +0 -13
  38. package/src/lib/components/data-table/data-table-filter-badge.tsx +75 -0
  39. package/src/lib/components/data-table/data-table-filter-dialog.tsx +27 -28
  40. package/src/lib/components/data-table/data-table-types.ts +1 -0
  41. package/src/lib/components/data-table/data-table-view-options.tsx +72 -23
  42. package/src/lib/components/data-table/data-table.tsx +23 -24
  43. package/src/lib/components/data-table/filters/data-table-boolean-filter.tsx +57 -0
  44. package/src/lib/components/data-table/filters/data-table-datetime-filter.tsx +93 -0
  45. package/src/lib/components/data-table/filters/data-table-id-filter.tsx +58 -0
  46. package/src/lib/components/data-table/filters/data-table-number-filter.tsx +119 -0
  47. package/src/lib/components/data-table/filters/data-table-string-filter.tsx +62 -0
  48. package/src/lib/components/data-table/human-readable-operator.tsx +65 -0
  49. package/src/lib/components/layout/nav-user.tsx +4 -4
  50. package/src/lib/components/shared/asset/asset-focal-point-editor.tsx +93 -0
  51. package/src/lib/components/shared/{asset-gallery.tsx → asset/asset-gallery.tsx} +51 -20
  52. package/src/lib/components/shared/{asset-picker-dialog.tsx → asset/asset-picker-dialog.tsx} +1 -1
  53. package/src/lib/components/shared/{asset-preview-dialog.tsx → asset/asset-preview-dialog.tsx} +1 -7
  54. package/src/lib/components/shared/asset/asset-preview-selector.tsx +34 -0
  55. package/src/lib/components/shared/asset/asset-preview.tsx +128 -0
  56. package/src/lib/components/shared/asset/asset-properties.tsx +46 -0
  57. package/src/lib/components/shared/{focal-point-control.tsx → asset/focal-point-control.tsx} +1 -1
  58. package/src/lib/components/shared/custom-fields-form.tsx +4 -3
  59. package/src/lib/components/shared/customer-selector.tsx +13 -14
  60. package/src/lib/components/shared/detail-page-button.tsx +2 -2
  61. package/src/lib/components/shared/entity-assets.tsx +3 -3
  62. package/src/lib/components/shared/navigation-confirmation.tsx +39 -0
  63. package/src/lib/components/shared/paginated-list-data-table.tsx +9 -1
  64. package/src/lib/components/shared/product-variant-selector.tsx +111 -0
  65. package/src/lib/components/shared/vendure-image.tsx +1 -1
  66. package/src/lib/components/ui/calendar.tsx +508 -63
  67. package/src/lib/framework/document-introspection/get-document-structure.spec.ts +113 -3
  68. package/src/lib/framework/document-introspection/get-document-structure.ts +70 -11
  69. package/src/lib/framework/form-engine/use-generated-form.tsx +8 -7
  70. package/src/lib/framework/layout-engine/page-layout.tsx +4 -0
  71. package/src/lib/framework/page/list-page.tsx +23 -4
  72. package/src/lib/framework/page/use-detail-page.ts +1 -0
  73. package/src/lib/graphql/fragments.tsx +8 -0
  74. package/src/lib/index.ts +5 -5
  75. package/src/lib/providers/auth.tsx +12 -9
  76. package/src/lib/providers/channel-provider.tsx +1 -0
  77. package/src/lib/providers/server-config.tsx +7 -1
  78. package/src/lib/providers/user-settings.tsx +24 -0
  79. package/vite/utils/ast-utils.spec.ts +128 -0
  80. package/vite/utils/ast-utils.ts +119 -0
  81. package/vite/utils/config-loader.ts +410 -0
  82. package/vite/{schema-generator.ts → utils/schema-generator.ts} +7 -1
  83. package/vite/{ui-config.ts → utils/ui-config.ts} +2 -2
  84. package/vite/vite-plugin-admin-api-schema.ts +2 -2
  85. package/vite/vite-plugin-config-loader.ts +25 -13
  86. package/vite/vite-plugin-dashboard-metadata.ts +19 -15
  87. package/vite/vite-plugin-gql-tada.ts +2 -2
  88. package/vite/vite-plugin-ui-config.ts +3 -2
  89. package/dist/plugin/config-loader.js +0 -141
  90. package/src/lib/components/shared/asset-preview.tsx +0 -345
  91. package/vite/config-loader.ts +0 -181
  92. /package/dist/plugin/{ui-config.d.ts → utils/ui-config.d.ts} +0 -0
@@ -0,0 +1,10 @@
1
+ import ts from 'typescript';
2
+ import { PluginInfo } from './config-loader.js';
3
+ /**
4
+ * Get the plugin info from the source file.
5
+ */
6
+ export declare function getPluginInfo(sourceFile: ts.SourceFile): PluginInfo | undefined;
7
+ /**
8
+ * Given the AST of a TypeScript file, finds the name of the variable exported as VendureConfig.
9
+ */
10
+ export declare function findConfigExport(sourceFile: ts.SourceFile): string | undefined;
@@ -0,0 +1,96 @@
1
+ import path from 'path';
2
+ import ts from 'typescript';
3
+ /**
4
+ * Get the plugin info from the source file.
5
+ */
6
+ export function getPluginInfo(sourceFile) {
7
+ var _a;
8
+ const classDeclaration = sourceFile.statements.find(statement => {
9
+ return (statement.kind === ts.SyntaxKind.ClassDeclaration &&
10
+ statement.getText().includes('@VendurePlugin('));
11
+ });
12
+ if (classDeclaration) {
13
+ const identifier = classDeclaration.getChildren().find(child => {
14
+ return child.kind === ts.SyntaxKind.Identifier;
15
+ });
16
+ const dashboardEntryPath = (_a = classDeclaration
17
+ .getChildren()
18
+ .map(child => {
19
+ if (child.kind === ts.SyntaxKind.SyntaxList) {
20
+ const pluginDecorator = child.getChildren().find(_child => {
21
+ return _child.kind === ts.SyntaxKind.Decorator;
22
+ });
23
+ if (pluginDecorator) {
24
+ const callExpression = findFirstDescendantOfKind(pluginDecorator, ts.SyntaxKind.CallExpression);
25
+ if (callExpression) {
26
+ const objectLiteral = findFirstDescendantOfKind(callExpression, ts.SyntaxKind.ObjectLiteralExpression);
27
+ if (objectLiteral && ts.isObjectLiteralExpression(objectLiteral)) {
28
+ // Now find the specific 'dashboard' property
29
+ const dashboardProperty = objectLiteral.properties.find(prop => { var _a; return ts.isPropertyAssignment(prop) && ((_a = prop.name) === null || _a === void 0 ? void 0 : _a.getText()) === 'dashboard'; });
30
+ if (dashboardProperty &&
31
+ ts.isPropertyAssignment(dashboardProperty) &&
32
+ ts.isStringLiteral(dashboardProperty.initializer)) {
33
+ const dashboardPath = dashboardProperty.initializer.text;
34
+ return dashboardPath;
35
+ }
36
+ }
37
+ }
38
+ }
39
+ }
40
+ })
41
+ .filter(Boolean)) === null || _a === void 0 ? void 0 : _a[0];
42
+ if (identifier) {
43
+ return {
44
+ name: identifier.getText(),
45
+ pluginPath: path.dirname(sourceFile.fileName),
46
+ dashboardEntryPath,
47
+ };
48
+ }
49
+ }
50
+ }
51
+ /**
52
+ * Given the AST of a TypeScript file, finds the name of the variable exported as VendureConfig.
53
+ */
54
+ export function findConfigExport(sourceFile) {
55
+ let exportedSymbolName;
56
+ function visit(node) {
57
+ var _a;
58
+ if (ts.isVariableStatement(node) &&
59
+ ((_a = node.modifiers) === null || _a === void 0 ? void 0 : _a.some(m => m.kind === ts.SyntaxKind.ExportKeyword))) {
60
+ node.declarationList.declarations.forEach(declaration => {
61
+ if (ts.isVariableDeclaration(declaration)) {
62
+ const typeNode = declaration.type;
63
+ if (typeNode && ts.isTypeReferenceNode(typeNode)) {
64
+ const typeName = typeNode.typeName;
65
+ if (ts.isIdentifier(typeName) && typeName.text === 'VendureConfig') {
66
+ if (ts.isIdentifier(declaration.name)) {
67
+ exportedSymbolName = declaration.name.text;
68
+ }
69
+ }
70
+ }
71
+ }
72
+ });
73
+ }
74
+ ts.forEachChild(node, visit);
75
+ }
76
+ visit(sourceFile);
77
+ return exportedSymbolName;
78
+ }
79
+ function findFirstDescendantOfKind(node, kind) {
80
+ let foundNode;
81
+ function visit(_node) {
82
+ if (foundNode) {
83
+ // Stop searching if we already found it
84
+ return;
85
+ }
86
+ if (_node.kind === kind) {
87
+ foundNode = _node;
88
+ return;
89
+ }
90
+ // Recursively visit children
91
+ ts.forEachChild(_node, visit);
92
+ }
93
+ // Start the traversal from the initial node's children
94
+ ts.forEachChild(node, visit);
95
+ return foundNode;
96
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,120 @@
1
+ import ts from 'typescript';
2
+ import { describe, it, expect } from 'vitest';
3
+ import { getPluginInfo, findConfigExport } from './ast-utils.js';
4
+ describe('getPluginInfo', () => {
5
+ it('should return undefined when no plugin class is found', () => {
6
+ const sourceText = `
7
+ class NotAPlugin {
8
+ constructor() {}
9
+ }
10
+ `;
11
+ const sourceFile = ts.createSourceFile('path/to/test.ts', sourceText, ts.ScriptTarget.Latest, true);
12
+ const result = getPluginInfo(sourceFile);
13
+ expect(result).toBeUndefined();
14
+ });
15
+ it('should return plugin info when a valid plugin class is found', () => {
16
+ const sourceText = `
17
+ @VendurePlugin({
18
+ imports: [],
19
+ providers: []
20
+ })
21
+ class TestPlugin {
22
+ constructor() {}
23
+ }
24
+ `;
25
+ const sourceFile = ts.createSourceFile('path/to/test.ts', sourceText, ts.ScriptTarget.Latest, true);
26
+ const result = getPluginInfo(sourceFile);
27
+ expect(result).toEqual({
28
+ name: 'TestPlugin',
29
+ pluginPath: 'path/to',
30
+ dashboardEntryPath: undefined,
31
+ });
32
+ });
33
+ it('should handle multiple classes but only return the plugin one', () => {
34
+ const sourceText = `
35
+ class NotAPlugin {
36
+ constructor() {}
37
+ }
38
+
39
+ @VendurePlugin({
40
+ imports: [],
41
+ providers: []
42
+ })
43
+ class TestPlugin {
44
+ constructor() {}
45
+ }
46
+
47
+ class AnotherClass {
48
+ constructor() {}
49
+ }
50
+ `;
51
+ const sourceFile = ts.createSourceFile('path/to/test.ts', sourceText, ts.ScriptTarget.Latest, true);
52
+ const result = getPluginInfo(sourceFile);
53
+ expect(result).toEqual({
54
+ name: 'TestPlugin',
55
+ pluginPath: 'path/to',
56
+ dashboardEntryPath: undefined,
57
+ });
58
+ });
59
+ it('should determine the dashboard entry path when it is provided', () => {
60
+ const sourceText = `
61
+ @VendurePlugin({
62
+ imports: [],
63
+ providers: [],
64
+ dashboard: './dashboard/index.tsx',
65
+ })
66
+ class TestPlugin {
67
+ constructor() {}
68
+ }
69
+ `;
70
+ const sourceFile = ts.createSourceFile('path/to/test.ts', sourceText, ts.ScriptTarget.Latest, true);
71
+ const result = getPluginInfo(sourceFile);
72
+ expect(result).toEqual({
73
+ name: 'TestPlugin',
74
+ pluginPath: 'path/to',
75
+ dashboardEntryPath: './dashboard/index.tsx',
76
+ });
77
+ });
78
+ });
79
+ describe('findConfigExport', () => {
80
+ it('should return undefined when no VendureConfig export is found', () => {
81
+ const sourceText = `
82
+ export const notConfig = {
83
+ some: 'value'
84
+ };
85
+ `;
86
+ const sourceFile = ts.createSourceFile('path/to/test.ts', sourceText, ts.ScriptTarget.Latest, true);
87
+ const result = findConfigExport(sourceFile);
88
+ expect(result).toBeUndefined();
89
+ });
90
+ it('should find exported variable with VendureConfig type', () => {
91
+ const sourceText = `
92
+ import { VendureConfig } from '@vendure/core';
93
+
94
+ export const config: VendureConfig = {
95
+ authOptions: {
96
+ tokenMethod: 'bearer'
97
+ }
98
+ };
99
+ `;
100
+ const sourceFile = ts.createSourceFile('path/to/test.ts', sourceText, ts.ScriptTarget.Latest, true);
101
+ const result = findConfigExport(sourceFile);
102
+ expect(result).toBe('config');
103
+ });
104
+ it('should find exported variable with VendureConfig type among other exports', () => {
105
+ const sourceText = `
106
+ import { VendureConfig } from '@vendure/core';
107
+
108
+ export const otherExport = 'value';
109
+ export const config: VendureConfig = {
110
+ authOptions: {
111
+ tokenMethod: 'bearer'
112
+ }
113
+ };
114
+ export const anotherExport = 123;
115
+ `;
116
+ const sourceFile = ts.createSourceFile('path/to/test.ts', sourceText, ts.ScriptTarget.Latest, true);
117
+ const result = findConfigExport(sourceFile);
118
+ expect(result).toBe('config');
119
+ });
120
+ });
@@ -1,8 +1,24 @@
1
1
  import { VendureConfig } from '@vendure/core';
2
+ type Logger = {
3
+ info: (message: string) => void;
4
+ warn: (message: string) => void;
5
+ debug: (message: string) => void;
6
+ };
7
+ export type PluginInfo = {
8
+ name: string;
9
+ pluginPath: string;
10
+ dashboardEntryPath: string | undefined;
11
+ };
2
12
  export interface ConfigLoaderOptions {
3
13
  vendureConfigPath: string;
4
14
  tempDir: string;
5
15
  vendureConfigExport?: string;
16
+ logger?: Logger;
17
+ }
18
+ export interface LoadVendureConfigResult {
19
+ vendureConfig: VendureConfig;
20
+ exportedSymbolName: string;
21
+ pluginInfo: PluginInfo[];
6
22
  }
7
23
  /**
8
24
  * @description
@@ -16,12 +32,10 @@ export interface ConfigLoaderOptions {
16
32
  * internally uses esbuild to temporarily compile that TypeScript code. Unfortunately, esbuild does not support
17
33
  * these experimental decorators, errors will be thrown as soon as e.g. a TypeORM column decorator is encountered.
18
34
  *
19
- * To work around this, we compile the Vendure config file and all its imports using SWC, which does support
20
- * these experimental decorators. The compiled files are then loaded by Vite, which is able to handle the compiled
21
- * JavaScript output.
35
+ * To work around this, we compile the Vendure config file and all its imports using the TypeScript compiler,
36
+ * which fully supports these experimental decorators. The compiled files are then loaded by Vite, which is able
37
+ * to handle the compiled JavaScript output.
22
38
  */
23
- export declare function loadVendureConfig(options: ConfigLoaderOptions): Promise<{
24
- vendureConfig: VendureConfig;
25
- exportedSymbolName: string;
26
- }>;
27
- export declare function compileFile(inputRootDir: string, inputPath: string, outputDir: string, compiledFiles?: Set<string>): Promise<void>;
39
+ export declare function loadVendureConfig(options: ConfigLoaderOptions): Promise<LoadVendureConfigResult>;
40
+ export declare function compileFile(inputRootDir: string, inputPath: string, outputDir: string, logger?: Logger, compiledFiles?: Set<string>, isRoot?: boolean, pluginInfo?: PluginInfo[]): Promise<PluginInfo[]>;
41
+ export {};
@@ -0,0 +1,325 @@
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+ import tsConfigPaths from 'tsconfig-paths';
4
+ import * as ts from 'typescript';
5
+ import { pathToFileURL } from 'url';
6
+ import { findConfigExport, getPluginInfo } from './ast-utils.js';
7
+ const defaultLogger = {
8
+ info: (message) => {
9
+ /* noop */
10
+ },
11
+ warn: (message) => {
12
+ /* noop */
13
+ },
14
+ debug: (message) => {
15
+ /* noop */
16
+ },
17
+ };
18
+ /**
19
+ * @description
20
+ * This function compiles the given Vendure config file and any imported relative files (i.e.
21
+ * project files, not npm packages) into a temporary directory, and returns the compiled config.
22
+ *
23
+ * The reason we need to do this is that Vendure code makes use of TypeScript experimental decorators
24
+ * (e.g. for NestJS decorators and TypeORM column decorators) which are not supported by esbuild.
25
+ *
26
+ * In Vite, when we load some TypeScript into the top-level Vite config file (in the end-user project), Vite
27
+ * internally uses esbuild to temporarily compile that TypeScript code. Unfortunately, esbuild does not support
28
+ * these experimental decorators, errors will be thrown as soon as e.g. a TypeORM column decorator is encountered.
29
+ *
30
+ * To work around this, we compile the Vendure config file and all its imports using the TypeScript compiler,
31
+ * which fully supports these experimental decorators. The compiled files are then loaded by Vite, which is able
32
+ * to handle the compiled JavaScript output.
33
+ */
34
+ export async function loadVendureConfig(options) {
35
+ const { vendureConfigPath, vendureConfigExport, tempDir } = options;
36
+ const logger = options.logger || defaultLogger;
37
+ const outputPath = tempDir;
38
+ const configFileName = path.basename(vendureConfigPath);
39
+ const inputRootDir = path.dirname(vendureConfigPath);
40
+ await fs.remove(outputPath);
41
+ const pluginInfo = await compileFile(inputRootDir, vendureConfigPath, outputPath, logger);
42
+ const compiledConfigFilePath = pathToFileURL(path.join(outputPath, configFileName)).href.replace(/.ts$/, '.js');
43
+ // create package.json with type commonjs and save it to the output dir
44
+ await fs.writeFile(path.join(outputPath, 'package.json'), JSON.stringify({ type: 'commonjs' }, null, 2));
45
+ // We need to figure out the symbol exported by the config file by
46
+ // analyzing the AST and finding an export with the type "VendureConfig"
47
+ const sourceFile = ts.createSourceFile(vendureConfigPath, await fs.readFile(vendureConfigPath, 'utf-8'), ts.ScriptTarget.Latest, true);
48
+ const detectedExportedSymbolName = findConfigExport(sourceFile);
49
+ const configExportedSymbolName = detectedExportedSymbolName || vendureConfigExport;
50
+ if (!configExportedSymbolName) {
51
+ throw new Error(`Could not find a variable exported as VendureConfig. Please specify the name of the exported variable using the "vendureConfigExport" option.`);
52
+ }
53
+ // Register path aliases from tsconfig before importing
54
+ const tsConfigInfo = await findTsConfigPaths(vendureConfigPath, logger);
55
+ if (tsConfigInfo) {
56
+ tsConfigPaths.register({
57
+ baseUrl: outputPath,
58
+ paths: tsConfigInfo.paths,
59
+ });
60
+ }
61
+ const config = await import(compiledConfigFilePath).then(m => m[configExportedSymbolName]);
62
+ if (!config) {
63
+ throw new Error(`Could not find a variable exported as VendureConfig with the name "${configExportedSymbolName}".`);
64
+ }
65
+ return { vendureConfig: config, exportedSymbolName: configExportedSymbolName, pluginInfo };
66
+ }
67
+ /**
68
+ * Finds and parses tsconfig files in the given directory and its parent directories.
69
+ * Returns the paths configuration if found.
70
+ */
71
+ async function findTsConfigPaths(configPath, logger) {
72
+ const configDir = path.dirname(configPath);
73
+ let currentDir = configDir;
74
+ while (currentDir !== path.parse(currentDir).root) {
75
+ try {
76
+ const files = await fs.readdir(currentDir);
77
+ const tsConfigFiles = files.filter(file => /^tsconfig(\..*)?\.json$/.test(file));
78
+ for (const fileName of tsConfigFiles) {
79
+ const tsConfigPath = path.join(currentDir, fileName);
80
+ try {
81
+ const tsConfigContent = await fs.readFile(tsConfigPath, 'utf-8');
82
+ // Use JSON5 or similar parser if comments are expected in tsconfig.json
83
+ // For simplicity, assuming standard JSON here. Handle parse errors.
84
+ const tsConfig = JSON.parse(tsConfigContent);
85
+ const compilerOptions = tsConfig.compilerOptions || {};
86
+ if (compilerOptions.paths) {
87
+ // Determine the effective baseUrl: explicitly set or the directory of tsconfig.json
88
+ const tsConfigBaseUrl = path.resolve(currentDir, compilerOptions.baseUrl || '.');
89
+ const paths = {};
90
+ for (const [alias, patterns] of Object.entries(compilerOptions.paths)) {
91
+ // Store paths as defined in tsconfig, they will be relative to baseUrl
92
+ paths[alias] = patterns.map(pattern =>
93
+ // Normalize slashes for consistency, keep relative
94
+ pattern.replace(/\\/g, '/'));
95
+ }
96
+ logger.debug(`Found tsconfig paths in ${tsConfigPath}: ${JSON.stringify({ baseUrl: tsConfigBaseUrl, paths }, null, 2)}`);
97
+ return { baseUrl: tsConfigBaseUrl, paths };
98
+ }
99
+ }
100
+ catch (e) {
101
+ logger.warn(`Could not read or parse tsconfig file ${tsConfigPath}: ${e.message}`);
102
+ }
103
+ }
104
+ }
105
+ catch (e) {
106
+ // If we can't read the directory, just continue to the parent
107
+ logger.warn(`Could not read directory ${currentDir}: ${e.message}`);
108
+ }
109
+ currentDir = path.dirname(currentDir);
110
+ }
111
+ logger.debug(`No tsconfig paths found traversing up from ${configDir}`);
112
+ return undefined;
113
+ }
114
+ export async function compileFile(inputRootDir, inputPath, outputDir, logger = defaultLogger, compiledFiles = new Set(), isRoot = true, pluginInfo = []) {
115
+ const absoluteInputPath = path.resolve(inputPath);
116
+ if (compiledFiles.has(absoluteInputPath)) {
117
+ return pluginInfo;
118
+ }
119
+ compiledFiles.add(absoluteInputPath);
120
+ // Ensure output directory exists
121
+ await fs.ensureDir(outputDir);
122
+ // Read the source file
123
+ const source = await fs.readFile(inputPath, 'utf-8');
124
+ // Parse the source to find relative imports
125
+ const sourceFile = ts.createSourceFile(absoluteInputPath, source, ts.ScriptTarget.Latest, true);
126
+ const importPaths = new Set();
127
+ let tsConfigInfo;
128
+ if (isRoot) {
129
+ tsConfigInfo = await findTsConfigPaths(absoluteInputPath, logger);
130
+ if (tsConfigInfo) {
131
+ logger === null || logger === void 0 ? void 0 : logger.debug(`Using TypeScript configuration: ${JSON.stringify(tsConfigInfo, null, 2)}`);
132
+ }
133
+ }
134
+ async function collectImports(node) {
135
+ if (ts.isImportDeclaration(node) && ts.isStringLiteral(node.moduleSpecifier)) {
136
+ const importPath = node.moduleSpecifier.text;
137
+ // Handle relative imports
138
+ if (importPath.startsWith('.')) {
139
+ const resolvedPath = path.resolve(path.dirname(absoluteInputPath), importPath);
140
+ let resolvedTsPath = resolvedPath + '.ts';
141
+ // Also check for .tsx if .ts doesn't exist
142
+ if (!(await fs.pathExists(resolvedTsPath))) {
143
+ const resolvedTsxPath = resolvedPath + '.tsx';
144
+ if (await fs.pathExists(resolvedTsxPath)) {
145
+ resolvedTsPath = resolvedTsxPath;
146
+ }
147
+ else {
148
+ // If neither exists, maybe it's an index file?
149
+ const resolvedIndexPath = path.join(resolvedPath, 'index.ts');
150
+ if (await fs.pathExists(resolvedIndexPath)) {
151
+ resolvedTsPath = resolvedIndexPath;
152
+ }
153
+ else {
154
+ const resolvedIndexTsxPath = path.join(resolvedPath, 'index.tsx');
155
+ if (await fs.pathExists(resolvedIndexTsxPath)) {
156
+ resolvedTsPath = resolvedIndexTsxPath;
157
+ }
158
+ else {
159
+ // If still not found, log a warning or let TS handle it later
160
+ logger === null || logger === void 0 ? void 0 : logger.warn(`Could not resolve relative import "${importPath}" from "${absoluteInputPath}" to an existing .ts/.tsx file.`);
161
+ // Do not add to importPaths if we can't verify existence
162
+ return;
163
+ }
164
+ }
165
+ }
166
+ }
167
+ importPaths.add(resolvedTsPath);
168
+ }
169
+ // Handle path aliases if tsConfigInfo exists
170
+ else if (tsConfigInfo) {
171
+ // Attempt to resolve using path aliases
172
+ let resolved = false;
173
+ for (const [alias, patterns] of Object.entries(tsConfigInfo.paths)) {
174
+ const aliasPrefix = alias.replace('*', '');
175
+ const aliasSuffix = alias.endsWith('*') ? '*' : '';
176
+ if (importPath.startsWith(aliasPrefix) &&
177
+ (aliasSuffix === '*' || importPath === aliasPrefix)) {
178
+ const remainingImportPath = importPath.slice(aliasPrefix.length);
179
+ for (const pattern of patterns) {
180
+ const patternPrefix = pattern.replace('*', '');
181
+ const patternSuffix = pattern.endsWith('*') ? '*' : '';
182
+ // Ensure suffix match consistency (* vs exact)
183
+ if (aliasSuffix !== patternSuffix)
184
+ continue;
185
+ const potentialPathBase = path.resolve(tsConfigInfo.baseUrl, patternPrefix);
186
+ const resolvedPath = path.join(potentialPathBase, remainingImportPath);
187
+ let resolvedTsPath = resolvedPath + '.ts';
188
+ // Similar existence checks as relative paths
189
+ if (!(await fs.pathExists(resolvedTsPath))) {
190
+ const resolvedTsxPath = resolvedPath + '.tsx';
191
+ if (await fs.pathExists(resolvedTsxPath)) {
192
+ resolvedTsPath = resolvedTsxPath;
193
+ }
194
+ else {
195
+ const resolvedIndexPath = path.join(resolvedPath, 'index.ts');
196
+ if (await fs.pathExists(resolvedIndexPath)) {
197
+ resolvedTsPath = resolvedIndexPath;
198
+ }
199
+ else {
200
+ const resolvedIndexTsxPath = path.join(resolvedPath, 'index.tsx');
201
+ if (await fs.pathExists(resolvedIndexTsxPath)) {
202
+ resolvedTsPath = resolvedIndexTsxPath;
203
+ }
204
+ else {
205
+ // Path doesn't resolve to a file for this pattern
206
+ continue;
207
+ }
208
+ }
209
+ }
210
+ }
211
+ // Add the first successful resolution for this alias
212
+ importPaths.add(resolvedTsPath);
213
+ resolved = true;
214
+ break; // Stop checking patterns for this alias
215
+ }
216
+ }
217
+ if (resolved)
218
+ break; // Stop checking other aliases if resolved
219
+ }
220
+ }
221
+ // For all other imports (node_modules, etc), we should still add them to be processed
222
+ // by the TypeScript compiler, even if we can't resolve them to a file
223
+ else {
224
+ // Add the import path as is - TypeScript will handle resolution
225
+ // importPaths.add(importPath);
226
+ }
227
+ }
228
+ else {
229
+ const children = node.getChildren();
230
+ for (const child of children) {
231
+ // Only process nodes that could contain import statements
232
+ if (ts.isSourceFile(child) ||
233
+ ts.isModuleBlock(child) ||
234
+ ts.isModuleDeclaration(child) ||
235
+ ts.isImportDeclaration(child) ||
236
+ child.kind === ts.SyntaxKind.SyntaxList) {
237
+ await collectImports(child);
238
+ }
239
+ }
240
+ }
241
+ }
242
+ // Start collecting imports from the source file
243
+ await collectImports(sourceFile);
244
+ const extractedPluginInfo = getPluginInfo(sourceFile);
245
+ if (extractedPluginInfo) {
246
+ pluginInfo.push(extractedPluginInfo);
247
+ }
248
+ // Store the tsConfigInfo on the first call if found
249
+ const rootTsConfigInfo = isRoot ? tsConfigInfo : undefined;
250
+ // Recursively collect all files that need to be compiled
251
+ for (const importPath of importPaths) {
252
+ // Pass rootTsConfigInfo down, but set isRoot to false
253
+ await compileFile(inputRootDir, importPath, outputDir, logger, compiledFiles, false, pluginInfo);
254
+ }
255
+ // If this is the root file (the one that started the compilation),
256
+ // use the TypeScript compiler API to compile all files together
257
+ if (isRoot) {
258
+ logger.info(`Starting compilation for ${compiledFiles.size} files...`);
259
+ const allFiles = Array.from(compiledFiles);
260
+ const compilerOptions = {
261
+ // Base options
262
+ target: ts.ScriptTarget.ES2020,
263
+ module: ts.ModuleKind.CommonJS, // Output CommonJS for Node compatibility
264
+ experimentalDecorators: true,
265
+ emitDecoratorMetadata: true,
266
+ esModuleInterop: true,
267
+ skipLibCheck: true, // Faster compilation
268
+ forceConsistentCasingInFileNames: true,
269
+ moduleResolution: ts.ModuleResolutionKind.NodeJs, // Use Node.js module resolution
270
+ incremental: false, // No need for incremental compilation
271
+ noEmitOnError: false, // Continue emitting even with errors
272
+ isolatedModules: true, // Treat files as separate modules
273
+ strict: false, // Disable strict type checking for speed
274
+ noUnusedLocals: false, // Skip unused locals check
275
+ noUnusedParameters: false, // Skip unused parameters check
276
+ // Output options
277
+ outDir: outputDir, // Output directory for all compiled files
278
+ sourceMap: false, // Generate source maps
279
+ declaration: false, // Don't generate .d.ts files
280
+ // Path resolution options - use info found from tsconfig
281
+ baseUrl: rootTsConfigInfo ? rootTsConfigInfo.baseUrl : undefined, // Let TS handle resolution if no baseUrl
282
+ paths: rootTsConfigInfo ? rootTsConfigInfo.paths : undefined,
283
+ // rootDir: inputRootDir, // Often inferred correctly, can cause issues if set explicitly sometimes
284
+ allowJs: true, // Allow JS files if needed, though we primarily collect TS
285
+ resolveJsonModule: true, // Allow importing JSON
286
+ };
287
+ logger.debug(`compilerOptions: ${JSON.stringify(compilerOptions, null, 2)}`);
288
+ // Create a Program to represent the compilation context
289
+ const program = ts.createProgram(allFiles, compilerOptions);
290
+ logger.info(`Emitting compiled files to ${outputDir}`);
291
+ const emitResult = program.emit();
292
+ const hasEmitErrors = reportDiagnostics(program, emitResult, logger);
293
+ if (hasEmitErrors) {
294
+ throw new Error('TypeScript compilation failed with errors.');
295
+ }
296
+ logger.info(`Successfully compiled ${allFiles.length} files to ${outputDir}`);
297
+ }
298
+ return pluginInfo;
299
+ }
300
+ function reportDiagnostics(program, emitResult, logger) {
301
+ const allDiagnostics = ts.getPreEmitDiagnostics(program).concat(emitResult.diagnostics);
302
+ let hasEmitErrors = emitResult.emitSkipped;
303
+ allDiagnostics.forEach(diagnostic => {
304
+ if (diagnostic.file && diagnostic.start) {
305
+ const { line, character } = ts.getLineAndCharacterOfPosition(diagnostic.file, diagnostic.start);
306
+ const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
307
+ const logFn = diagnostic.category === ts.DiagnosticCategory.Error ? logger.warn : logger.info;
308
+ // eslint-disable-next-line no-console
309
+ console.log(`TS${diagnostic.code} ${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`);
310
+ if (diagnostic.category === ts.DiagnosticCategory.Error) {
311
+ hasEmitErrors = true;
312
+ }
313
+ }
314
+ else {
315
+ const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
316
+ const logFn = diagnostic.category === ts.DiagnosticCategory.Error ? logger.warn : logger.info;
317
+ // eslint-disable-next-line no-console
318
+ console.log(`TS${diagnostic.code}: ${message}`);
319
+ if (diagnostic.category === ts.DiagnosticCategory.Error) {
320
+ hasEmitErrors = true;
321
+ }
322
+ }
323
+ });
324
+ return hasEmitErrors;
325
+ }
@@ -1,5 +1,10 @@
1
1
  import { VendureConfig } from '@vendure/core';
2
2
  import { GraphQLSchema } from 'graphql';
3
+ /**
4
+ * @description
5
+ * This function generates a GraphQL schema from the Vendure config.
6
+ * It is used to generate the schema for the dashboard.
7
+ */
3
8
  export declare function generateSchema({ vendureConfig, }: {
4
9
  vendureConfig: VendureConfig;
5
10
  }): Promise<GraphQLSchema>;
@@ -2,8 +2,14 @@ import { GraphQLTypesLoader } from '@nestjs/graphql';
2
2
  import { resetConfig, setConfig, getConfig, runPluginConfigurations, getFinalVendureSchema, VENDURE_ADMIN_API_TYPE_PATHS, } from '@vendure/core';
3
3
  import { buildSchema } from 'graphql';
4
4
  let schemaPromise;
5
+ /**
6
+ * @description
7
+ * This function generates a GraphQL schema from the Vendure config.
8
+ * It is used to generate the schema for the dashboard.
9
+ */
5
10
  export async function generateSchema({ vendureConfig, }) {
6
11
  if (!schemaPromise) {
12
+ /* eslint-disable-next-line @typescript-eslint/no-misused-promises */
7
13
  schemaPromise = new Promise(async (resolve, reject) => {
8
14
  resetConfig();
9
15
  await setConfig(vendureConfig !== null && vendureConfig !== void 0 ? vendureConfig : {});
@@ -1,6 +1,6 @@
1
1
  import { DEFAULT_AUTH_TOKEN_HEADER_KEY, DEFAULT_CHANNEL_TOKEN_KEY, ADMIN_API_PATH, } from '@vendure/common/lib/shared-constants';
2
- import { defaultAvailableLocales } from './constants.js';
3
- import { defaultLocale, defaultLanguage, defaultAvailableLanguages } from './constants.js';
2
+ import { defaultAvailableLocales } from '../constants.js';
3
+ import { defaultLocale, defaultLanguage, defaultAvailableLanguages } from '../constants.js';
4
4
  export function getAdminUiConfig(config, adminUiConfig) {
5
5
  const { authOptions, apiOptions } = config;
6
6
  const propOrDefault = (prop, defaultVal, isArray = false) => {
@@ -1,5 +1,5 @@
1
1
  import { GraphQLList, GraphQLNonNull, GraphQLObjectType, isEnumType, isInputObjectType, isObjectType, isScalarType, } from 'graphql';
2
- import { generateSchema } from './schema-generator.js';
2
+ import { generateSchema } from './utils/schema-generator.js';
3
3
  import { getConfigLoaderApi } from './vite-plugin-config-loader.js';
4
4
  const virtualModuleId = 'virtual:admin-api-schema';
5
5
  const resolvedVirtualModuleId = `\0${virtualModuleId}`;
@@ -12,7 +12,7 @@ export function adminApiSchemaPlugin() {
12
12
  configLoaderApi = getConfigLoaderApi(plugins);
13
13
  },
14
14
  async buildStart() {
15
- const vendureConfig = await configLoaderApi.getVendureConfig();
15
+ const { vendureConfig } = await configLoaderApi.getVendureConfig();
16
16
  if (!schemaInfo) {
17
17
  const safeSchema = await generateSchema({ vendureConfig });
18
18
  schemaInfo = generateSchemaInfo(safeSchema);
@@ -1,8 +1,7 @@
1
- import { VendureConfig } from '@vendure/core';
2
1
  import { Plugin } from 'vite';
3
- import { ConfigLoaderOptions } from './config-loader.js';
2
+ import { ConfigLoaderOptions, LoadVendureConfigResult } from './utils/config-loader.js';
4
3
  export interface ConfigLoaderApi {
5
- getVendureConfig(): Promise<VendureConfig>;
4
+ getVendureConfig(): Promise<LoadVendureConfigResult>;
6
5
  }
7
6
  export declare const configLoaderName = "vendure:config-loader";
8
7
  /**