motia 0.8.2-beta.140-428722 → 0.8.3-beta.140
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/cjs/cloud/build/builders/archiver.d.ts +2 -1
- package/dist/cjs/cloud/build/builders/archiver.js +25 -14
- package/dist/cjs/cloud/build/builders/include-static-files.js +2 -1
- package/dist/cjs/cloud/build/builders/node/index.js +4 -4
- package/dist/cjs/cloud/build/builders/python/index.js +1 -1
- package/dist/cjs/create/index.js +33 -32
- package/dist/cjs/create/interactive.js +1 -0
- package/dist/cjs/create/templates/generate.d.ts +1 -0
- package/dist/cjs/create/templates/generate.js +89 -18
- package/dist/cjs/create/templates/generate.ts +93 -19
- package/dist/cjs/create/templates/index.js +1 -0
- package/dist/cjs/create/templates/index.ts +2 -1
- package/dist/cjs/create/templates/nodejs/tutorial.tsx.txt +1 -1
- package/dist/cjs/create/templates/plugin/.gitignore.txt +3 -0
- package/dist/cjs/create/templates/plugin/README.md.txt +64 -0
- package/dist/cjs/create/templates/plugin/package.json.txt +47 -0
- package/dist/cjs/create/templates/plugin/postcss.config.js.txt +6 -0
- package/dist/cjs/create/templates/plugin/src/components/example-page.tsx.txt +64 -0
- package/dist/cjs/create/templates/plugin/src/index.ts.txt +4 -0
- package/dist/cjs/create/templates/plugin/src/plugin.ts.txt +17 -0
- package/dist/cjs/create/templates/plugin/src/styles.css.txt +3 -0
- package/dist/cjs/create/templates/plugin/tsconfig.json.txt +27 -0
- package/dist/cjs/create/templates/plugin/vite.config.ts.txt +30 -0
- package/dist/cjs/create/templates/python/tutorial.tsx.txt +1 -1
- package/dist/cjs/dev.js +10 -18
- package/dist/cjs/generate-locked-data.d.ts +3 -2
- package/dist/cjs/generate-locked-data.js +2 -2
- package/dist/cjs/plugins/collect-plugin-steps.d.ts +5 -0
- package/dist/cjs/plugins/collect-plugin-steps.js +44 -0
- package/dist/cjs/plugins/create-plugin-context.d.ts +2 -0
- package/dist/cjs/plugins/create-plugin-context.js +33 -0
- package/dist/cjs/plugins/index.d.ts +1 -0
- package/dist/cjs/plugins/index.js +5 -0
- package/dist/cjs/plugins/install-plugin-dependencies.d.ts +2 -0
- package/dist/cjs/plugins/install-plugin-dependencies.js +56 -0
- package/dist/cjs/plugins/load-config.d.ts +2 -0
- package/dist/cjs/{load-motia-config.js → plugins/load-config.js} +17 -12
- package/dist/cjs/plugins/plugin-dependencies.d.ts +1 -0
- package/dist/cjs/plugins/plugin-dependencies.js +10 -0
- package/dist/cjs/{generate-plugins.d.ts → plugins/process-plugins.d.ts} +1 -1
- package/dist/cjs/plugins/process-plugins.js +20 -0
- package/dist/cjs/plugins/process-steps.d.ts +2 -0
- package/dist/cjs/plugins/process-steps.js +40 -0
- package/dist/cjs/start.js +7 -15
- package/dist/cjs/utils/get-package-manager.d.ts +1 -0
- package/dist/cjs/utils/get-package-manager.js +19 -0
- package/dist/esm/cloud/build/builders/archiver.d.ts +2 -1
- package/dist/esm/cloud/build/builders/archiver.js +25 -14
- package/dist/esm/cloud/build/builders/include-static-files.js +2 -1
- package/dist/esm/cloud/build/builders/node/index.js +4 -4
- package/dist/esm/cloud/build/builders/python/index.js +1 -1
- package/dist/esm/create/index.js +32 -31
- package/dist/esm/create/interactive.js +1 -0
- package/dist/esm/create/templates/generate.d.ts +1 -0
- package/dist/esm/create/templates/generate.js +87 -17
- package/dist/esm/create/templates/generate.ts +93 -19
- package/dist/esm/create/templates/index.js +2 -1
- package/dist/esm/create/templates/index.ts +2 -1
- package/dist/esm/create/templates/nodejs/tutorial.tsx.txt +1 -1
- package/dist/esm/create/templates/plugin/.gitignore.txt +3 -0
- package/dist/esm/create/templates/plugin/README.md.txt +64 -0
- package/dist/esm/create/templates/plugin/package.json.txt +47 -0
- package/dist/esm/create/templates/plugin/postcss.config.js.txt +6 -0
- package/dist/esm/create/templates/plugin/src/components/example-page.tsx.txt +64 -0
- package/dist/esm/create/templates/plugin/src/index.ts.txt +4 -0
- package/dist/esm/create/templates/plugin/src/plugin.ts.txt +17 -0
- package/dist/esm/create/templates/plugin/src/styles.css.txt +3 -0
- package/dist/esm/create/templates/plugin/tsconfig.json.txt +27 -0
- package/dist/esm/create/templates/plugin/vite.config.ts.txt +30 -0
- package/dist/esm/create/templates/python/tutorial.tsx.txt +1 -1
- package/dist/esm/dev.js +10 -18
- package/dist/esm/generate-locked-data.d.ts +3 -2
- package/dist/esm/generate-locked-data.js +2 -2
- package/dist/esm/plugins/collect-plugin-steps.d.ts +5 -0
- package/dist/esm/plugins/collect-plugin-steps.js +37 -0
- package/dist/esm/plugins/create-plugin-context.d.ts +2 -0
- package/dist/esm/plugins/create-plugin-context.js +29 -0
- package/dist/esm/plugins/index.d.ts +1 -0
- package/dist/esm/plugins/index.js +1 -0
- package/dist/esm/plugins/install-plugin-dependencies.d.ts +2 -0
- package/dist/esm/plugins/install-plugin-dependencies.js +49 -0
- package/dist/esm/plugins/load-config.d.ts +2 -0
- package/dist/esm/plugins/load-config.js +17 -0
- package/dist/esm/plugins/plugin-dependencies.d.ts +1 -0
- package/dist/esm/plugins/plugin-dependencies.js +7 -0
- package/dist/esm/{generate-plugins.d.ts → plugins/process-plugins.d.ts} +1 -1
- package/dist/esm/plugins/process-plugins.js +16 -0
- package/dist/esm/plugins/process-steps.d.ts +2 -0
- package/dist/esm/plugins/process-steps.js +36 -0
- package/dist/esm/start.js +7 -15
- package/dist/esm/utils/get-package-manager.d.ts +1 -0
- package/dist/esm/utils/get-package-manager.js +15 -0
- package/dist/types/cloud/build/builders/archiver.d.ts +2 -1
- package/dist/types/create/templates/generate.d.ts +1 -0
- package/dist/types/generate-locked-data.d.ts +3 -2
- package/dist/types/plugins/collect-plugin-steps.d.ts +5 -0
- package/dist/types/plugins/create-plugin-context.d.ts +2 -0
- package/dist/types/plugins/index.d.ts +1 -0
- package/dist/types/plugins/install-plugin-dependencies.d.ts +2 -0
- package/dist/types/plugins/load-config.d.ts +2 -0
- package/dist/types/plugins/plugin-dependencies.d.ts +1 -0
- package/dist/types/{generate-plugins.d.ts → plugins/process-plugins.d.ts} +1 -1
- package/dist/types/plugins/process-steps.d.ts +2 -0
- package/dist/types/utils/get-package-manager.d.ts +1 -0
- package/package.json +4 -4
- package/dist/cjs/generate-plugins.js +0 -163
- package/dist/cjs/load-motia-config.d.ts +0 -2
- package/dist/esm/generate-plugins.js +0 -123
- package/dist/esm/load-motia-config.d.ts +0 -2
- package/dist/esm/load-motia-config.js +0 -15
- package/dist/types/load-motia-config.d.ts +0 -2
|
@@ -9,6 +9,7 @@ export declare class Archiver {
|
|
|
9
9
|
private uncompressedSize;
|
|
10
10
|
constructor(filePath: string);
|
|
11
11
|
appendDirectory(sourcePath: string, targetPath: string): void;
|
|
12
|
-
|
|
12
|
+
private calculateDirectorySize;
|
|
13
|
+
append(stream: fs.ReadStream | string | Buffer, filePath: string): void;
|
|
13
14
|
finalize(): Promise<ArchiveResult>;
|
|
14
15
|
}
|
|
@@ -16,39 +16,50 @@ class Archiver {
|
|
|
16
16
|
}
|
|
17
17
|
appendDirectory(sourcePath, targetPath) {
|
|
18
18
|
try {
|
|
19
|
-
const
|
|
19
|
+
const stat = fs_1.default.statSync(sourcePath);
|
|
20
|
+
if (!stat.isDirectory()) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
this.uncompressedSize += this.calculateDirectorySize(sourcePath);
|
|
24
|
+
this.archive.directory(sourcePath, targetPath === '/' ? false : targetPath);
|
|
25
|
+
}
|
|
26
|
+
catch (_error) { }
|
|
27
|
+
}
|
|
28
|
+
calculateDirectorySize(dirPath) {
|
|
29
|
+
let totalSize = 0;
|
|
30
|
+
try {
|
|
31
|
+
const items = fs_1.default.readdirSync(dirPath);
|
|
20
32
|
for (const item of items) {
|
|
21
|
-
const fullPath = path_1.default.join(
|
|
33
|
+
const fullPath = path_1.default.join(dirPath, item);
|
|
22
34
|
try {
|
|
23
35
|
const stat = fs_1.default.statSync(fullPath);
|
|
24
36
|
if (stat.isDirectory()) {
|
|
25
|
-
this.
|
|
37
|
+
totalSize += this.calculateDirectorySize(fullPath);
|
|
26
38
|
}
|
|
27
39
|
else {
|
|
28
|
-
|
|
40
|
+
totalSize += stat.size;
|
|
29
41
|
}
|
|
30
42
|
}
|
|
31
|
-
catch (_error) {
|
|
32
|
-
// Ignore individual file errors
|
|
33
|
-
}
|
|
43
|
+
catch (_error) { }
|
|
34
44
|
}
|
|
35
45
|
}
|
|
36
|
-
catch (_error) {
|
|
37
|
-
|
|
38
|
-
}
|
|
46
|
+
catch (_error) { }
|
|
47
|
+
return totalSize;
|
|
39
48
|
}
|
|
40
49
|
append(stream, filePath) {
|
|
41
|
-
// Track uncompressed size
|
|
42
50
|
if (typeof stream === 'string') {
|
|
43
|
-
// String content
|
|
44
51
|
this.uncompressedSize += Buffer.byteLength(stream, 'utf8');
|
|
52
|
+
this.archive.append(stream, { name: filePath });
|
|
53
|
+
}
|
|
54
|
+
else if (Buffer.isBuffer(stream)) {
|
|
55
|
+
this.uncompressedSize += stream.length;
|
|
56
|
+
this.archive.append(stream, { name: filePath });
|
|
45
57
|
}
|
|
46
58
|
else {
|
|
47
|
-
// ReadStream - get file stats
|
|
48
59
|
const stats = fs_1.default.statSync(stream.path);
|
|
49
60
|
this.uncompressedSize += stats.size;
|
|
61
|
+
this.archive.append(stream, { name: filePath });
|
|
50
62
|
}
|
|
51
|
-
this.archive.append(stream, { name: filePath });
|
|
52
63
|
}
|
|
53
64
|
async finalize() {
|
|
54
65
|
return new Promise((resolve, reject) => {
|
|
@@ -18,7 +18,8 @@ const includeStaticFiles = (steps, builder, archive) => {
|
|
|
18
18
|
const matches = (0, glob_1.globSync)(file, { cwd: path_1.default.dirname(step.filePath), absolute: true });
|
|
19
19
|
matches.forEach((filePath) => {
|
|
20
20
|
const relativeFilePath = path_1.default.relative(builder.projectDir, filePath);
|
|
21
|
-
|
|
21
|
+
const content = fs_1.default.readFileSync(filePath);
|
|
22
|
+
archive.append(content, relativeFilePath);
|
|
22
23
|
});
|
|
23
24
|
});
|
|
24
25
|
}
|
|
@@ -94,8 +94,8 @@ class NodeBuilder {
|
|
|
94
94
|
const archiver = new archiver_1.Archiver(path_1.default.join(constants_1.distDir, zipName));
|
|
95
95
|
const routerJs = path_1.default.join(constants_1.distDir, 'router.js');
|
|
96
96
|
const routerMap = path_1.default.join(constants_1.distDir, 'router.js.map');
|
|
97
|
-
archiver.append(fs_1.default.
|
|
98
|
-
archiver.append(fs_1.default.
|
|
97
|
+
archiver.append(fs_1.default.readFileSync(routerJs), 'router.js');
|
|
98
|
+
archiver.append(fs_1.default.readFileSync(routerMap), 'router.js.map');
|
|
99
99
|
(0, include_static_files_1.includeStaticFiles)(steps, this.builder, archiver);
|
|
100
100
|
const { compressedSize, uncompressedSize } = await archiver.finalize();
|
|
101
101
|
fs_1.default.unlinkSync(tsRouter);
|
|
@@ -123,8 +123,8 @@ class NodeBuilder {
|
|
|
123
123
|
};
|
|
124
124
|
await esbuild.build(userConfig ? { ...defaultConfig, ...userConfig } : defaultConfig);
|
|
125
125
|
const archiver = new archiver_1.Archiver(path_1.default.join(constants_1.distDir, bundlePath));
|
|
126
|
-
archiver.append(fs_1.default.
|
|
127
|
-
archiver.append(fs_1.default.
|
|
126
|
+
archiver.append(fs_1.default.readFileSync(outputJsFile), entrypointPath);
|
|
127
|
+
archiver.append(fs_1.default.readFileSync(outputMapFile), entrypointMapPath);
|
|
128
128
|
(0, include_static_files_1.includeStaticFiles)([step], this.builder, archiver);
|
|
129
129
|
const { compressedSize, uncompressedSize } = await archiver.finalize();
|
|
130
130
|
fs_1.default.unlinkSync(outputJsFile);
|
|
@@ -123,7 +123,7 @@ class PythonBuilder {
|
|
|
123
123
|
}
|
|
124
124
|
getModuleName(step) {
|
|
125
125
|
// return step path
|
|
126
|
-
return step.filePath.replace(this.builder.projectDir, '').substring(1).replace(/\.py$/, '').replace(
|
|
126
|
+
return step.filePath.replace(this.builder.projectDir, '').substring(1).replace(/\.py$/, '').replace(/[\\/]/g, '.');
|
|
127
127
|
}
|
|
128
128
|
}
|
|
129
129
|
exports.PythonBuilder = PythonBuilder;
|
package/dist/cjs/create/index.js
CHANGED
|
@@ -8,7 +8,9 @@ const fs_1 = __importDefault(require("fs"));
|
|
|
8
8
|
const path_1 = __importDefault(require("path"));
|
|
9
9
|
const generate_types_1 = require("../generate-types");
|
|
10
10
|
const install_1 = require("../install");
|
|
11
|
+
const plugin_dependencies_1 = require("../plugins/plugin-dependencies");
|
|
11
12
|
const execute_command_1 = require("../utils/execute-command");
|
|
13
|
+
const get_package_manager_1 = require("../utils/get-package-manager");
|
|
12
14
|
const version_1 = require("../version");
|
|
13
15
|
const pull_rules_1 = require("./pull-rules");
|
|
14
16
|
const setup_template_1 = require("./setup-template");
|
|
@@ -17,20 +19,6 @@ require('ts-node').register({
|
|
|
17
19
|
transpileOnly: true,
|
|
18
20
|
compilerOptions: { module: 'commonjs' },
|
|
19
21
|
});
|
|
20
|
-
const getPackageManager = (dir) => {
|
|
21
|
-
if ((0, utils_1.checkIfFileExists)(dir, 'yarn.lock')) {
|
|
22
|
-
return 'yarn';
|
|
23
|
-
}
|
|
24
|
-
else if ((0, utils_1.checkIfFileExists)(dir, 'pnpm-lock.yaml')) {
|
|
25
|
-
return 'pnpm';
|
|
26
|
-
}
|
|
27
|
-
else if ((0, utils_1.checkIfFileExists)(dir, 'package-lock.json')) {
|
|
28
|
-
return 'npm';
|
|
29
|
-
}
|
|
30
|
-
else {
|
|
31
|
-
return 'unknown';
|
|
32
|
-
}
|
|
33
|
-
};
|
|
34
22
|
const installRequiredDependencies = async (packageManager, rootDir, context) => {
|
|
35
23
|
context.log('installing-dependencies', (message) => message.tag('info').append('Installing dependencies...'));
|
|
36
24
|
const installCommand = {
|
|
@@ -47,11 +35,7 @@ const installRequiredDependencies = async (packageManager, rootDir, context) =>
|
|
|
47
35
|
'@types/jest@^29.5.14',
|
|
48
36
|
'jest@^29.7.0',
|
|
49
37
|
'ts-jest@^29.2.5',
|
|
50
|
-
|
|
51
|
-
`@motiadev/plugin-logs@${version_1.version}`,
|
|
52
|
-
`@motiadev/plugin-states@${version_1.version}`,
|
|
53
|
-
`@motiadev/plugin-endpoint@${version_1.version}`,
|
|
54
|
-
`@motiadev/plugin-observability@${version_1.version}`,
|
|
38
|
+
...plugin_dependencies_1.pluginDependencies.map((dep) => `${dep}@${version_1.version}`),
|
|
55
39
|
].join(' ');
|
|
56
40
|
try {
|
|
57
41
|
await (0, execute_command_1.executeCommand)(`${installCommand} ${dependencies}`, rootDir);
|
|
@@ -64,7 +48,7 @@ const installRequiredDependencies = async (packageManager, rootDir, context) =>
|
|
|
64
48
|
};
|
|
65
49
|
const preparePackageManager = async (rootDir, context) => {
|
|
66
50
|
let packageManager = 'npm';
|
|
67
|
-
const detectedPackageManager = getPackageManager(rootDir);
|
|
51
|
+
const detectedPackageManager = (0, get_package_manager_1.getPackageManager)(rootDir);
|
|
68
52
|
if (detectedPackageManager !== 'unknown') {
|
|
69
53
|
context.log('package-manager-detected', (message) => message.tag('info').append('Detected package manager').append(detectedPackageManager, 'gray'));
|
|
70
54
|
packageManager = detectedPackageManager;
|
|
@@ -82,9 +66,14 @@ const installNodeDependencies = async (rootDir, context) => {
|
|
|
82
66
|
});
|
|
83
67
|
return packageManager;
|
|
84
68
|
};
|
|
85
|
-
const wrapUp = async (context, packageManager) => {
|
|
69
|
+
const wrapUp = async (context, packageManager, isPlugin = false) => {
|
|
86
70
|
context.log('project-setup-completed', (message) => message.tag('success').append('Project setup completed, happy coding!'));
|
|
87
|
-
|
|
71
|
+
if (isPlugin) {
|
|
72
|
+
context.log('package-manager-used', (message) => message.tag('info').append('To build the plugin, run').append(`${packageManager} run build`, 'gray'));
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
context.log('package-manager-used', (message) => message.tag('info').append('To start the development server, run').append(`${packageManager} run dev`, 'gray'));
|
|
76
|
+
}
|
|
88
77
|
};
|
|
89
78
|
const create = async ({ projectName, template, cursorEnabled, context }) => {
|
|
90
79
|
console.log('\n\n' +
|
|
@@ -100,6 +89,7 @@ const create = async ({ projectName, template, cursorEnabled, context }) => {
|
|
|
100
89
|
'\n\n');
|
|
101
90
|
const isCurrentDir = projectName === '.' || projectName === './' || projectName === '.\\';
|
|
102
91
|
const rootDir = isCurrentDir ? process.cwd() : path_1.default.join(process.cwd(), projectName);
|
|
92
|
+
const isPluginTemplate = template === 'plugin';
|
|
103
93
|
if (!isCurrentDir && !(0, utils_1.checkIfDirectoryExists)(rootDir)) {
|
|
104
94
|
fs_1.default.mkdirSync(path_1.default.join(rootDir));
|
|
105
95
|
context.log('directory-created', (message) => message.tag('success').append('Directory created ').append(projectName, 'gray'));
|
|
@@ -107,7 +97,8 @@ const create = async ({ projectName, template, cursorEnabled, context }) => {
|
|
|
107
97
|
else {
|
|
108
98
|
context.log('directory-using', (message) => message.tag('info').append('Using current directory'));
|
|
109
99
|
}
|
|
110
|
-
|
|
100
|
+
// Plugin template handles package.json differently (via template)
|
|
101
|
+
if (!isPluginTemplate && !(0, utils_1.checkIfFileExists)(rootDir, 'package.json')) {
|
|
111
102
|
const finalProjectName = !projectName || projectName === '.' || projectName === './' || projectName === '.\\'
|
|
112
103
|
? path_1.default.basename(process.cwd())
|
|
113
104
|
: projectName.trim();
|
|
@@ -127,7 +118,7 @@ const create = async ({ projectName, template, cursorEnabled, context }) => {
|
|
|
127
118
|
fs_1.default.writeFileSync(path_1.default.join(rootDir, 'package.json'), JSON.stringify(packageJsonContent, null, 2));
|
|
128
119
|
context.log('package-json-created', (message) => message.tag('success').append('File').append('package.json', 'cyan').append('has been created.'));
|
|
129
120
|
}
|
|
130
|
-
else {
|
|
121
|
+
else if (!isPluginTemplate) {
|
|
131
122
|
const packageJsonPath = path_1.default.join(rootDir, 'package.json');
|
|
132
123
|
const packageJson = JSON.parse(fs_1.default.readFileSync(packageJsonPath, 'utf-8'));
|
|
133
124
|
if (!packageJson.scripts) {
|
|
@@ -149,7 +140,8 @@ const create = async ({ projectName, template, cursorEnabled, context }) => {
|
|
|
149
140
|
.append('command to')
|
|
150
141
|
.append('package.json', 'gray'));
|
|
151
142
|
}
|
|
152
|
-
|
|
143
|
+
// Plugin template handles tsconfig.json via template
|
|
144
|
+
if (!isPluginTemplate && !(0, utils_1.checkIfFileExists)(rootDir, 'tsconfig.json')) {
|
|
153
145
|
const tsconfigContent = {
|
|
154
146
|
compilerOptions: {
|
|
155
147
|
target: 'ES2020',
|
|
@@ -172,7 +164,8 @@ const create = async ({ projectName, template, cursorEnabled, context }) => {
|
|
|
172
164
|
fs_1.default.writeFileSync(path_1.default.join(rootDir, 'tsconfig.json'), JSON.stringify(tsconfigContent, null, 2));
|
|
173
165
|
context.log('tsconfig-json-created', (message) => message.tag('success').append('File').append('tsconfig.json', 'cyan').append('has been created.'));
|
|
174
166
|
}
|
|
175
|
-
|
|
167
|
+
// Plugin template handles .gitignore via template
|
|
168
|
+
if (!isPluginTemplate && !(0, utils_1.checkIfFileExists)(rootDir, '.gitignore')) {
|
|
176
169
|
const gitignoreContent = [
|
|
177
170
|
'node_modules',
|
|
178
171
|
'python_modules',
|
|
@@ -186,18 +179,26 @@ const create = async ({ projectName, template, cursorEnabled, context }) => {
|
|
|
186
179
|
fs_1.default.writeFileSync(path_1.default.join(rootDir, '.gitignore'), gitignoreContent);
|
|
187
180
|
context.log('gitignore-created', (message) => message.tag('success').append('File').append('.gitignore', 'cyan').append('has been created.'));
|
|
188
181
|
}
|
|
189
|
-
|
|
182
|
+
// Skip cursor rules for plugin template
|
|
183
|
+
if (!isPluginTemplate && cursorEnabled) {
|
|
190
184
|
await (0, pull_rules_1.pullRules)({ force: true, rootDir }, context);
|
|
191
185
|
}
|
|
192
186
|
if (template) {
|
|
193
187
|
await (0, setup_template_1.setupTemplate)(template, rootDir, context);
|
|
194
188
|
}
|
|
195
|
-
|
|
196
|
-
if (
|
|
197
|
-
await (
|
|
189
|
+
let packageManager;
|
|
190
|
+
if (!isPluginTemplate) {
|
|
191
|
+
packageManager = await installNodeDependencies(rootDir, context);
|
|
192
|
+
if (template === 'python') {
|
|
193
|
+
await (0, install_1.pythonInstall)({ baseDir: rootDir });
|
|
194
|
+
}
|
|
195
|
+
await (0, generate_types_1.generateTypes)(rootDir);
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
// For plugin template, just detect the package manager
|
|
199
|
+
packageManager = await preparePackageManager(rootDir, context);
|
|
198
200
|
}
|
|
199
|
-
await (
|
|
200
|
-
await wrapUp(context, packageManager);
|
|
201
|
+
await wrapUp(context, packageManager, isPluginTemplate);
|
|
201
202
|
return;
|
|
202
203
|
};
|
|
203
204
|
exports.create = create;
|
|
@@ -10,6 +10,7 @@ const index_1 = require("./index");
|
|
|
10
10
|
const choices = {
|
|
11
11
|
nodejs: 'Base (TypeScript)',
|
|
12
12
|
python: 'Base (Python)',
|
|
13
|
+
plugin: 'Plugin (TypeScript)',
|
|
13
14
|
};
|
|
14
15
|
const createInteractive = async (args, context) => {
|
|
15
16
|
context.log('welcome', (message) => message.append('\n🚀 ' + colors_1.default.bold('Welcome to Motia Project Creator!')));
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import type { CliContext } from '../../cloud/config-utils';
|
|
2
2
|
export type Generator = (rootDir: string, context: CliContext) => Promise<void>;
|
|
3
3
|
export declare const generateTemplateSteps: (templateFolder: string) => Generator;
|
|
4
|
+
export declare const generatePluginTemplate: (templateFolder: string) => Generator;
|
|
@@ -33,10 +33,28 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.generateTemplateSteps = void 0;
|
|
36
|
+
exports.generatePluginTemplate = exports.generateTemplateSteps = void 0;
|
|
37
37
|
const fs_1 = require("fs");
|
|
38
38
|
const glob_1 = require("glob");
|
|
39
39
|
const path = __importStar(require("path"));
|
|
40
|
+
const replaceTemplateVariables = (content, projectName) => {
|
|
41
|
+
const replacements = {
|
|
42
|
+
'{{PROJECT_NAME}}': projectName,
|
|
43
|
+
'{{PLUGIN_NAME}}': toPascalCase(projectName),
|
|
44
|
+
'{{CSS_FILE_NAME}}': projectName.replace(/^@[^/]+\//, ''),
|
|
45
|
+
};
|
|
46
|
+
return Object.entries(replacements).reduce((result, [key, value]) => {
|
|
47
|
+
return result.replace(new RegExp(key, 'g'), value);
|
|
48
|
+
}, content);
|
|
49
|
+
};
|
|
50
|
+
const toPascalCase = (str) => {
|
|
51
|
+
// Remove @ and scope if present
|
|
52
|
+
const name = str.replace(/^@[^/]+\//, '');
|
|
53
|
+
return name
|
|
54
|
+
.split(/[-_]/)
|
|
55
|
+
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
56
|
+
.join('');
|
|
57
|
+
};
|
|
40
58
|
const generateTemplateSteps = (templateFolder) => {
|
|
41
59
|
return async (rootDir, context) => {
|
|
42
60
|
const templatePath = path.join(__dirname, templateFolder);
|
|
@@ -46,13 +64,7 @@ const generateTemplateSteps = (templateFolder) => {
|
|
|
46
64
|
const filePath = path.join(templatePath, fileName);
|
|
47
65
|
const targetFilePath = path.join(rootDir, fileName);
|
|
48
66
|
const targetDir = path.dirname(targetFilePath);
|
|
49
|
-
|
|
50
|
-
// Check if it's a directory in the template
|
|
51
|
-
(0, fs_1.statSync)(targetDir);
|
|
52
|
-
}
|
|
53
|
-
catch {
|
|
54
|
-
(0, fs_1.mkdirSync)(targetDir, { recursive: true });
|
|
55
|
-
}
|
|
67
|
+
(0, fs_1.mkdirSync)(targetDir, { recursive: true });
|
|
56
68
|
if ((0, fs_1.statSync)(filePath).isDirectory()) {
|
|
57
69
|
const folderPath = filePath.replace(templatePath, '');
|
|
58
70
|
(0, fs_1.mkdirSync)(path.join(rootDir, folderPath), { recursive: true });
|
|
@@ -62,19 +74,37 @@ const generateTemplateSteps = (templateFolder) => {
|
|
|
62
74
|
const isWorkbenchConfig = fileName.match('motia-workbench.json');
|
|
63
75
|
const generateFilePath = path.join(rootDir, sanitizedFileName);
|
|
64
76
|
let content = await fs_1.promises.readFile(filePath, 'utf8');
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
77
|
+
if (isWorkbenchConfig) {
|
|
78
|
+
try {
|
|
79
|
+
// Use file descriptor to avoid TOCTOU vulnerability
|
|
80
|
+
let fd = null;
|
|
81
|
+
try {
|
|
82
|
+
// Try to open existing file for reading
|
|
83
|
+
fd = await fs_1.promises.open(generateFilePath, fs_1.constants.O_RDONLY);
|
|
84
|
+
const existingWorkbenchConfig = await fd.readFile('utf8');
|
|
85
|
+
const workbenchContent = JSON.parse(content);
|
|
86
|
+
content = JSON.stringify([...JSON.parse(existingWorkbenchConfig), ...workbenchContent], null, 2);
|
|
87
|
+
context.log('workbench-config-updated', (message) => message.tag('success').append('Workbench config').append('has been updated.'));
|
|
88
|
+
}
|
|
89
|
+
finally {
|
|
90
|
+
if (fd)
|
|
91
|
+
await fd.close();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
void 0;
|
|
72
96
|
}
|
|
73
97
|
}
|
|
74
|
-
|
|
75
|
-
|
|
98
|
+
// Use file descriptor for atomic write operation
|
|
99
|
+
let fd = null;
|
|
100
|
+
try {
|
|
101
|
+
fd = await fs_1.promises.open(generateFilePath, fs_1.constants.O_CREAT | fs_1.constants.O_WRONLY | fs_1.constants.O_TRUNC, 0o644);
|
|
102
|
+
await fd.writeFile(content, 'utf8');
|
|
103
|
+
}
|
|
104
|
+
finally {
|
|
105
|
+
if (fd)
|
|
106
|
+
await fd.close();
|
|
76
107
|
}
|
|
77
|
-
await fs_1.promises.writeFile(generateFilePath, content, 'utf8');
|
|
78
108
|
context.log(sanitizedFileName, (message) => {
|
|
79
109
|
message.tag('success').append('File').append(sanitizedFileName, 'cyan').append('has been created.');
|
|
80
110
|
});
|
|
@@ -86,3 +116,44 @@ const generateTemplateSteps = (templateFolder) => {
|
|
|
86
116
|
};
|
|
87
117
|
};
|
|
88
118
|
exports.generateTemplateSteps = generateTemplateSteps;
|
|
119
|
+
const generatePluginTemplate = (templateFolder) => {
|
|
120
|
+
return async (rootDir, context) => {
|
|
121
|
+
const templatePath = path.join(__dirname, templateFolder);
|
|
122
|
+
const files = (0, glob_1.globSync)('**/*', { absolute: false, cwd: templatePath, dot: true });
|
|
123
|
+
const projectName = path.basename(rootDir);
|
|
124
|
+
try {
|
|
125
|
+
for (const fileName of files) {
|
|
126
|
+
const filePath = path.join(templatePath, fileName);
|
|
127
|
+
const targetFilePath = path.join(rootDir, fileName);
|
|
128
|
+
const targetDir = path.dirname(targetFilePath);
|
|
129
|
+
(0, fs_1.mkdirSync)(targetDir, { recursive: true });
|
|
130
|
+
if ((0, fs_1.statSync)(filePath).isDirectory()) {
|
|
131
|
+
const folderPath = filePath.replace(templatePath, '');
|
|
132
|
+
(0, fs_1.mkdirSync)(path.join(rootDir, folderPath), { recursive: true });
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
const sanitizedFileName = fileName.replace('.txt', '');
|
|
136
|
+
const generateFilePath = path.join(rootDir, sanitizedFileName);
|
|
137
|
+
let content = await fs_1.promises.readFile(filePath, 'utf8');
|
|
138
|
+
content = replaceTemplateVariables(content, projectName);
|
|
139
|
+
// Use file descriptor for atomic write operation
|
|
140
|
+
let fd = null;
|
|
141
|
+
try {
|
|
142
|
+
fd = await fs_1.promises.open(generateFilePath, fs_1.constants.O_CREAT | fs_1.constants.O_WRONLY | fs_1.constants.O_TRUNC, 0o644);
|
|
143
|
+
await fd.writeFile(content, 'utf8');
|
|
144
|
+
}
|
|
145
|
+
finally {
|
|
146
|
+
if (fd)
|
|
147
|
+
await fd.close();
|
|
148
|
+
}
|
|
149
|
+
context.log(sanitizedFileName, (message) => {
|
|
150
|
+
message.tag('success').append('File').append(sanitizedFileName, 'cyan').append('has been created.');
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
console.error('Error generating template files:', error);
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
};
|
|
159
|
+
exports.generatePluginTemplate = generatePluginTemplate;
|
|
@@ -1,10 +1,31 @@
|
|
|
1
|
-
import { promises as fs, mkdirSync, statSync } from 'fs'
|
|
1
|
+
import { constants, promises as fs, mkdirSync, statSync } from 'fs'
|
|
2
2
|
import { globSync } from 'glob'
|
|
3
3
|
import * as path from 'path'
|
|
4
4
|
import type { CliContext } from '../../cloud/config-utils'
|
|
5
5
|
|
|
6
6
|
export type Generator = (rootDir: string, context: CliContext) => Promise<void>
|
|
7
7
|
|
|
8
|
+
const replaceTemplateVariables = (content: string, projectName: string): string => {
|
|
9
|
+
const replacements: Record<string, string> = {
|
|
10
|
+
'{{PROJECT_NAME}}': projectName,
|
|
11
|
+
'{{PLUGIN_NAME}}': toPascalCase(projectName),
|
|
12
|
+
'{{CSS_FILE_NAME}}': projectName.replace(/^@[^/]+\//, ''),
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return Object.entries(replacements).reduce((result, [key, value]) => {
|
|
16
|
+
return result.replace(new RegExp(key, 'g'), value)
|
|
17
|
+
}, content)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const toPascalCase = (str: string): string => {
|
|
21
|
+
// Remove @ and scope if present
|
|
22
|
+
const name = str.replace(/^@[^/]+\//, '')
|
|
23
|
+
return name
|
|
24
|
+
.split(/[-_]/)
|
|
25
|
+
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
26
|
+
.join('')
|
|
27
|
+
}
|
|
28
|
+
|
|
8
29
|
export const generateTemplateSteps = (templateFolder: string): Generator => {
|
|
9
30
|
return async (rootDir: string, context: CliContext): Promise<void> => {
|
|
10
31
|
const templatePath = path.join(__dirname, templateFolder)
|
|
@@ -16,12 +37,7 @@ export const generateTemplateSteps = (templateFolder: string): Generator => {
|
|
|
16
37
|
const targetFilePath = path.join(rootDir, fileName)
|
|
17
38
|
const targetDir = path.dirname(targetFilePath)
|
|
18
39
|
|
|
19
|
-
|
|
20
|
-
// Check if it's a directory in the template
|
|
21
|
-
statSync(targetDir)
|
|
22
|
-
} catch {
|
|
23
|
-
mkdirSync(targetDir, { recursive: true })
|
|
24
|
-
}
|
|
40
|
+
mkdirSync(targetDir, { recursive: true })
|
|
25
41
|
|
|
26
42
|
if (statSync(filePath).isDirectory()) {
|
|
27
43
|
const folderPath = filePath.replace(templatePath, '')
|
|
@@ -34,23 +50,81 @@ export const generateTemplateSteps = (templateFolder: string): Generator => {
|
|
|
34
50
|
const generateFilePath = path.join(rootDir, sanitizedFileName)
|
|
35
51
|
let content = await fs.readFile(filePath, 'utf8')
|
|
36
52
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
53
|
+
if (isWorkbenchConfig) {
|
|
54
|
+
try {
|
|
55
|
+
// Use file descriptor to avoid TOCTOU vulnerability
|
|
56
|
+
let fd: fs.FileHandle | null = null
|
|
57
|
+
try {
|
|
58
|
+
// Try to open existing file for reading
|
|
59
|
+
fd = await fs.open(generateFilePath, constants.O_RDONLY)
|
|
60
|
+
const existingWorkbenchConfig = await fd.readFile('utf8')
|
|
61
|
+
const workbenchContent = JSON.parse(content)
|
|
42
62
|
|
|
43
|
-
|
|
63
|
+
content = JSON.stringify([...JSON.parse(existingWorkbenchConfig), ...workbenchContent], null, 2)
|
|
44
64
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
65
|
+
context.log('workbench-config-updated', (message) =>
|
|
66
|
+
message.tag('success').append('Workbench config').append('has been updated.'),
|
|
67
|
+
)
|
|
68
|
+
} finally {
|
|
69
|
+
if (fd) await fd.close()
|
|
70
|
+
}
|
|
71
|
+
} catch {
|
|
72
|
+
void 0
|
|
48
73
|
}
|
|
49
|
-
} catch {
|
|
50
|
-
void 0
|
|
51
74
|
}
|
|
52
75
|
|
|
53
|
-
|
|
76
|
+
// Use file descriptor for atomic write operation
|
|
77
|
+
let fd: fs.FileHandle | null = null
|
|
78
|
+
try {
|
|
79
|
+
fd = await fs.open(generateFilePath, constants.O_CREAT | constants.O_WRONLY | constants.O_TRUNC, 0o644)
|
|
80
|
+
await fd.writeFile(content, 'utf8')
|
|
81
|
+
} finally {
|
|
82
|
+
if (fd) await fd.close()
|
|
83
|
+
}
|
|
84
|
+
context.log(sanitizedFileName, (message) => {
|
|
85
|
+
message.tag('success').append('File').append(sanitizedFileName, 'cyan').append('has been created.')
|
|
86
|
+
})
|
|
87
|
+
}
|
|
88
|
+
} catch (error) {
|
|
89
|
+
console.error('Error generating template files:', error)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export const generatePluginTemplate = (templateFolder: string): Generator => {
|
|
95
|
+
return async (rootDir: string, context: CliContext): Promise<void> => {
|
|
96
|
+
const templatePath = path.join(__dirname, templateFolder)
|
|
97
|
+
const files = globSync('**/*', { absolute: false, cwd: templatePath, dot: true })
|
|
98
|
+
const projectName = path.basename(rootDir)
|
|
99
|
+
|
|
100
|
+
try {
|
|
101
|
+
for (const fileName of files) {
|
|
102
|
+
const filePath = path.join(templatePath, fileName)
|
|
103
|
+
const targetFilePath = path.join(rootDir, fileName)
|
|
104
|
+
const targetDir = path.dirname(targetFilePath)
|
|
105
|
+
|
|
106
|
+
mkdirSync(targetDir, { recursive: true })
|
|
107
|
+
|
|
108
|
+
if (statSync(filePath).isDirectory()) {
|
|
109
|
+
const folderPath = filePath.replace(templatePath, '')
|
|
110
|
+
mkdirSync(path.join(rootDir, folderPath), { recursive: true })
|
|
111
|
+
continue
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const sanitizedFileName = fileName.replace('.txt', '')
|
|
115
|
+
const generateFilePath = path.join(rootDir, sanitizedFileName)
|
|
116
|
+
let content = await fs.readFile(filePath, 'utf8')
|
|
117
|
+
|
|
118
|
+
content = replaceTemplateVariables(content, projectName)
|
|
119
|
+
|
|
120
|
+
// Use file descriptor for atomic write operation
|
|
121
|
+
let fd: fs.FileHandle | null = null
|
|
122
|
+
try {
|
|
123
|
+
fd = await fs.open(generateFilePath, constants.O_CREAT | constants.O_WRONLY | constants.O_TRUNC, 0o644)
|
|
124
|
+
await fd.writeFile(content, 'utf8')
|
|
125
|
+
} finally {
|
|
126
|
+
if (fd) await fd.close()
|
|
127
|
+
}
|
|
54
128
|
context.log(sanitizedFileName, (message) => {
|
|
55
129
|
message.tag('success').append('File').append(sanitizedFileName, 'cyan').append('has been created.')
|
|
56
130
|
})
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { type Generator, generateTemplateSteps } from './generate'
|
|
1
|
+
import { type Generator, generatePluginTemplate, generateTemplateSteps } from './generate'
|
|
2
2
|
|
|
3
3
|
export const templates: Record<string, Generator> = {
|
|
4
4
|
nodejs: generateTemplateSteps('nodejs'),
|
|
5
5
|
python: generateTemplateSteps('python'),
|
|
6
|
+
plugin: generatePluginTemplate('plugin'),
|
|
6
7
|
}
|
|
@@ -653,7 +653,7 @@ export const steps: TutorialStep[] = [
|
|
|
653
653
|
<br />
|
|
654
654
|
<br />
|
|
655
655
|
We recommend you give our{' '}
|
|
656
|
-
<a href="https://www.motia.dev/docs/concepts" target="_blank">
|
|
656
|
+
<a href="https://www.motia.dev/docs/concepts/overview" target="_blank">
|
|
657
657
|
core concepts
|
|
658
658
|
</a>{' '}
|
|
659
659
|
a read if you wish to learn further about Motia's fundamentals.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# {{PROJECT_NAME}}
|
|
2
|
+
|
|
3
|
+
A minimal plugin demonstrating the Motia plugin system.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This plugin serves as a reference implementation showing how to create custom workbench plugins for Motia. It demonstrates:
|
|
8
|
+
|
|
9
|
+
- Basic plugin structure and configuration
|
|
10
|
+
- Creating custom workbench tabs
|
|
11
|
+
- Using Motia's UI component library
|
|
12
|
+
- Building with Vite and TypeScript
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pnpm install
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Development
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
# Build the plugin
|
|
24
|
+
pnpm run build
|
|
25
|
+
|
|
26
|
+
# Watch mode for development
|
|
27
|
+
pnpm run dev
|
|
28
|
+
|
|
29
|
+
# Clean build artifacts
|
|
30
|
+
pnpm run clean
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Usage
|
|
34
|
+
|
|
35
|
+
To use this plugin in your Motia project, import it in your `motia.config.ts`:
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
import examplePlugin from '{{PROJECT_NAME}}/plugin'
|
|
39
|
+
|
|
40
|
+
export default {
|
|
41
|
+
plugins: [examplePlugin],
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Structure
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
{{PROJECT_NAME}}/
|
|
49
|
+
├── src/
|
|
50
|
+
│ ├── components/
|
|
51
|
+
│ │ └── example-page.tsx # Main UI component
|
|
52
|
+
│ ├── index.ts # Package entry point
|
|
53
|
+
│ ├── plugin.ts # Plugin definition
|
|
54
|
+
│ └── styles.css # Tailwind styles
|
|
55
|
+
├── package.json
|
|
56
|
+
├── tsconfig.json
|
|
57
|
+
├── vite.config.ts
|
|
58
|
+
└── README.md
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Learn More
|
|
62
|
+
|
|
63
|
+
For detailed documentation on creating plugins, see the [Motia Plugins Guide](https://motia.dev/docs/development-guide/plugins).
|
|
64
|
+
|