@motiadev/workbench 0.8.2-beta.140-628177 → 0.8.2-beta.140-111855
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/components.json +1 -1
- package/dist/index.d.ts +7 -7
- package/dist/index.js +5 -5
- package/dist/middleware.d.ts +1 -1
- package/dist/middleware.js +3 -3
- package/dist/motia-plugin/__tests__/generator.test.js +97 -0
- package/dist/motia-plugin/__tests__/resolver.test.d.ts +1 -0
- package/dist/motia-plugin/__tests__/resolver.test.js +64 -0
- package/dist/motia-plugin/__tests__/validator.test.d.ts +1 -0
- package/dist/motia-plugin/__tests__/validator.test.js +59 -0
- package/dist/motia-plugin/generator.d.ts +78 -0
- package/dist/motia-plugin/generator.js +128 -0
- package/dist/motia-plugin/hmr.d.ts +19 -0
- package/dist/motia-plugin/hmr.js +66 -0
- package/dist/motia-plugin/index.d.ts +27 -0
- package/dist/motia-plugin/index.js +118 -0
- package/dist/motia-plugin/resolver.d.ts +63 -0
- package/dist/motia-plugin/resolver.js +92 -0
- package/dist/motia-plugin/types.d.ts +169 -0
- package/dist/motia-plugin/types.js +36 -0
- package/dist/motia-plugin/utils.d.ts +57 -0
- package/dist/motia-plugin/utils.js +75 -0
- package/dist/motia-plugin/validator.d.ts +19 -0
- package/dist/motia-plugin/validator.js +163 -0
- package/dist/postcss.config.mjs +1 -1
- package/dist/src/App.d.ts +1 -1
- package/dist/src/App.js +1 -18
- package/dist/src/components/flow/base-edge.d.ts +2 -2
- package/dist/src/components/flow/base-edge.js +1 -1
- package/dist/src/components/flow/flow-page.js +2 -2
- package/dist/src/components/flow/flow-tab-menu-item.js +2 -2
- package/dist/src/components/flow/flow-view.d.ts +3 -3
- package/dist/src/components/flow/hooks/use-get-flow-state.d.ts +2 -2
- package/dist/src/components/flow/hooks/use-get-flow-state.js +0 -4
- package/dist/src/components/flow/hooks/use-save-workflow-config.d.ts +1 -1
- package/dist/src/components/flow/node-organizer.d.ts +3 -3
- package/dist/src/components/flow/nodes/api-flow-node.d.ts +1 -1
- package/dist/src/components/flow/nodes/cron-flow-node.d.ts +1 -1
- package/dist/src/components/flow/nodes/event-flow-node.d.ts +1 -1
- package/dist/src/components/flow/nodes/noop-flow-node.d.ts +1 -1
- package/dist/src/components/header/deploy-button.js +2 -2
- package/dist/src/components/header/header.d.ts +1 -1
- package/dist/src/components/header/header.js +2 -2
- package/dist/src/components/root-motia.d.ts +2 -1
- package/dist/src/components/tutorial/engine/tutorial-engine.d.ts +1 -1
- package/dist/src/components/tutorial/hooks/use-tutorial-engine.d.ts +1 -1
- package/dist/src/components/tutorial/hooks/use-tutorial.d.ts +1 -1
- package/dist/src/components/tutorial/tutorial-button.d.ts +1 -1
- package/dist/src/components/tutorial/tutorial-button.js +1 -1
- package/dist/src/components/tutorial/tutorial-step.d.ts +2 -2
- package/dist/src/components/tutorial/tutorial-step.js +1 -1
- package/dist/src/components/tutorial/tutorial.css +8 -8
- package/dist/src/components/ui/json-editor.d.ts +1 -1
- package/dist/src/components/ui/json-editor.js +1 -1
- package/dist/src/components/ui/table.js +1 -1
- package/dist/src/components/ui/theme-toggle.d.ts +1 -1
- package/dist/src/components/ui/tooltip.d.ts +1 -1
- package/dist/src/hooks/use-fetch-flows.js +1 -1
- package/dist/src/hooks/use-update-handle-positions.d.ts +1 -1
- package/dist/src/index.css +5 -5
- package/dist/src/lib/plugins.js +3 -3
- package/dist/src/main.js +2 -3
- package/dist/src/project-view-mode.js +1 -1
- package/dist/src/publicComponents/api-node.d.ts +2 -2
- package/dist/src/publicComponents/base-node/base-handle.d.ts +3 -2
- package/dist/src/publicComponents/base-node/base-node.d.ts +3 -2
- package/dist/src/publicComponents/base-node/base-node.js +1 -1
- package/dist/src/publicComponents/base-node/code-display.d.ts +2 -2
- package/dist/src/publicComponents/base-node/code-display.js +1 -1
- package/dist/src/publicComponents/base-node/emits.d.ts +2 -2
- package/dist/src/publicComponents/base-node/feature-card.d.ts +2 -2
- package/dist/src/publicComponents/base-node/language-indicator.d.ts +2 -2
- package/dist/src/publicComponents/base-node/node-header.d.ts +3 -2
- package/dist/src/publicComponents/base-node/node-sidebar.d.ts +2 -2
- package/dist/src/publicComponents/base-node/subscribe.d.ts +1 -1
- package/dist/src/publicComponents/cron-node.d.ts +3 -2
- package/dist/src/publicComponents/event-node.d.ts +3 -2
- package/dist/src/publicComponents/node-props.d.ts +1 -1
- package/dist/src/publicComponents/noop-node.d.ts +3 -2
- package/dist/src/stores/use-global-store.d.ts +0 -6
- package/dist/src/stores/use-global-store.js +0 -6
- package/dist/src/types/flow.d.ts +1 -1
- package/dist/tsconfig.app.tsbuildinfo +1 -1
- package/dist/tsconfig.node.tsbuildinfo +1 -1
- package/motia-plugin/__tests__/generator.test.ts +129 -0
- package/motia-plugin/__tests__/resolver.test.ts +82 -0
- package/motia-plugin/__tests__/validator.test.ts +71 -0
- package/motia-plugin/generator.ts +130 -0
- package/motia-plugin/hmr.ts +78 -0
- package/motia-plugin/index.ts +143 -0
- package/motia-plugin/resolver.ts +96 -0
- package/motia-plugin/types.ts +198 -0
- package/motia-plugin/utils.ts +70 -0
- package/motia-plugin/validator.ts +197 -0
- package/package.json +9 -9
- package/postcss.config.mjs +1 -1
- package/dist/src/components/observability/events/code/function-call.d.ts +0 -13
- package/dist/src/components/observability/events/code/function-call.js +0 -16
- package/dist/src/components/observability/events/event-icon.d.ts +0 -7
- package/dist/src/components/observability/events/event-icon.js +0 -16
- package/dist/src/components/observability/events/trace-emit-event.d.ts +0 -5
- package/dist/src/components/observability/events/trace-emit-event.js +0 -5
- package/dist/src/components/observability/events/trace-event.d.ts +0 -5
- package/dist/src/components/observability/events/trace-event.js +0 -20
- package/dist/src/components/observability/events/trace-log-event.d.ts +0 -5
- package/dist/src/components/observability/events/trace-log-event.js +0 -5
- package/dist/src/components/observability/events/trace-state-event.d.ts +0 -5
- package/dist/src/components/observability/events/trace-state-event.js +0 -5
- package/dist/src/components/observability/events/trace-stream-event.d.ts +0 -5
- package/dist/src/components/observability/events/trace-stream-event.js +0 -5
- package/dist/src/components/observability/hooks/use-get-endtime.d.ts +0 -2
- package/dist/src/components/observability/hooks/use-get-endtime.js +0 -15
- package/dist/src/components/observability/trace-item/trace-item-detail.d.ts +0 -8
- package/dist/src/components/observability/trace-item/trace-item-detail.js +0 -10
- package/dist/src/components/observability/trace-item/trace-item.d.ts +0 -10
- package/dist/src/components/observability/trace-item/trace-item.js +0 -14
- package/dist/src/components/observability/trace-status.d.ts +0 -8
- package/dist/src/components/observability/trace-status.js +0 -18
- package/dist/src/components/observability/trace-tab-label.d.ts +0 -1
- package/dist/src/components/observability/trace-tab-label.js +0 -5
- package/dist/src/components/observability/trace-timeline.d.ts +0 -6
- package/dist/src/components/observability/trace-timeline.js +0 -30
- package/dist/src/components/observability/traces-groups.d.ts +0 -9
- package/dist/src/components/observability/traces-groups.js +0 -9
- package/dist/src/components/observability/traces-page.d.ts +0 -1
- package/dist/src/components/observability/traces-page.js +0 -33
- package/dist/src/components/states/hooks/states-hooks.d.ts +0 -13
- package/dist/src/components/states/hooks/states-hooks.js +0 -26
- package/dist/src/components/states/state-details.d.ts +0 -7
- package/dist/src/components/states/state-details.js +0 -3
- package/dist/src/components/states/state-editor.d.ts +0 -7
- package/dist/src/components/states/state-editor.js +0 -71
- package/dist/src/components/states/state-sidebar.d.ts +0 -8
- package/dist/src/components/states/state-sidebar.js +0 -17
- package/dist/src/components/states/state-tab-label.d.ts +0 -1
- package/dist/src/components/states/state-tab-label.js +0 -5
- package/dist/src/components/states/states-page.d.ts +0 -1
- package/dist/src/components/states/states-page.js +0 -56
- package/dist/src/types/observability.d.ts +0 -78
- package/dist/vite-plugin-motia-plugins.d.ts +0 -9
- package/dist/vite-plugin-motia-plugins.js +0 -69
- /package/dist/{src/types/observability.js → motia-plugin/__tests__/generator.test.d.ts} +0 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { validatePlugins } from '../validator'
|
|
2
|
+
|
|
3
|
+
describe('Validator', () => {
|
|
4
|
+
describe('validatePlugins', () => {
|
|
5
|
+
it('should validate array of valid plugins', () => {
|
|
6
|
+
const plugins = [
|
|
7
|
+
{ packageName: '@test/plugin-1', label: 'Plugin 1' },
|
|
8
|
+
{ packageName: '@test/plugin-2', label: 'Plugin 2' },
|
|
9
|
+
]
|
|
10
|
+
const result = validatePlugins(plugins)
|
|
11
|
+
|
|
12
|
+
expect(result.valid).toBe(true)
|
|
13
|
+
expect(result.errors).toHaveLength(0)
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
it('should reject non-array input', () => {
|
|
17
|
+
const result = validatePlugins('not-an-array' as any)
|
|
18
|
+
|
|
19
|
+
expect(result.valid).toBe(false)
|
|
20
|
+
expect(result.errors.some((err) => err.includes('array'))).toBe(true)
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
it('should handle empty array', () => {
|
|
24
|
+
const result = validatePlugins([])
|
|
25
|
+
|
|
26
|
+
expect(result.valid).toBe(true)
|
|
27
|
+
expect(result.warnings.some((w) => w.includes('No plugins'))).toBe(true)
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('should collect all errors from multiple plugins', () => {
|
|
31
|
+
const plugins = [{ packageName: '' }, { packageName: '' }, { packageName: 'valid' }]
|
|
32
|
+
const result = validatePlugins(plugins)
|
|
33
|
+
|
|
34
|
+
expect(result.valid).toBe(false)
|
|
35
|
+
expect(result.errors.length).toBeGreaterThanOrEqual(2)
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
it('should warn about duplicate package names', () => {
|
|
39
|
+
const plugins = [
|
|
40
|
+
{ packageName: '@test/plugin', label: 'Plugin 1' },
|
|
41
|
+
{ packageName: '@test/plugin', label: 'Plugin 2' },
|
|
42
|
+
]
|
|
43
|
+
const result = validatePlugins(plugins)
|
|
44
|
+
|
|
45
|
+
expect(result.valid).toBe(true)
|
|
46
|
+
expect(result.warnings.some((w) => w.includes('Duplicate'))).toBe(true)
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it('should support failFast option', () => {
|
|
50
|
+
const plugins = [{ packageName: '' }, { packageName: '' }]
|
|
51
|
+
const result = validatePlugins(plugins, { failFast: true })
|
|
52
|
+
|
|
53
|
+
expect(result.valid).toBe(false)
|
|
54
|
+
// With failFast, should stop after first plugin validation fails
|
|
55
|
+
// Each plugin with empty packageName generates 2 errors (min length + invalid format)
|
|
56
|
+
// So failFast should result in only the first plugin's errors
|
|
57
|
+
expect(result.errors.length).toBeLessThanOrEqual(2)
|
|
58
|
+
// Verify it didn't process second plugin by checking error messages don't mention index 1
|
|
59
|
+
const hasIndex1Error = result.errors.some((err) => err.includes('index 1'))
|
|
60
|
+
expect(hasIndex1Error).toBe(false)
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
it('should handle mixed valid and invalid plugins', () => {
|
|
64
|
+
const plugins = [{ packageName: '@test/valid' }, { packageName: '' }, { packageName: '@test/another-valid' }]
|
|
65
|
+
const result = validatePlugins(plugins)
|
|
66
|
+
|
|
67
|
+
expect(result.valid).toBe(false)
|
|
68
|
+
expect(result.errors.length).toBeGreaterThan(0)
|
|
69
|
+
})
|
|
70
|
+
})
|
|
71
|
+
})
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { getUniquePackageNames } from './resolver'
|
|
2
|
+
import type { WorkbenchPlugin } from './types'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Generates import statements for all unique plugin packages.
|
|
6
|
+
*
|
|
7
|
+
* @param packages - Array of unique package names
|
|
8
|
+
* @returns JavaScript code string with import statements
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* generateImports(['@org/plugin-1', '~/plugins/local'])
|
|
13
|
+
* // Returns:
|
|
14
|
+
* // import * as plugin_0 from '@org/plugin-1'
|
|
15
|
+
* // import * as plugin_1 from '~/plugins/local'
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export function generateImports(packages: string[]): string {
|
|
19
|
+
return packages.map((packageName, index) => `import * as plugin_${index} from '${packageName}'`).join('\n')
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Generates the package map that links package names to their imported modules.
|
|
24
|
+
*
|
|
25
|
+
* @param packages - Array of unique package names
|
|
26
|
+
* @returns JavaScript code string defining the package map
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```ts
|
|
30
|
+
* generatePackageMap(['@org/plugin-1', '~/plugins/local'])
|
|
31
|
+
* // Returns: const packageMap = {'@org/plugin-1': plugin_0,'~/plugins/local': plugin_1}
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export function generatePackageMap(packages: string[]): string {
|
|
35
|
+
const entries = packages.map((packageName, index) => `'${packageName}': plugin_${index}`)
|
|
36
|
+
return `const packageMap = {${entries.join(',')}}`
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Generates the plugin transformation logic that processes plugin configurations.
|
|
41
|
+
*
|
|
42
|
+
* @param plugins - Array of plugin configurations
|
|
43
|
+
* @returns JavaScript code string with plugin processing logic
|
|
44
|
+
*/
|
|
45
|
+
export function generatePluginLogic(plugins: WorkbenchPlugin[]): string {
|
|
46
|
+
return `
|
|
47
|
+
const motiaPlugins = ${JSON.stringify(plugins)}
|
|
48
|
+
|
|
49
|
+
export const plugins = motiaPlugins.map((plugin) => {
|
|
50
|
+
const component = packageMap[plugin.packageName]
|
|
51
|
+
const config = component.config || {}
|
|
52
|
+
const componentName = config.componentName || plugin.componentName
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
label: plugin.label || config.label || 'Plugin label',
|
|
56
|
+
labelIcon: plugin.labelIcon || config.labelIcon || 'toy-brick',
|
|
57
|
+
position: plugin.position || config.position || 'top',
|
|
58
|
+
props: plugin.props || config.props || {},
|
|
59
|
+
component: componentName ? component[componentName] : component.default,
|
|
60
|
+
}
|
|
61
|
+
})
|
|
62
|
+
`
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Generates the complete virtual module code for all plugins.
|
|
67
|
+
* This is the main code generation function that combines all parts.
|
|
68
|
+
*
|
|
69
|
+
* @param plugins - Array of plugin configurations
|
|
70
|
+
* @returns Complete JavaScript code string for the virtual module
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```ts
|
|
74
|
+
* const plugins = [
|
|
75
|
+
* { packageName: '@test/plugin', label: 'Test' }
|
|
76
|
+
* ]
|
|
77
|
+
* const code = generatePluginCode(plugins)
|
|
78
|
+
* // Returns complete module code with imports, map, and logic
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
export function generatePluginCode(plugins: WorkbenchPlugin[]): string {
|
|
82
|
+
if (!plugins || plugins.length === 0) {
|
|
83
|
+
return 'export const plugins = []'
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const packages = getUniquePackageNames(plugins)
|
|
87
|
+
const imports = generateImports(packages)
|
|
88
|
+
const packageMap = generatePackageMap(packages)
|
|
89
|
+
const logic = generatePluginLogic(plugins)
|
|
90
|
+
|
|
91
|
+
return `${imports}
|
|
92
|
+
${packageMap}
|
|
93
|
+
${logic}`
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Generates CSS imports for plugins that specify cssImports.
|
|
98
|
+
*
|
|
99
|
+
* @param plugins - Array of plugin configurations
|
|
100
|
+
* @returns CSS import statements as a string
|
|
101
|
+
*
|
|
102
|
+
* @example
|
|
103
|
+
* ```ts
|
|
104
|
+
* const plugins = [
|
|
105
|
+
* { packageName: '@test/plugin', cssImports: ['styles.css', 'theme.css'] }
|
|
106
|
+
* ]
|
|
107
|
+
* generateCssImports(plugins)
|
|
108
|
+
* // Returns:
|
|
109
|
+
* // @import 'styles.css';
|
|
110
|
+
* // @import 'theme.css';
|
|
111
|
+
* ```
|
|
112
|
+
*/
|
|
113
|
+
export function generateCssImports(plugins: WorkbenchPlugin[]): string {
|
|
114
|
+
const cssImports = plugins
|
|
115
|
+
.flatMap((plugin) => plugin.cssImports || [])
|
|
116
|
+
.filter((cssImport) => cssImport && cssImport.trim() !== '')
|
|
117
|
+
.map((cssImport) => `@import '${cssImport}';`)
|
|
118
|
+
|
|
119
|
+
return cssImports.join('\n')
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Checks if the generated code is valid (non-empty and has content).
|
|
124
|
+
*
|
|
125
|
+
* @param code - The generated code to check
|
|
126
|
+
* @returns True if code is valid
|
|
127
|
+
*/
|
|
128
|
+
export function isValidCode(code: string): boolean {
|
|
129
|
+
return typeof code === 'string' && code.trim().length > 0
|
|
130
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { HmrContext, ModuleNode } from 'vite'
|
|
2
|
+
import type { WorkbenchPlugin } from './types'
|
|
3
|
+
import { CONSTANTS } from './types'
|
|
4
|
+
import { normalizePath } from './utils'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Checks if a file change should trigger HMR for plugins.
|
|
8
|
+
*
|
|
9
|
+
* @param file - The file path that changed
|
|
10
|
+
* @param plugins - Current plugin configurations
|
|
11
|
+
* @returns True if the change affects plugins
|
|
12
|
+
*/
|
|
13
|
+
export function shouldInvalidatePlugins(file: string, plugins: WorkbenchPlugin[]): boolean {
|
|
14
|
+
const normalizedFile = normalizePath(file)
|
|
15
|
+
|
|
16
|
+
// Check if the changed file is a local plugin
|
|
17
|
+
for (const plugin of plugins) {
|
|
18
|
+
if (plugin.packageName.startsWith('~/')) {
|
|
19
|
+
const pluginPath = plugin.packageName.replace('~/', '')
|
|
20
|
+
if (normalizedFile.includes(pluginPath)) {
|
|
21
|
+
return true
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Check if it's a plugin configuration file
|
|
27
|
+
if (normalizedFile.endsWith('motia.config.ts') || normalizedFile.endsWith('motia.config.js')) {
|
|
28
|
+
return true
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return false
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Handles hot updates for the plugin system.
|
|
36
|
+
* This function is called by Vite's handleHotUpdate hook.
|
|
37
|
+
*
|
|
38
|
+
* @param ctx - Vite's HMR context
|
|
39
|
+
* @param plugins - Current plugin configurations
|
|
40
|
+
* @returns Array of modules to update, or undefined to continue with default behavior
|
|
41
|
+
*/
|
|
42
|
+
export function handlePluginHotUpdate(ctx: HmrContext, plugins: WorkbenchPlugin[]): ModuleNode[] | void {
|
|
43
|
+
const { file, server } = ctx
|
|
44
|
+
|
|
45
|
+
console.log('[motia-plugins] HMR: File changed:', file)
|
|
46
|
+
|
|
47
|
+
// Check if this change affects plugins
|
|
48
|
+
if (!shouldInvalidatePlugins(file, plugins)) {
|
|
49
|
+
return // Let Vite handle it normally
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
console.log('[motia-plugins] HMR: Plugin configuration or local plugin changed, invalidating virtual module')
|
|
53
|
+
|
|
54
|
+
// Find the virtual module
|
|
55
|
+
const virtualModule = server.moduleGraph.getModuleById(CONSTANTS.RESOLVED_VIRTUAL_MODULE_ID)
|
|
56
|
+
|
|
57
|
+
if (virtualModule) {
|
|
58
|
+
// Invalidate the virtual module to force regeneration
|
|
59
|
+
server.moduleGraph.invalidateModule(virtualModule)
|
|
60
|
+
console.log('[motia-plugins] HMR: Virtual module invalidated')
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Return modules to update (includes the virtual module and any dependent modules)
|
|
64
|
+
const modulesToUpdate: ModuleNode[] = []
|
|
65
|
+
|
|
66
|
+
if (virtualModule) {
|
|
67
|
+
modulesToUpdate.push(virtualModule)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Add any modules that import the virtual module
|
|
71
|
+
const importers = virtualModule?.importers || new Set()
|
|
72
|
+
for (const importer of importers) {
|
|
73
|
+
modulesToUpdate.push(importer)
|
|
74
|
+
console.log('[motia-plugins] HMR: Invalidating importer:', importer.id)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return modulesToUpdate
|
|
78
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import type { Plugin } from 'vite'
|
|
2
|
+
import { generateCssImports, generatePluginCode, isValidCode } from './generator'
|
|
3
|
+
import { handlePluginHotUpdate } from './hmr'
|
|
4
|
+
import { createAliasConfig } from './resolver'
|
|
5
|
+
import type { WorkbenchPlugin } from './types'
|
|
6
|
+
import { CONSTANTS } from './types'
|
|
7
|
+
import { normalizePath } from './utils'
|
|
8
|
+
import { validatePlugins } from './validator'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Vite plugin for loading and managing Motia workbench plugins.
|
|
12
|
+
*
|
|
13
|
+
* Features:
|
|
14
|
+
* - Hot Module Replacement (HMR) support
|
|
15
|
+
* - Runtime validation with detailed error messages
|
|
16
|
+
* - Verbose logging for debugging
|
|
17
|
+
* - CSS injection for plugin styles
|
|
18
|
+
*
|
|
19
|
+
* @param plugins - Array of plugin configurations
|
|
20
|
+
* @param options - Optional loader configuration
|
|
21
|
+
* @returns Vite plugin instance
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```ts
|
|
25
|
+
* export default defineConfig({
|
|
26
|
+
* plugins: [
|
|
27
|
+
* motiaPluginsPlugin([
|
|
28
|
+
* { packageName: '@my-org/plugin', label: 'My Plugin' }
|
|
29
|
+
* ])
|
|
30
|
+
* ]
|
|
31
|
+
* })
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export default function motiaPluginsPlugin(plugins: WorkbenchPlugin[]): Plugin {
|
|
35
|
+
let devServer: any = null
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
const validationResult = validatePlugins(plugins, {
|
|
39
|
+
failFast: false,
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
if (!validationResult.valid) {
|
|
43
|
+
console.error('[motia-plugins] Plugin configuration validation failed:')
|
|
44
|
+
validationResult.errors.forEach((err) => console.error(`[motia-plugins] ${err}`))
|
|
45
|
+
throw new Error('Invalid plugin configuration. See errors above.')
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (validationResult.warnings.length > 0) {
|
|
49
|
+
validationResult.warnings.forEach((warning) => console.warn('[motia-plugins]', warning))
|
|
50
|
+
}
|
|
51
|
+
} catch (error) {
|
|
52
|
+
console.error('[motia-plugins] Failed to validate plugins:', error)
|
|
53
|
+
throw error
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const alias = createAliasConfig(plugins)
|
|
57
|
+
|
|
58
|
+
console.log(`[motia-plugins] Initialized with ${plugins.length} plugin(s)`)
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
name: 'vite-plugin-motia-plugins',
|
|
62
|
+
enforce: 'pre',
|
|
63
|
+
|
|
64
|
+
buildStart() {
|
|
65
|
+
console.log('[motia-plugins] Build started')
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
config: () => ({
|
|
69
|
+
resolve: {
|
|
70
|
+
alias,
|
|
71
|
+
},
|
|
72
|
+
}),
|
|
73
|
+
|
|
74
|
+
configureServer(server) {
|
|
75
|
+
devServer = server
|
|
76
|
+
console.log('[motia-plugins] Dev server configured, HMR enabled')
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
resolveId(id) {
|
|
80
|
+
if (id === CONSTANTS.VIRTUAL_MODULE_ID) {
|
|
81
|
+
return CONSTANTS.RESOLVED_VIRTUAL_MODULE_ID
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
load(id) {
|
|
86
|
+
if (id !== CONSTANTS.RESOLVED_VIRTUAL_MODULE_ID) {
|
|
87
|
+
return null
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
console.log('[motia-plugins] Loading plugins virtual module')
|
|
91
|
+
console.log('[motia-plugins] Generating plugin code...')
|
|
92
|
+
|
|
93
|
+
const code = generatePluginCode(plugins)
|
|
94
|
+
|
|
95
|
+
if (!isValidCode(code)) {
|
|
96
|
+
console.error('[motia-plugins] Generated code is invalid or empty')
|
|
97
|
+
return 'export const plugins = []'
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
console.log('[motia-plugins] Plugin code generated successfully')
|
|
101
|
+
|
|
102
|
+
return code
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
async transform(code, id) {
|
|
106
|
+
const normalizedId = normalizePath(id)
|
|
107
|
+
|
|
108
|
+
if (!normalizedId.endsWith('/workbench/src/index.css')) {
|
|
109
|
+
return null
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
console.log('[motia-plugins] Injecting plugin CSS imports')
|
|
113
|
+
|
|
114
|
+
const cssImports = generateCssImports(plugins)
|
|
115
|
+
|
|
116
|
+
if (!cssImports) {
|
|
117
|
+
return null
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
code: `${cssImports}\n${code}`,
|
|
122
|
+
map: null,
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
handleHotUpdate(ctx) {
|
|
127
|
+
if (!devServer) {
|
|
128
|
+
return
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const modulesToUpdate = handlePluginHotUpdate(ctx, plugins)
|
|
132
|
+
|
|
133
|
+
if (modulesToUpdate) {
|
|
134
|
+
console.log('[motia-plugins] Hot reloaded plugins')
|
|
135
|
+
return modulesToUpdate
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
|
|
139
|
+
buildEnd() {
|
|
140
|
+
console.log('[motia-plugins] Build ended')
|
|
141
|
+
},
|
|
142
|
+
}
|
|
143
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import type { ResolvedPackage, WorkbenchPlugin } from './types'
|
|
2
|
+
import { isLocalPlugin, normalizePath, resolveLocalPath, resolveNpmPath } from './utils'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Resolves a plugin package to its absolute path and creates an alias.
|
|
6
|
+
*
|
|
7
|
+
* @param plugin - The plugin configuration to resolve
|
|
8
|
+
* @returns Resolved package information including path and alias
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* // Local plugin
|
|
13
|
+
* resolvePluginPackage({ packageName: '~/plugins/my-plugin' })
|
|
14
|
+
* // Returns: {
|
|
15
|
+
* // packageName: '~/plugins/my-plugin',
|
|
16
|
+
* // resolvedPath: '/Users/project/plugins/my-plugin',
|
|
17
|
+
* // isLocal: true,
|
|
18
|
+
* // alias: '~/plugins/my-plugin'
|
|
19
|
+
* // }
|
|
20
|
+
*
|
|
21
|
+
* // NPM package
|
|
22
|
+
* resolvePluginPackage({ packageName: '@org/plugin' })
|
|
23
|
+
* // Returns: {
|
|
24
|
+
* // packageName: '@org/plugin',
|
|
25
|
+
* // resolvedPath: '/Users/project/node_modules/@org/plugin',
|
|
26
|
+
* // isLocal: false,
|
|
27
|
+
* // alias: '@org/plugin'
|
|
28
|
+
* // }
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export function resolvePluginPackage(plugin: WorkbenchPlugin): ResolvedPackage {
|
|
32
|
+
const { packageName } = plugin
|
|
33
|
+
const local = isLocalPlugin(packageName)
|
|
34
|
+
|
|
35
|
+
const resolvedPath = local ? resolveLocalPath(packageName) : resolveNpmPath(packageName)
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
packageName,
|
|
39
|
+
resolvedPath: normalizePath(resolvedPath),
|
|
40
|
+
isLocal: local,
|
|
41
|
+
alias: packageName,
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Resolves all plugin packages and creates a Vite alias configuration.
|
|
47
|
+
*
|
|
48
|
+
* @param plugins - Array of plugin configurations
|
|
49
|
+
* @returns Vite alias configuration object
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```ts
|
|
53
|
+
* const plugins = [
|
|
54
|
+
* { packageName: '~/plugins/local' },
|
|
55
|
+
* { packageName: '@org/npm-plugin' }
|
|
56
|
+
* ]
|
|
57
|
+
* const aliases = createAliasConfig(plugins)
|
|
58
|
+
* // Returns: {
|
|
59
|
+
* // '~/plugins/local': '/Users/project/plugins/local',
|
|
60
|
+
* // '@org/npm-plugin': '/Users/project/node_modules/@org/npm-plugin'
|
|
61
|
+
* // }
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
export function createAliasConfig(plugins: WorkbenchPlugin[]): Record<string, string> {
|
|
65
|
+
// Get unique package names to avoid duplicate aliases
|
|
66
|
+
const uniquePackages = Array.from(new Set(plugins.map((p) => p.packageName)))
|
|
67
|
+
|
|
68
|
+
const aliases: Record<string, string> = {}
|
|
69
|
+
|
|
70
|
+
for (const packageName of uniquePackages) {
|
|
71
|
+
const resolved = resolvePluginPackage({ packageName } as WorkbenchPlugin)
|
|
72
|
+
aliases[packageName] = resolved.resolvedPath
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return aliases
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Resolves all plugins and returns their resolved package information.
|
|
80
|
+
*
|
|
81
|
+
* @param plugins - Array of plugin configurations
|
|
82
|
+
* @returns Array of resolved package information
|
|
83
|
+
*/
|
|
84
|
+
export function resolveAllPlugins(plugins: WorkbenchPlugin[]): ResolvedPackage[] {
|
|
85
|
+
return plugins.map((plugin) => resolvePluginPackage(plugin))
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Gets the unique set of package names from plugins.
|
|
90
|
+
*
|
|
91
|
+
* @param plugins - Array of plugin configurations
|
|
92
|
+
* @returns Array of unique package names
|
|
93
|
+
*/
|
|
94
|
+
export function getUniquePackageNames(plugins: WorkbenchPlugin[]): string[] {
|
|
95
|
+
return Array.from(new Set(plugins.map((p) => p.packageName)))
|
|
96
|
+
}
|