@vendure/dashboard 3.3.6-master-202507110238 → 3.3.6
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.
- package/dist/plugin/types.d.ts +40 -0
- package/dist/plugin/utils/ast-utils.d.ts +0 -5
- package/dist/plugin/utils/ast-utils.js +0 -67
- package/dist/plugin/utils/ast-utils.spec.js +1 -76
- package/dist/plugin/utils/compiler.d.ts +22 -0
- package/dist/plugin/utils/compiler.js +162 -0
- package/dist/plugin/utils/config-loader.d.ts +0 -120
- package/dist/plugin/utils/config-loader.js +1 -367
- package/dist/plugin/utils/logger.d.ts +3 -0
- package/dist/plugin/utils/logger.js +39 -0
- package/dist/plugin/utils/plugin-discovery.d.ts +27 -0
- package/dist/plugin/utils/plugin-discovery.js +343 -0
- package/dist/plugin/utils/tsconfig-utils.d.ts +9 -0
- package/dist/plugin/utils/tsconfig-utils.js +50 -0
- package/dist/plugin/vite-plugin-config-loader.d.ts +3 -3
- package/dist/plugin/vite-plugin-config-loader.js +13 -13
- package/dist/plugin/vite-plugin-dashboard-metadata.js +19 -2
- package/dist/plugin/vite-plugin-vendure-dashboard.d.ts +7 -7
- package/dist/plugin/vite-plugin-vendure-dashboard.js +2 -2
- package/package.json +134 -131
- package/src/app/routes/_authenticated/_orders/components/customer-address-selector.tsx +0 -1
- package/src/app/routes/_authenticated/_orders/components/payment-details.tsx +6 -2
- package/src/app/routes/_authenticated/_orders/components/state-transition-control.tsx +2 -2
- package/src/app/routes/_authenticated/_orders/orders_.draft.$id.tsx +0 -1
- package/src/app/routes/_authenticated/_products/components/option-value-input.tsx +1 -1
- package/src/app/routes/_authenticated/_zones/components/zone-countries-table.tsx +0 -7
- package/src/lib/components/layout/content-language-selector.tsx +1 -1
- package/src/lib/components/shared/asset/asset-preview.tsx +0 -6
- package/src/lib/components/shared/option-value-input.tsx +1 -1
- package/src/lib/components/shared/product-variant-selector.tsx +1 -1
- package/src/lib/components/ui/calendar.tsx +1 -1
- package/src/lib/framework/dashboard-widget/metrics-widget/index.tsx +1 -1
- package/src/lib/framework/dashboard-widget/orders-summary/index.tsx +0 -2
- package/src/lib/hooks/use-extended-list-query.ts +32 -30
- package/vite/tests/barrel-exports.spec.ts +13 -4
- package/{dist/plugin/tests/barrel-exports/my-plugin/src/my.plugin.js → vite/tests/fixtures-npm-plugin/fake_node_modules/test-plugin/index.js} +6 -6
- package/vite/tests/fixtures-npm-plugin/fake_node_modules/test-plugin/package.json +8 -0
- package/vite/tests/fixtures-npm-plugin/package.json +6 -0
- package/{dist/plugin/tests/barrel-exports/vendure-config.js → vite/tests/fixtures-npm-plugin/vendure-config.ts} +5 -6
- package/vite/tests/fixtures-path-alias/aliased-plugin/index.ts +1 -0
- package/vite/tests/fixtures-path-alias/aliased-plugin/src/aliased.plugin.ts +8 -0
- package/vite/tests/fixtures-path-alias/package.json +6 -0
- package/vite/tests/fixtures-path-alias/vendure-config.ts +19 -0
- package/vite/tests/npm-plugin.spec.ts +46 -0
- package/vite/tests/path-alias.spec.ts +33 -0
- package/vite/tests/tsconfig.json +11 -0
- package/vite/types.ts +44 -0
- package/vite/utils/ast-utils.spec.ts +1 -80
- package/vite/utils/ast-utils.ts +0 -86
- package/vite/utils/compiler.ts +244 -0
- package/vite/utils/config-loader.ts +0 -555
- package/vite/utils/logger.ts +43 -0
- package/vite/utils/plugin-discovery.ts +442 -0
- package/vite/utils/tsconfig-utils.ts +79 -0
- package/vite/vite-plugin-config-loader.ts +20 -17
- package/vite/vite-plugin-dashboard-metadata.ts +26 -7
- package/vite/vite-plugin-tailwind-source.ts +2 -2
- package/vite/vite-plugin-vendure-dashboard.ts +9 -9
- package/dist/plugin/tests/barrel-exports/my-plugin/index.d.ts +0 -1
- package/dist/plugin/tests/barrel-exports/my-plugin/index.js +0 -17
- package/dist/plugin/tests/barrel-exports/my-plugin/src/my.plugin.d.ts +0 -2
- package/dist/plugin/tests/barrel-exports/vendure-config.d.ts +0 -2
- package/dist/plugin/tests/barrel-exports.spec.js +0 -14
- /package/dist/plugin/{tests/barrel-exports.spec.d.ts → types.js} +0 -0
- /package/vite/tests/{barrel-exports → fixtures-barrel-exports}/my-plugin/index.ts +0 -0
- /package/vite/tests/{barrel-exports → fixtures-barrel-exports}/my-plugin/src/my.plugin.ts +0 -0
- /package/vite/tests/{barrel-exports → fixtures-barrel-exports}/package.json +0 -0
- /package/vite/tests/{barrel-exports → fixtures-barrel-exports}/vendure-config.ts +0 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { VendureConfig } from '@vendure/core';
|
|
2
|
+
|
|
3
|
+
import { AliasedPlugin } from './aliased-plugin';
|
|
4
|
+
|
|
5
|
+
export const config: VendureConfig = {
|
|
6
|
+
apiOptions: {
|
|
7
|
+
port: 3000,
|
|
8
|
+
},
|
|
9
|
+
authOptions: {
|
|
10
|
+
tokenMethod: 'bearer',
|
|
11
|
+
},
|
|
12
|
+
dbConnectionOptions: {
|
|
13
|
+
type: 'postgres',
|
|
14
|
+
},
|
|
15
|
+
paymentOptions: {
|
|
16
|
+
paymentMethodHandlers: [],
|
|
17
|
+
},
|
|
18
|
+
plugins: [AliasedPlugin],
|
|
19
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { rm } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import tsconfigPaths from 'tsconfig-paths';
|
|
4
|
+
import { describe, expect, it } from 'vitest';
|
|
5
|
+
|
|
6
|
+
import { compile } from '../utils/compiler.js';
|
|
7
|
+
import { debugLogger, noopLogger } from '../utils/logger.js';
|
|
8
|
+
|
|
9
|
+
describe('detecting plugins in npm packages', () => {
|
|
10
|
+
it(
|
|
11
|
+
'should detect plugins in npm packages',
|
|
12
|
+
async () => {
|
|
13
|
+
const tempDir = join(__dirname, './__temp/npm-plugin');
|
|
14
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
15
|
+
const fakeNodeModules = join(__dirname, 'fixtures-npm-plugin', 'fake_node_modules');
|
|
16
|
+
|
|
17
|
+
// For this test to work, we need to use tsconfigPaths to point
|
|
18
|
+
// the `test-plugin` package to the `fake_node_modules` directory.
|
|
19
|
+
// This is because the `test-plugin` package is not installed in
|
|
20
|
+
// the `node_modules` directory, but in the `fake_node_modules`
|
|
21
|
+
// directory.
|
|
22
|
+
tsconfigPaths.register({
|
|
23
|
+
baseUrl: fakeNodeModules,
|
|
24
|
+
paths: {
|
|
25
|
+
'test-plugin': [join(fakeNodeModules, 'test-plugin')],
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const result = await compile({
|
|
30
|
+
outputPath: tempDir,
|
|
31
|
+
vendureConfigPath: join(__dirname, 'fixtures-npm-plugin', 'vendure-config.ts'),
|
|
32
|
+
logger: process.env.LOG ? debugLogger : noopLogger,
|
|
33
|
+
pluginPackageScanner: {
|
|
34
|
+
nodeModulesRoot: fakeNodeModules,
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
expect(result.pluginInfo).toHaveLength(1);
|
|
39
|
+
expect(result.pluginInfo[0].name).toBe('TestPlugin');
|
|
40
|
+
expect(result.pluginInfo[0].dashboardEntryPath).toBe('./dashboard/index.tsx');
|
|
41
|
+
expect(result.pluginInfo[0].sourcePluginPath).toBeUndefined();
|
|
42
|
+
expect(result.pluginInfo[0].pluginPath).toBe(join(fakeNodeModules, 'test-plugin', 'index.js'));
|
|
43
|
+
},
|
|
44
|
+
{ timeout: 10_000 },
|
|
45
|
+
);
|
|
46
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { rm } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { describe, expect, it } from 'vitest';
|
|
4
|
+
|
|
5
|
+
import { compile } from '../utils/compiler.js';
|
|
6
|
+
import { debugLogger, noopLogger } from '../utils/logger.js';
|
|
7
|
+
|
|
8
|
+
describe('detecting plugins using tsconfig path aliases', () => {
|
|
9
|
+
it(
|
|
10
|
+
'should detect plugins using tsconfig path aliases',
|
|
11
|
+
async () => {
|
|
12
|
+
const tempDir = join(__dirname, './__temp/path-alias');
|
|
13
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
14
|
+
|
|
15
|
+
const result = await compile({
|
|
16
|
+
outputPath: tempDir,
|
|
17
|
+
vendureConfigPath: join(__dirname, 'fixtures-path-alias', 'vendure-config.ts'),
|
|
18
|
+
logger: process.env.LOG ? debugLogger : noopLogger,
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
expect(result.pluginInfo).toHaveLength(1);
|
|
22
|
+
expect(result.pluginInfo[0].name).toBe('AliasedPlugin');
|
|
23
|
+
expect(result.pluginInfo[0].dashboardEntryPath).toBe('./dashboard/index.tsx');
|
|
24
|
+
expect(result.pluginInfo[0].sourcePluginPath).toBe(
|
|
25
|
+
join(__dirname, 'fixtures-path-alias', 'aliased-plugin', 'src', 'aliased.plugin.ts'),
|
|
26
|
+
);
|
|
27
|
+
expect(result.pluginInfo[0].pluginPath).toBe(
|
|
28
|
+
join(tempDir, 'aliased-plugin', 'src', 'aliased.plugin.js'),
|
|
29
|
+
);
|
|
30
|
+
},
|
|
31
|
+
{ timeout: 10_000 },
|
|
32
|
+
);
|
|
33
|
+
});
|
package/vite/types.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export type Logger = {
|
|
2
|
+
info: (message: string) => void;
|
|
3
|
+
warn: (message: string) => void;
|
|
4
|
+
debug: (message: string) => void;
|
|
5
|
+
error: (message: string) => void;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export type PluginInfo = {
|
|
9
|
+
name: string;
|
|
10
|
+
pluginPath: string;
|
|
11
|
+
dashboardEntryPath: string | undefined;
|
|
12
|
+
/** The original source path of the plugin, only set for local plugins that are compiled */
|
|
13
|
+
sourcePluginPath?: string;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export type GetCompiledConfigPathFn = (params: {
|
|
17
|
+
inputRootDir: string;
|
|
18
|
+
outputPath: string;
|
|
19
|
+
configFileName: string;
|
|
20
|
+
}) => string;
|
|
21
|
+
|
|
22
|
+
export type TransformTsConfigPathMappingsFn = (params: {
|
|
23
|
+
phase: 'compiling' | 'loading';
|
|
24
|
+
alias: string;
|
|
25
|
+
patterns: string[];
|
|
26
|
+
}) => string[];
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @description
|
|
30
|
+
* The PathAdapter interface allows customization of how paths are handled
|
|
31
|
+
* when compiling the Vendure config and its imports.
|
|
32
|
+
*/
|
|
33
|
+
export interface PathAdapter {
|
|
34
|
+
/**
|
|
35
|
+
* @description
|
|
36
|
+
* A function to determine the path to the compiled Vendure config file.
|
|
37
|
+
*/
|
|
38
|
+
getCompiledConfigPath?: GetCompiledConfigPathFn;
|
|
39
|
+
/**
|
|
40
|
+
* If your project makes use of the TypeScript `paths` configuration, the compiler will
|
|
41
|
+
* attempt to use these paths when compiling the Vendure config and its imports.
|
|
42
|
+
*/
|
|
43
|
+
transformTsConfigPathMappings?: TransformTsConfigPathMappingsFn;
|
|
44
|
+
}
|
|
@@ -1,86 +1,7 @@
|
|
|
1
1
|
import ts from 'typescript';
|
|
2
2
|
import { describe, expect, it } from 'vitest';
|
|
3
3
|
|
|
4
|
-
import { findConfigExport
|
|
5
|
-
|
|
6
|
-
describe('getPluginInfo', () => {
|
|
7
|
-
it('should return undefined when no plugin class is found', () => {
|
|
8
|
-
const sourceText = `
|
|
9
|
-
class NotAPlugin {
|
|
10
|
-
constructor() {}
|
|
11
|
-
}
|
|
12
|
-
`;
|
|
13
|
-
const sourceFile = ts.createSourceFile('path/to/test.ts', sourceText, ts.ScriptTarget.Latest, true);
|
|
14
|
-
const result = getPluginInfo(sourceFile);
|
|
15
|
-
expect(result).toBeUndefined();
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
it('should return plugin info when a valid plugin class is found', () => {
|
|
19
|
-
const sourceText = `
|
|
20
|
-
@VendurePlugin({
|
|
21
|
-
imports: [],
|
|
22
|
-
providers: []
|
|
23
|
-
})
|
|
24
|
-
class TestPlugin {
|
|
25
|
-
constructor() {}
|
|
26
|
-
}
|
|
27
|
-
`;
|
|
28
|
-
const sourceFile = ts.createSourceFile('path/to/test.ts', sourceText, ts.ScriptTarget.Latest, true);
|
|
29
|
-
const result = getPluginInfo(sourceFile);
|
|
30
|
-
expect(result).toEqual({
|
|
31
|
-
name: 'TestPlugin',
|
|
32
|
-
pluginPath: 'path/to',
|
|
33
|
-
dashboardEntryPath: undefined,
|
|
34
|
-
});
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it('should handle multiple classes but only return the plugin one', () => {
|
|
38
|
-
const sourceText = `
|
|
39
|
-
class NotAPlugin {
|
|
40
|
-
constructor() {}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
@VendurePlugin({
|
|
44
|
-
imports: [],
|
|
45
|
-
providers: []
|
|
46
|
-
})
|
|
47
|
-
class TestPlugin {
|
|
48
|
-
constructor() {}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
class AnotherClass {
|
|
52
|
-
constructor() {}
|
|
53
|
-
}
|
|
54
|
-
`;
|
|
55
|
-
const sourceFile = ts.createSourceFile('path/to/test.ts', sourceText, ts.ScriptTarget.Latest, true);
|
|
56
|
-
const result = getPluginInfo(sourceFile);
|
|
57
|
-
expect(result).toEqual({
|
|
58
|
-
name: 'TestPlugin',
|
|
59
|
-
pluginPath: 'path/to',
|
|
60
|
-
dashboardEntryPath: undefined,
|
|
61
|
-
});
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
it('should determine the dashboard entry path when it is provided', () => {
|
|
65
|
-
const sourceText = `
|
|
66
|
-
@VendurePlugin({
|
|
67
|
-
imports: [],
|
|
68
|
-
providers: [],
|
|
69
|
-
dashboard: './dashboard/index.tsx',
|
|
70
|
-
})
|
|
71
|
-
class TestPlugin {
|
|
72
|
-
constructor() {}
|
|
73
|
-
}
|
|
74
|
-
`;
|
|
75
|
-
const sourceFile = ts.createSourceFile('path/to/test.ts', sourceText, ts.ScriptTarget.Latest, true);
|
|
76
|
-
const result = getPluginInfo(sourceFile);
|
|
77
|
-
expect(result).toEqual({
|
|
78
|
-
name: 'TestPlugin',
|
|
79
|
-
pluginPath: 'path/to',
|
|
80
|
-
dashboardEntryPath: './dashboard/index.tsx',
|
|
81
|
-
});
|
|
82
|
-
});
|
|
83
|
-
});
|
|
4
|
+
import { findConfigExport } from './ast-utils.js';
|
|
84
5
|
|
|
85
6
|
describe('findConfigExport', () => {
|
|
86
7
|
it('should return undefined when no VendureConfig export is found', () => {
|
package/vite/utils/ast-utils.ts
CHANGED
|
@@ -1,70 +1,5 @@
|
|
|
1
|
-
import path from 'path';
|
|
2
1
|
import ts from 'typescript';
|
|
3
2
|
|
|
4
|
-
import { PluginInfo } from './config-loader.js';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Get the plugin info from the source file.
|
|
8
|
-
*/
|
|
9
|
-
export function getPluginInfo(sourceFile: ts.SourceFile): PluginInfo | undefined {
|
|
10
|
-
const classDeclaration = sourceFile.statements.find(statement => {
|
|
11
|
-
return (
|
|
12
|
-
statement.kind === ts.SyntaxKind.ClassDeclaration &&
|
|
13
|
-
statement.getText().includes('@VendurePlugin(')
|
|
14
|
-
);
|
|
15
|
-
});
|
|
16
|
-
if (classDeclaration) {
|
|
17
|
-
const identifier = classDeclaration.getChildren().find(child => {
|
|
18
|
-
return child.kind === ts.SyntaxKind.Identifier;
|
|
19
|
-
});
|
|
20
|
-
const dashboardEntryPath = classDeclaration
|
|
21
|
-
.getChildren()
|
|
22
|
-
.map(child => {
|
|
23
|
-
if (child.kind === ts.SyntaxKind.SyntaxList) {
|
|
24
|
-
const pluginDecorator = child.getChildren().find(_child => {
|
|
25
|
-
return _child.kind === ts.SyntaxKind.Decorator;
|
|
26
|
-
});
|
|
27
|
-
if (pluginDecorator) {
|
|
28
|
-
const callExpression = findFirstDescendantOfKind(
|
|
29
|
-
pluginDecorator,
|
|
30
|
-
ts.SyntaxKind.CallExpression,
|
|
31
|
-
);
|
|
32
|
-
if (callExpression) {
|
|
33
|
-
const objectLiteral = findFirstDescendantOfKind(
|
|
34
|
-
callExpression,
|
|
35
|
-
ts.SyntaxKind.ObjectLiteralExpression,
|
|
36
|
-
);
|
|
37
|
-
if (objectLiteral && ts.isObjectLiteralExpression(objectLiteral)) {
|
|
38
|
-
// Now find the specific 'dashboard' property
|
|
39
|
-
const dashboardProperty = objectLiteral.properties.find(
|
|
40
|
-
prop =>
|
|
41
|
-
ts.isPropertyAssignment(prop) && prop.name?.getText() === 'dashboard',
|
|
42
|
-
);
|
|
43
|
-
|
|
44
|
-
if (
|
|
45
|
-
dashboardProperty &&
|
|
46
|
-
ts.isPropertyAssignment(dashboardProperty) &&
|
|
47
|
-
ts.isStringLiteral(dashboardProperty.initializer)
|
|
48
|
-
) {
|
|
49
|
-
const dashboardPath = dashboardProperty.initializer.text;
|
|
50
|
-
return dashboardPath;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
})
|
|
57
|
-
.filter(Boolean)?.[0];
|
|
58
|
-
if (identifier) {
|
|
59
|
-
return {
|
|
60
|
-
name: identifier.getText(),
|
|
61
|
-
pluginPath: path.dirname(sourceFile.fileName),
|
|
62
|
-
dashboardEntryPath,
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
3
|
/**
|
|
69
4
|
* Given the AST of a TypeScript file, finds the name of the variable exported as VendureConfig.
|
|
70
5
|
*/
|
|
@@ -96,24 +31,3 @@ export function findConfigExport(sourceFile: ts.SourceFile): string | undefined
|
|
|
96
31
|
visit(sourceFile);
|
|
97
32
|
return exportedSymbolName;
|
|
98
33
|
}
|
|
99
|
-
|
|
100
|
-
function findFirstDescendantOfKind(node: ts.Node, kind: ts.SyntaxKind): ts.Node | undefined {
|
|
101
|
-
let foundNode: ts.Node | undefined;
|
|
102
|
-
|
|
103
|
-
function visit(_node: ts.Node) {
|
|
104
|
-
if (foundNode) {
|
|
105
|
-
// Stop searching if we already found it
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
|
-
if (_node.kind === kind) {
|
|
109
|
-
foundNode = _node;
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
// Recursively visit children
|
|
113
|
-
ts.forEachChild(_node, visit);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// Start the traversal from the initial node's children
|
|
117
|
-
ts.forEachChild(node, visit);
|
|
118
|
-
return foundNode;
|
|
119
|
-
}
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import { VendureConfig } from '@vendure/core';
|
|
2
|
+
import fs from 'fs-extra';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import tsConfigPaths from 'tsconfig-paths';
|
|
5
|
+
import { RegisterParams } from 'tsconfig-paths/lib/register.js';
|
|
6
|
+
import * as ts from 'typescript';
|
|
7
|
+
import { pathToFileURL } from 'url';
|
|
8
|
+
|
|
9
|
+
import { Logger, PathAdapter, PluginInfo } from '../types.js';
|
|
10
|
+
|
|
11
|
+
import { findConfigExport } from './ast-utils.js';
|
|
12
|
+
import { noopLogger } from './logger.js';
|
|
13
|
+
import { discoverPlugins } from './plugin-discovery.js';
|
|
14
|
+
import { findTsConfigPaths } from './tsconfig-utils.js';
|
|
15
|
+
|
|
16
|
+
const defaultPathAdapter: Required<PathAdapter> = {
|
|
17
|
+
getCompiledConfigPath: ({ outputPath, configFileName }) => path.join(outputPath, configFileName),
|
|
18
|
+
transformTsConfigPathMappings: ({ patterns }) => patterns,
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export interface PackageScannerConfig {
|
|
22
|
+
nodeModulesRoot?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface CompilerOptions {
|
|
26
|
+
vendureConfigPath: string;
|
|
27
|
+
outputPath: string;
|
|
28
|
+
pathAdapter?: PathAdapter;
|
|
29
|
+
logger?: Logger;
|
|
30
|
+
pluginPackageScanner?: PackageScannerConfig;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface CompileResult {
|
|
34
|
+
vendureConfig: VendureConfig;
|
|
35
|
+
exportedSymbolName: string;
|
|
36
|
+
pluginInfo: PluginInfo[];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Compiles TypeScript files and discovers Vendure plugins in both the compiled output
|
|
41
|
+
* and in node_modules.
|
|
42
|
+
*/
|
|
43
|
+
export async function compile(options: CompilerOptions): Promise<CompileResult> {
|
|
44
|
+
const { vendureConfigPath, outputPath, pathAdapter, logger = noopLogger, pluginPackageScanner } = options;
|
|
45
|
+
const getCompiledConfigPath =
|
|
46
|
+
pathAdapter?.getCompiledConfigPath ?? defaultPathAdapter.getCompiledConfigPath;
|
|
47
|
+
const transformTsConfigPathMappings =
|
|
48
|
+
pathAdapter?.transformTsConfigPathMappings ?? defaultPathAdapter.transformTsConfigPathMappings;
|
|
49
|
+
|
|
50
|
+
// 1. Compile TypeScript files
|
|
51
|
+
const compileStart = Date.now();
|
|
52
|
+
await compileTypeScript({
|
|
53
|
+
inputPath: vendureConfigPath,
|
|
54
|
+
outputPath,
|
|
55
|
+
logger,
|
|
56
|
+
transformTsConfigPathMappings,
|
|
57
|
+
});
|
|
58
|
+
logger.info(`TypeScript compilation completed in ${Date.now() - compileStart}ms`);
|
|
59
|
+
|
|
60
|
+
// 2. Discover plugins
|
|
61
|
+
const analyzePluginsStart = Date.now();
|
|
62
|
+
const plugins = await discoverPlugins({
|
|
63
|
+
vendureConfigPath,
|
|
64
|
+
transformTsConfigPathMappings,
|
|
65
|
+
logger,
|
|
66
|
+
outputPath,
|
|
67
|
+
pluginPackageScanner,
|
|
68
|
+
});
|
|
69
|
+
logger.info(
|
|
70
|
+
`Analyzed plugins and found ${plugins.length} dashboard extensions in ${Date.now() - analyzePluginsStart}ms`,
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
// 3. Load the compiled config
|
|
74
|
+
const configFileName = path.basename(vendureConfigPath);
|
|
75
|
+
const compiledConfigFilePath = pathToFileURL(
|
|
76
|
+
getCompiledConfigPath({
|
|
77
|
+
inputRootDir: path.dirname(vendureConfigPath),
|
|
78
|
+
outputPath,
|
|
79
|
+
configFileName,
|
|
80
|
+
}),
|
|
81
|
+
).href.replace(/.ts$/, '.js');
|
|
82
|
+
|
|
83
|
+
// Create package.json with type commonjs
|
|
84
|
+
await fs.writeFile(
|
|
85
|
+
path.join(outputPath, 'package.json'),
|
|
86
|
+
JSON.stringify({ type: 'commonjs', private: true }, null, 2),
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
// Find the exported config symbol
|
|
90
|
+
const sourceFile = ts.createSourceFile(
|
|
91
|
+
vendureConfigPath,
|
|
92
|
+
await fs.readFile(vendureConfigPath, 'utf-8'),
|
|
93
|
+
ts.ScriptTarget.Latest,
|
|
94
|
+
true,
|
|
95
|
+
);
|
|
96
|
+
const exportedSymbolName = findConfigExport(sourceFile);
|
|
97
|
+
if (!exportedSymbolName) {
|
|
98
|
+
throw new Error(
|
|
99
|
+
`Could not find a variable exported as VendureConfig. Please specify the name of the exported variable.`,
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const loadConfigStart = Date.now();
|
|
104
|
+
|
|
105
|
+
await registerTsConfigPaths({
|
|
106
|
+
outputPath,
|
|
107
|
+
configPath: vendureConfigPath,
|
|
108
|
+
logger,
|
|
109
|
+
phase: 'loading',
|
|
110
|
+
transformTsConfigPathMappings,
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
let config: any;
|
|
114
|
+
try {
|
|
115
|
+
config = await import(compiledConfigFilePath).then(m => m[exportedSymbolName]);
|
|
116
|
+
} catch (e) {
|
|
117
|
+
logger.error(`Error loading config: ${e instanceof Error ? e.message : String(e)}`);
|
|
118
|
+
}
|
|
119
|
+
if (!config) {
|
|
120
|
+
throw new Error(
|
|
121
|
+
`Could not find a variable exported as VendureConfig with the name "${exportedSymbolName}".`,
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
logger.debug(`Loaded config in ${Date.now() - loadConfigStart}ms`);
|
|
125
|
+
|
|
126
|
+
return { vendureConfig: config, exportedSymbolName, pluginInfo: plugins };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Compiles TypeScript files to JavaScript
|
|
131
|
+
*/
|
|
132
|
+
async function compileTypeScript({
|
|
133
|
+
inputPath,
|
|
134
|
+
outputPath,
|
|
135
|
+
logger,
|
|
136
|
+
transformTsConfigPathMappings,
|
|
137
|
+
}: {
|
|
138
|
+
inputPath: string;
|
|
139
|
+
outputPath: string;
|
|
140
|
+
logger: Logger;
|
|
141
|
+
transformTsConfigPathMappings: Required<PathAdapter>['transformTsConfigPathMappings'];
|
|
142
|
+
}): Promise<void> {
|
|
143
|
+
await fs.ensureDir(outputPath);
|
|
144
|
+
|
|
145
|
+
// Find tsconfig paths first
|
|
146
|
+
const tsConfigInfo = await findTsConfigPaths(
|
|
147
|
+
inputPath,
|
|
148
|
+
logger,
|
|
149
|
+
'compiling',
|
|
150
|
+
transformTsConfigPathMappings,
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
const compilerOptions: ts.CompilerOptions = {
|
|
154
|
+
target: ts.ScriptTarget.ES2020,
|
|
155
|
+
module: ts.ModuleKind.CommonJS,
|
|
156
|
+
moduleResolution: ts.ModuleResolutionKind.Node10, // More explicit CJS resolution
|
|
157
|
+
experimentalDecorators: true,
|
|
158
|
+
emitDecoratorMetadata: true,
|
|
159
|
+
esModuleInterop: true,
|
|
160
|
+
skipLibCheck: true,
|
|
161
|
+
noEmit: false,
|
|
162
|
+
// Speed optimizations
|
|
163
|
+
noEmitOnError: false, // Emit output even if there are errors
|
|
164
|
+
noImplicitAny: false, // Don't require implicit any
|
|
165
|
+
noUnusedLocals: false, // Don't check for unused locals
|
|
166
|
+
noUnusedParameters: false, // Don't check for unused parameters
|
|
167
|
+
allowJs: true,
|
|
168
|
+
checkJs: false, // Don't type check JS files
|
|
169
|
+
skipDefaultLibCheck: true, // Skip checking .d.ts files
|
|
170
|
+
isolatedModules: false, // Need to check cross-file references to compile dependencies
|
|
171
|
+
incremental: false, // Don't use incremental compilation (faster for one-off builds)
|
|
172
|
+
resolveJsonModule: true,
|
|
173
|
+
preserveSymlinks: false,
|
|
174
|
+
outDir: outputPath,
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
logger.debug(`Compiling ${inputPath} to ${outputPath} using TypeScript...`);
|
|
178
|
+
|
|
179
|
+
// Add path mappings if found
|
|
180
|
+
if (tsConfigInfo) {
|
|
181
|
+
// We need to set baseUrl and paths for TypeScript to resolve the imports
|
|
182
|
+
compilerOptions.baseUrl = tsConfigInfo.baseUrl;
|
|
183
|
+
compilerOptions.paths = tsConfigInfo.paths;
|
|
184
|
+
// This is critical - it tells TypeScript to preserve the paths in the output
|
|
185
|
+
// compilerOptions.rootDir = tsConfigInfo.baseUrl;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
logger.debug(`tsConfig paths: ${JSON.stringify(tsConfigInfo?.paths, null, 2)}`);
|
|
189
|
+
logger.debug(`tsConfig baseUrl: ${tsConfigInfo?.baseUrl ?? 'UNKNOWN'}`);
|
|
190
|
+
|
|
191
|
+
// Create a custom transformer to rewrite the output paths
|
|
192
|
+
const customTransformers: ts.CustomTransformers = {
|
|
193
|
+
after: [
|
|
194
|
+
context => {
|
|
195
|
+
return sourceFile => {
|
|
196
|
+
// Only transform files that are not the entry point
|
|
197
|
+
if (sourceFile.fileName === inputPath) {
|
|
198
|
+
return sourceFile;
|
|
199
|
+
}
|
|
200
|
+
sourceFile.fileName = path.join(outputPath, path.basename(sourceFile.fileName));
|
|
201
|
+
return sourceFile;
|
|
202
|
+
};
|
|
203
|
+
},
|
|
204
|
+
],
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
const program = ts.createProgram([inputPath], compilerOptions);
|
|
208
|
+
const emitResult = program.emit(undefined, undefined, undefined, undefined, customTransformers);
|
|
209
|
+
|
|
210
|
+
// Only log actual emit errors, not type errors
|
|
211
|
+
if (emitResult.emitSkipped) {
|
|
212
|
+
for (const diagnostic of emitResult.diagnostics) {
|
|
213
|
+
if (diagnostic.file && diagnostic.start !== undefined) {
|
|
214
|
+
const { line, character } = ts.getLineAndCharacterOfPosition(
|
|
215
|
+
diagnostic.file,
|
|
216
|
+
diagnostic.start,
|
|
217
|
+
);
|
|
218
|
+
const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
|
|
219
|
+
logger.warn(`${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`);
|
|
220
|
+
} else {
|
|
221
|
+
logger.warn(ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n'));
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
async function registerTsConfigPaths(options: {
|
|
228
|
+
outputPath: string;
|
|
229
|
+
configPath: string;
|
|
230
|
+
logger: Logger;
|
|
231
|
+
phase: 'compiling' | 'loading';
|
|
232
|
+
transformTsConfigPathMappings: Required<PathAdapter>['transformTsConfigPathMappings'];
|
|
233
|
+
}) {
|
|
234
|
+
const { outputPath, configPath, logger, phase, transformTsConfigPathMappings } = options;
|
|
235
|
+
const tsConfigInfo = await findTsConfigPaths(configPath, logger, phase, transformTsConfigPathMappings);
|
|
236
|
+
if (tsConfigInfo) {
|
|
237
|
+
const params: RegisterParams = {
|
|
238
|
+
baseUrl: outputPath,
|
|
239
|
+
paths: tsConfigInfo.paths,
|
|
240
|
+
};
|
|
241
|
+
logger.debug(`Registering tsconfig paths: ${JSON.stringify(params, null, 2)}`);
|
|
242
|
+
tsConfigPaths.register(params);
|
|
243
|
+
}
|
|
244
|
+
}
|