(null);
12 |
13 | useEffect(() => {
14 | window.addEventListener('message', event => {
15 | const message = event.data;
16 |
17 | switch (message.command) {
18 | case Command.ShowWizard:
19 | setPage(Page.Wizard);
20 | setPageData(message.data);
21 |
22 | break;
23 | }
24 | });
25 |
26 | vscode.postMessage({
27 | command: Command.Ready,
28 | });
29 | }, []);
30 |
31 | return (
32 |
33 | {page === Page.Wizard && !!pageData && }
34 |
35 | );
36 | };
37 |
38 | export default App;
39 |
--------------------------------------------------------------------------------
/src/webview/components/Wizard.tsx:
--------------------------------------------------------------------------------
1 | import { WebviewApi } from 'vscode-webview';
2 | import { Wizard as WizardType } from 'types/webview';
3 | import { Renderer } from './Wizard/Renderer';
4 |
5 | interface WizardProps {
6 | data: WizardType;
7 | vscode: WebviewApi;
8 | }
9 |
10 | const Wizard: React.FC = ({ data, vscode }) => {
11 | return (
12 |
13 |
{data.title}
14 |
{data.description}
15 |
16 |
17 |
18 | );
19 | };
20 |
21 | export default Wizard;
22 |
--------------------------------------------------------------------------------
/src/webview/components/Wizard/DynamicRowInput.tsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from 'react';
2 | import { WizardDynamicRowField, WizardField } from 'types/webview';
3 | import { FieldRenderer } from './FieldRenderer';
4 | import { FieldArray, useFormikContext } from 'formik';
5 |
6 | interface Props {
7 | field: WizardDynamicRowField;
8 | }
9 |
10 | export const DynamicRowInput: React.FC = ({ field }) => {
11 | const { values } = useFormikContext();
12 | const rows = values[field.id] ?? ([] as Record[]);
13 |
14 | return (
15 | {
18 | return (
19 | <>
20 | {/* @ts-ignore */}
21 |
22 |
23 | {field.fields.map(field => (
24 |
25 | {field.label}
26 |
27 | ))}
28 | Action
29 |
30 |
31 |
32 | {rows.map((row: any, index: number) => (
33 |
34 | {field.fields.map(childField => (
35 |
39 |
40 |
41 | ))}
42 |
43 | arrayHelpers.remove(index)}>
44 | Remove
45 |
46 |
47 |
48 | ))}
49 |
50 |
51 | arrayHelpers.push({})}>
52 | Add Row
53 |
54 | >
55 | );
56 | }}
57 | >
58 | );
59 | };
60 |
--------------------------------------------------------------------------------
/src/webview/components/Wizard/FieldErrorMessage.tsx:
--------------------------------------------------------------------------------
1 | import { Field, FormikProps, getIn } from 'formik';
2 |
3 | export const FieldErrorMessage: React.FC<{ name: string }> = ({ name }) => {
4 | return (
5 | }) => {
8 | const error = form.errors[name];
9 | const touch = getIn(form.touched, name);
10 |
11 | return touch && error ? error : null;
12 | }}
13 | />
14 | );
15 | };
16 |
--------------------------------------------------------------------------------
/src/webview/components/app.css:
--------------------------------------------------------------------------------
1 | .app {
2 | padding: 16px;
3 | }
4 |
5 | .dynamic-row-cell {
6 | vertical-align: top;
7 | padding-top: 8px;
8 | padding-bottom: 6px;
9 | }
10 |
11 | .dynamic-row-title {
12 | width: 100%;
13 | text-align: center;
14 | font-size: 14px;
15 | font-weight: 600;
16 | margin-bottom: 12px;
17 | }
18 |
19 | .dynamic-row-add-row {
20 | margin-top: 12px;
21 | }
22 |
23 | .tab-panel {
24 | padding: 16px;
25 | overflow: visible;
26 | }
27 |
--------------------------------------------------------------------------------
/src/webview/error/WizzardClosedError.ts:
--------------------------------------------------------------------------------
1 | export default class WizzardClosedError extends Error {}
2 |
--------------------------------------------------------------------------------
/src/webview/index.tsx:
--------------------------------------------------------------------------------
1 | import { createRoot } from 'react-dom/client';
2 | import App from './components/App';
3 |
4 | const root = createRoot(document.getElementById('root') as HTMLElement);
5 |
6 | root.render();
7 |
--------------------------------------------------------------------------------
/src/wizard/BlockWizard.ts:
--------------------------------------------------------------------------------
1 | import IndexManager from 'indexer/IndexManager';
2 | import ModuleIndexer from 'indexer/module/ModuleIndexer';
3 | import { GeneratorWizard } from 'webview/GeneratorWizard';
4 | import { WizardFieldBuilder } from 'webview/WizardFieldBuilder';
5 | import { WizardFormBuilder } from 'webview/WizardFormBuilder';
6 | import { WizardTabBuilder } from 'webview/WizardTabBuilder';
7 |
8 | export interface BlockWizardData {
9 | module: string;
10 | name: string;
11 | path: string;
12 | }
13 |
14 | export default class BlockWizard extends GeneratorWizard {
15 | public async show(contextModule?: string): Promise {
16 | const moduleIndexData = IndexManager.getIndexData(ModuleIndexer.KEY);
17 |
18 | if (!moduleIndexData) {
19 | throw new Error('Module index data not found');
20 | }
21 |
22 | const modules = moduleIndexData.getModuleOptions(m => m.location === 'app');
23 |
24 | const builder = new WizardFormBuilder();
25 |
26 | builder.setTitle('Generate a new block');
27 | builder.setDescription('Generates a new Magento2 block class.');
28 |
29 | const tab = new WizardTabBuilder();
30 | tab.setId('block');
31 | tab.setTitle('Block');
32 |
33 | tab.addField(
34 | WizardFieldBuilder.select('module', 'Module*')
35 | .setOptions(modules)
36 | .setInitialValue(contextModule || modules[0].value)
37 | .build()
38 | );
39 |
40 | tab.addField(
41 | WizardFieldBuilder.text('name', 'Block Name*').setPlaceholder('Block class name').build()
42 | );
43 |
44 | tab.addField(
45 | WizardFieldBuilder.text('path', 'Block Directory*')
46 | .setPlaceholder('Block/Path')
47 | .setInitialValue('Block')
48 | .build()
49 | );
50 |
51 | builder.addTab(tab.build());
52 |
53 | builder.addValidation('module', 'required');
54 | builder.addValidation('name', 'required|min:1');
55 | builder.addValidation('path', 'required|min:1');
56 |
57 | const data = await this.openWizard(builder.build());
58 |
59 | return data;
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/wizard/DataPatchWizard.ts:
--------------------------------------------------------------------------------
1 | import IndexManager from 'indexer/IndexManager';
2 | import ModuleIndexer from 'indexer/module/ModuleIndexer';
3 | import { GeneratorWizard } from 'webview/GeneratorWizard';
4 | import { WizardFieldBuilder } from 'webview/WizardFieldBuilder';
5 | import { WizardFormBuilder } from 'webview/WizardFormBuilder';
6 | import { WizardTabBuilder } from 'webview/WizardTabBuilder';
7 | import Validation from 'common/Validation';
8 |
9 | export interface DataPatchWizardData {
10 | module: string;
11 | className: string;
12 | revertable: boolean;
13 | }
14 |
15 | export default class DataPatchWizard extends GeneratorWizard {
16 | public async show(contextModule?: string): Promise {
17 | const moduleIndexData = IndexManager.getIndexData(ModuleIndexer.KEY);
18 |
19 | if (!moduleIndexData) {
20 | throw new Error('Module index data not found');
21 | }
22 |
23 | const modules = moduleIndexData.getModuleOptions(module => module.location === 'app');
24 |
25 | const builder = new WizardFormBuilder();
26 |
27 | builder.setTitle('Generate a new Data Patch');
28 | builder.setDescription('Generates a new Data Patch for a module.');
29 |
30 | const tab = new WizardTabBuilder();
31 | tab.setId('dataPatch');
32 | tab.setTitle('Data Patch');
33 |
34 | tab.addField(
35 | WizardFieldBuilder.select('module', 'Module')
36 | .setDescription(['Module where data patch will be generated in'])
37 | .setOptions(modules)
38 | .setInitialValue(contextModule || modules[0].value)
39 | .build()
40 | );
41 |
42 | tab.addField(
43 | WizardFieldBuilder.text('className', 'Class Name')
44 | .setDescription(['The class name for the data patch'])
45 | .setPlaceholder('YourPatchName')
46 | .build()
47 | );
48 |
49 | tab.addField(
50 | WizardFieldBuilder.checkbox('revertable', 'Revertable').setInitialValue(false).build()
51 | );
52 |
53 | builder.addTab(tab.build());
54 |
55 | builder.addValidation('module', 'required');
56 | builder.addValidation('className', [
57 | 'required',
58 | `regex:/${Validation.CLASS_NAME_REGEX.source}/`,
59 | ]);
60 |
61 | const data = await this.openWizard(builder.build());
62 |
63 | return data;
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/wizard/ViewModelWizard.ts:
--------------------------------------------------------------------------------
1 | import IndexManager from 'indexer/IndexManager';
2 | import ModuleIndexer from 'indexer/module/ModuleIndexer';
3 | import { GeneratorWizard } from 'webview/GeneratorWizard';
4 | import { WizardFieldBuilder } from 'webview/WizardFieldBuilder';
5 | import { WizardFormBuilder } from 'webview/WizardFormBuilder';
6 | import { WizardTabBuilder } from 'webview/WizardTabBuilder';
7 |
8 | export interface ViewModelWizardData {
9 | module: string;
10 | className: string;
11 | directory: string;
12 | }
13 |
14 | export default class ViewModelWizard extends GeneratorWizard {
15 | public async show(contextModule?: string): Promise {
16 | const moduleIndexData = IndexManager.getIndexData(ModuleIndexer.KEY);
17 |
18 | if (!moduleIndexData) {
19 | throw new Error('Module index data not found');
20 | }
21 |
22 | const modules = moduleIndexData.getModuleOptions(m => m.location === 'app');
23 |
24 | const builder = new WizardFormBuilder();
25 |
26 | builder.setTitle('Generate a new ViewModel');
27 | builder.setDescription('Generates a new Magento2 ViewModel class.');
28 |
29 | const tab = new WizardTabBuilder();
30 | tab.setId('viewModel');
31 | tab.setTitle('ViewModel');
32 |
33 | tab.addField(
34 | WizardFieldBuilder.select('module', 'Module*')
35 | .setOptions(modules)
36 | .setInitialValue(contextModule || modules[0].value)
37 | .build()
38 | );
39 |
40 | tab.addField(
41 | WizardFieldBuilder.text('className', 'Class Name*')
42 | .setPlaceholder('ViewModel class name')
43 | .build()
44 | );
45 |
46 | tab.addField(
47 | WizardFieldBuilder.text('directory', 'Directory*')
48 | .setPlaceholder('ViewModel/Path')
49 | .setInitialValue('ViewModel')
50 | .build()
51 | );
52 |
53 | builder.addTab(tab.build());
54 |
55 | builder.addValidation('module', 'required');
56 | builder.addValidation('className', 'required|min:1');
57 | builder.addValidation('directory', 'required|min:1');
58 |
59 | const data = await this.openWizard(builder.build());
60 |
61 | return data;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/templates/handlebars/graphql/blank-schema.hbs:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/magebitcom/magento-toolbox/434fe91e78eb3d128dc9669a2019ffd50baed463/templates/handlebars/graphql/blank-schema.hbs
--------------------------------------------------------------------------------
/templates/handlebars/license/mit.hbs:
--------------------------------------------------------------------------------
1 | Copyright © {{ year }}-present {{ copyright }}
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | SOFTWARE.
--------------------------------------------------------------------------------
/templates/handlebars/php/registration.hbs:
--------------------------------------------------------------------------------
1 | fileHeader}}
4 | declare(strict_types=1);
5 |
6 | use Magento\Framework\Component\ComponentRegistrar;
7 |
8 | ComponentRegistrar::register(ComponentRegistrar::MODULE, '{{ vendor }}_{{ module }}', __DIR__);
9 |
--------------------------------------------------------------------------------
/templates/handlebars/xml/blank-acl.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{> fileHeader}}
3 |
5 |
6 |
--------------------------------------------------------------------------------
/templates/handlebars/xml/blank-config.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{> fileHeader}}
3 |
5 |
6 |
--------------------------------------------------------------------------------
/templates/handlebars/xml/blank-crontab.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{> fileHeader}}
3 |
5 |
6 |
--------------------------------------------------------------------------------
/templates/handlebars/xml/blank-di.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{> fileHeader}}
3 |
5 |
6 |
--------------------------------------------------------------------------------
/templates/handlebars/xml/blank-email-templates.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{> fileHeader}}
3 |
5 |
6 |
--------------------------------------------------------------------------------
/templates/handlebars/xml/blank-events.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{> fileHeader}}
3 |
5 |
6 |
--------------------------------------------------------------------------------
/templates/handlebars/xml/blank-extension-attributes.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{> fileHeader}}
3 |
5 |
6 |
--------------------------------------------------------------------------------
/templates/handlebars/xml/blank-fieldset.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{> fileHeader}}
3 |
5 |
6 |
--------------------------------------------------------------------------------
/templates/handlebars/xml/blank-indexer.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{> fileHeader}}
3 |
5 |
6 |
--------------------------------------------------------------------------------
/templates/handlebars/xml/blank-layout.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{> fileHeader}}
3 |
5 |
6 |
--------------------------------------------------------------------------------
/templates/handlebars/xml/blank-mview.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{> fileHeader}}
3 |
5 |
6 |
--------------------------------------------------------------------------------
/templates/handlebars/xml/blank-page-types.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{> fileHeader}}
3 |
5 |
--------------------------------------------------------------------------------
/templates/handlebars/xml/blank-routes.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{> fileHeader}}
3 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/templates/handlebars/xml/blank-sections.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{> fileHeader}}
3 |
5 |
6 |
--------------------------------------------------------------------------------
/templates/handlebars/xml/blank-system.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{> fileHeader}}
3 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/templates/handlebars/xml/blank-view.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{> fileHeader}}
3 |
5 |
6 |
--------------------------------------------------------------------------------
/templates/handlebars/xml/blank-webapi.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{> fileHeader}}
3 |
5 |
6 |
--------------------------------------------------------------------------------
/templates/handlebars/xml/blank-widget.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{> fileHeader}}
3 |
5 |
6 |
--------------------------------------------------------------------------------
/templates/handlebars/xml/cron/group.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{> groupContent}}
3 |
--------------------------------------------------------------------------------
/templates/handlebars/xml/cron/job.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{cronSchedule}}
3 |
--------------------------------------------------------------------------------
/templates/handlebars/xml/di/plugin.hbs:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/templates/handlebars/xml/di/preference.hbs:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/templates/handlebars/xml/di/type.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{> typeContent }}
3 |
--------------------------------------------------------------------------------
/templates/handlebars/xml/events/event.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{> eventContent }}
3 |
--------------------------------------------------------------------------------
/templates/handlebars/xml/events/observer.hbs:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/templates/webview/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Magento Toolbox
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/test-resources/reference/generator/block/TestBlock.php:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 | * * * * *
8 |
9 |
10 |
11 | * * * * *
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/test-resources/reference/generator/cronJob/crontab.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 | * * * * *
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/test-resources/reference/generator/dataPatch/TestDataPatch.php:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/test-resources/reference/generator/module/module-with-sequence.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/test-resources/reference/generator/module/module.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/test-resources/reference/generator/module/registration-with-comment.php:
--------------------------------------------------------------------------------
1 | getEvent();
25 | // TODO: Observer code
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/test-resources/reference/generator/observer/TestObserverCustomPath.php:
--------------------------------------------------------------------------------
1 | getEvent();
25 | // TODO: Observer code
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/test-resources/reference/generator/observer/events-adminhtml.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/test-resources/reference/generator/observer/events-with-comment.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/test-resources/reference/generator/observer/events.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/test-resources/reference/generator/plugin/SubjectClass.php:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/test-resources/reference/generator/plugin/di.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/test-resources/reference/generator/preference/TestPreference.php:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/test-resources/reference/generator/viewModel/TestViewModel.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/test-resources/workspace/app/code/Foo/Bar/registration.php:
--------------------------------------------------------------------------------
1 |
2 |
8 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "ESNext",
4 | "moduleResolution": "bundler",
5 | "target": "ES2022",
6 | "lib": ["ES2022", "DOM"],
7 | "jsx": "react-jsx",
8 | "sourceMap": true,
9 | "rootDir": "src",
10 | "baseUrl": "src",
11 | "skipLibCheck": true,
12 | "emitDecoratorMetadata": true,
13 | "experimentalDecorators": true,
14 | "strict": true
15 | },
16 | "include": ["src"]
17 | }
18 |
--------------------------------------------------------------------------------