@superblocksteam/vite-plugin-file-sync 2.0.6-next.99 → 2.0.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/ai-service/app-interface/linter.d.ts +2 -7
- package/dist/ai-service/app-interface/linter.d.ts.map +1 -1
- package/dist/ai-service/app-interface/linter.js +41 -52
- package/dist/ai-service/app-interface/linter.js.map +1 -1
- package/dist/ai-service/app-interface/shell.d.ts +0 -2
- package/dist/ai-service/app-interface/shell.d.ts.map +1 -1
- package/dist/ai-service/app-interface/shell.js +4 -13
- package/dist/ai-service/app-interface/shell.js.map +1 -1
- package/dist/ai-service/const.d.ts +0 -2
- package/dist/ai-service/const.d.ts.map +1 -1
- package/dist/ai-service/const.js +0 -2
- package/dist/ai-service/const.js.map +1 -1
- package/dist/ai-service/eval/template-renderer.d.ts.map +1 -1
- package/dist/ai-service/eval/template-renderer.js +1 -1
- package/dist/ai-service/eval/template-renderer.js.map +1 -1
- package/dist/ai-service/index.d.ts.map +1 -1
- package/dist/ai-service/index.js +12 -4
- package/dist/ai-service/index.js.map +1 -1
- package/dist/ai-service/integrations/from-prompt-context.d.ts +1 -1
- package/dist/ai-service/integrations/from-prompt-context.d.ts.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbButtonPropsDocs.d.ts +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbButtonPropsDocs.d.ts.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbButtonPropsDocs.js +2 -2
- package/dist/ai-service/prompts/generated/library-components/SbButtonPropsDocs.js.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbCheckboxPropsDocs.d.ts +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbCheckboxPropsDocs.d.ts.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbCheckboxPropsDocs.js +2 -2
- package/dist/ai-service/prompts/generated/library-components/SbCheckboxPropsDocs.js.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbColumnPropsDocs.d.ts +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbColumnPropsDocs.d.ts.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbColumnPropsDocs.js +2 -2
- package/dist/ai-service/prompts/generated/library-components/SbColumnPropsDocs.js.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbContainerPropsDocs.d.ts +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbContainerPropsDocs.d.ts.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbContainerPropsDocs.js +2 -2
- package/dist/ai-service/prompts/generated/library-components/SbContainerPropsDocs.js.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbDatePickerPropsDocs.d.ts +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbDatePickerPropsDocs.d.ts.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbDatePickerPropsDocs.js +2 -2
- package/dist/ai-service/prompts/generated/library-components/SbDatePickerPropsDocs.js.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbDropdownPropsDocs.d.ts +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbDropdownPropsDocs.d.ts.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbDropdownPropsDocs.js +2 -2
- package/dist/ai-service/prompts/generated/library-components/SbDropdownPropsDocs.js.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbIconPropsDocs.d.ts +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbIconPropsDocs.d.ts.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbIconPropsDocs.js +2 -2
- package/dist/ai-service/prompts/generated/library-components/SbIconPropsDocs.js.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbImagePropsDocs.d.ts +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbImagePropsDocs.d.ts.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbImagePropsDocs.js +2 -2
- package/dist/ai-service/prompts/generated/library-components/SbImagePropsDocs.js.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbInputPropsDocs.d.ts +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbInputPropsDocs.d.ts.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbInputPropsDocs.js +2 -2
- package/dist/ai-service/prompts/generated/library-components/SbInputPropsDocs.js.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbModalPropsDocs.d.ts +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbModalPropsDocs.d.ts.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbModalPropsDocs.js +2 -2
- package/dist/ai-service/prompts/generated/library-components/SbModalPropsDocs.js.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbPagePropsDocs.d.ts +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbPagePropsDocs.d.ts.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbPagePropsDocs.js +2 -2
- package/dist/ai-service/prompts/generated/library-components/SbPagePropsDocs.js.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbSectionPropsDocs.d.ts +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbSectionPropsDocs.d.ts.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbSectionPropsDocs.js +2 -2
- package/dist/ai-service/prompts/generated/library-components/SbSectionPropsDocs.js.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbSlideoutPropsDocs.d.ts +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbSlideoutPropsDocs.d.ts.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbSlideoutPropsDocs.js +2 -2
- package/dist/ai-service/prompts/generated/library-components/SbSlideoutPropsDocs.js.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbSwitchPropsDocs.d.ts +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbSwitchPropsDocs.d.ts.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbSwitchPropsDocs.js +2 -2
- package/dist/ai-service/prompts/generated/library-components/SbSwitchPropsDocs.js.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbTablePropsDocs.d.ts +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbTablePropsDocs.d.ts.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbTablePropsDocs.js +2 -2
- package/dist/ai-service/prompts/generated/library-components/SbTablePropsDocs.js.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbTextPropsDocs.d.ts +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbTextPropsDocs.d.ts.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbTextPropsDocs.js +2 -2
- package/dist/ai-service/prompts/generated/library-components/SbTextPropsDocs.js.map +1 -1
- package/dist/ai-service/prompts/generated/library-typedefs/Dim.js +1 -1
- package/dist/ai-service/prompts/generated/library-typedefs/SbEventFlow.js +1 -1
- package/dist/ai-service/prompts/generated/library-typedefs/index.d.ts +0 -1
- package/dist/ai-service/prompts/generated/library-typedefs/index.d.ts.map +1 -1
- package/dist/ai-service/prompts/generated/library-typedefs/index.js +0 -1
- package/dist/ai-service/prompts/generated/library-typedefs/index.js.map +1 -1
- package/dist/ai-service/prompts/generated/subprompts/full-examples.js +1 -1
- package/dist/ai-service/prompts/generated/subprompts/superblocks-api.d.ts +1 -1
- package/dist/ai-service/prompts/generated/subprompts/superblocks-api.d.ts.map +1 -1
- package/dist/ai-service/prompts/generated/subprompts/superblocks-api.js +2 -2
- package/dist/ai-service/prompts/generated/subprompts/superblocks-api.js.map +1 -1
- package/dist/ai-service/prompts/generated/subprompts/superblocks-components-rules.js +1 -1
- package/dist/ai-service/prompts/generated/subprompts/superblocks-custom-components.js +1 -1
- package/dist/ai-service/prompts/generated/subprompts/superblocks-data-filtering.js +1 -1
- package/dist/ai-service/prompts/generated/subprompts/superblocks-event-flow.js +1 -1
- package/dist/ai-service/prompts/generated/subprompts/superblocks-forms.js +1 -1
- package/dist/ai-service/prompts/generated/subprompts/superblocks-layouts.js +1 -1
- package/dist/ai-service/prompts/generated/subprompts/superblocks-page.js +1 -1
- package/dist/ai-service/prompts/generated/subprompts/superblocks-rbac.js +1 -1
- package/dist/ai-service/prompts/generated/subprompts/superblocks-routes.js +1 -1
- package/dist/ai-service/prompts/generated/subprompts/superblocks-state.js +1 -1
- package/dist/ai-service/prompts/generated/subprompts/superblocks-theming.js +1 -1
- package/dist/ai-service/prompts/generated/subprompts/system.js +1 -1
- package/dist/ai-service/prompts/system.d.ts.map +1 -1
- package/dist/ai-service/prompts/system.js +0 -4
- package/dist/ai-service/prompts/system.js.map +1 -1
- package/dist/ai-service/state-machine/clark-fsm.d.ts +0 -2
- package/dist/ai-service/state-machine/clark-fsm.d.ts.map +1 -1
- package/dist/ai-service/state-machine/handlers/agent-planning.d.ts.map +1 -1
- package/dist/ai-service/state-machine/handlers/agent-planning.js +2 -13
- package/dist/ai-service/state-machine/handlers/agent-planning.js.map +1 -1
- package/dist/ai-service/state-machine/handlers/llm-generating.d.ts +1 -1
- package/dist/ai-service/state-machine/handlers/llm-generating.d.ts.map +1 -1
- package/dist/ai-service/state-machine/handlers/llm-generating.js +24 -75
- package/dist/ai-service/state-machine/handlers/llm-generating.js.map +1 -1
- package/dist/ai-service/state-machine/handlers/runtime-reviewing.d.ts.map +1 -1
- package/dist/ai-service/state-machine/handlers/runtime-reviewing.js +6 -18
- package/dist/ai-service/state-machine/handlers/runtime-reviewing.js.map +1 -1
- package/dist/ai-service/transform/add-metadata-to-api-yaml/transformer.d.ts.map +1 -1
- package/dist/ai-service/transform/add-metadata-to-api-yaml/transformer.js +2 -3
- package/dist/ai-service/transform/add-metadata-to-api-yaml/transformer.js.map +1 -1
- package/dist/ai-service/transform/shared.d.ts.map +1 -1
- package/dist/ai-service/transform/shared.js +8 -9
- package/dist/ai-service/transform/shared.js.map +1 -1
- package/dist/ai-service/types.d.ts +10 -23
- package/dist/ai-service/types.d.ts.map +1 -1
- package/dist/ai-service/types.js.map +1 -1
- package/dist/codegen.js +1 -1
- package/dist/codegen.js.map +1 -1
- package/dist/components-manager.d.ts +1 -4
- package/dist/components-manager.d.ts.map +1 -1
- package/dist/components-manager.js +4 -34
- package/dist/components-manager.js.map +1 -1
- package/dist/file-sync-vite-plugin.d.ts.map +1 -1
- package/dist/file-sync-vite-plugin.js +15 -43
- package/dist/file-sync-vite-plugin.js.map +1 -1
- package/dist/file-system-helpers.d.ts +0 -4
- package/dist/file-system-helpers.d.ts.map +1 -1
- package/dist/file-system-helpers.js +0 -10
- package/dist/file-system-helpers.js.map +1 -1
- package/dist/file-system-manager.d.ts +39 -52
- package/dist/file-system-manager.d.ts.map +1 -1
- package/dist/file-system-manager.js +532 -659
- package/dist/file-system-manager.js.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -1
- package/dist/index.js.map +1 -1
- package/dist/inject-index-vite-plugin.d.ts +2 -0
- package/dist/inject-index-vite-plugin.d.ts.map +1 -1
- package/dist/inject-index-vite-plugin.js +2 -2
- package/dist/inject-index-vite-plugin.js.map +1 -1
- package/dist/injected-index.d.ts +2 -2
- package/dist/injected-index.d.ts.map +1 -1
- package/dist/injected-index.js.map +1 -1
- package/dist/lock-service/index.d.ts.map +1 -1
- package/dist/lock-service/index.js +2 -13
- package/dist/lock-service/index.js.map +1 -1
- package/dist/parsing/computed/to-code-computed.d.ts.map +1 -1
- package/dist/parsing/computed/to-code-computed.js +3 -7
- package/dist/parsing/computed/to-code-computed.js.map +1 -1
- package/dist/parsing/entity/to-value-entity.js.map +1 -1
- package/dist/parsing/events/to-code-events.d.ts +1 -1
- package/dist/parsing/events/to-code-events.d.ts.map +1 -1
- package/dist/parsing/events/to-code-events.js +4 -15
- package/dist/parsing/events/to-code-events.js.map +1 -1
- package/dist/parsing/events/to-value-events.d.ts.map +1 -1
- package/dist/parsing/events/to-value-events.js +0 -47
- package/dist/parsing/events/to-value-events.js.map +1 -1
- package/dist/parsing/properties.js.map +1 -1
- package/dist/parsing/template/index.js +1 -1
- package/dist/parsing/template/index.js.map +1 -1
- package/dist/parsing/template/to-code-template.d.ts +1 -2
- package/dist/parsing/template/to-code-template.d.ts.map +1 -1
- package/dist/parsing/template/to-code-template.js +2 -2
- package/dist/parsing/template/to-code-template.js.map +1 -1
- package/dist/plugin-options.d.ts +2 -0
- package/dist/plugin-options.d.ts.map +1 -1
- package/dist/plugin-options.js.map +1 -1
- package/dist/refactor/blocks.d.ts.map +1 -1
- package/dist/refactor/blocks.js +69 -2
- package/dist/refactor/blocks.js.map +1 -1
- package/dist/refactor/javascript.d.ts +4 -0
- package/dist/refactor/javascript.d.ts.map +1 -1
- package/dist/refactor/javascript.js +8 -0
- package/dist/refactor/javascript.js.map +1 -1
- package/dist/rename-manager.d.ts +5 -0
- package/dist/rename-manager.d.ts.map +1 -1
- package/dist/rename-manager.js +27 -1
- package/dist/rename-manager.js.map +1 -1
- package/dist/routing.d.ts +2 -2
- package/dist/routing.d.ts.map +1 -1
- package/dist/routing.js +1 -21
- package/dist/routing.js.map +1 -1
- package/dist/sb-scope-manager.d.ts +1 -1
- package/dist/sb-scope-manager.d.ts.map +1 -1
- package/dist/sb-scope-manager.js +0 -10
- package/dist/sb-scope-manager.js.map +1 -1
- package/dist/socket-manager.d.ts +2 -2
- package/dist/socket-manager.d.ts.map +1 -1
- package/dist/socket-manager.js +5 -7
- package/dist/socket-manager.js.map +1 -1
- package/dist/source-tracker.d.ts +20 -20
- package/dist/source-tracker.d.ts.map +1 -1
- package/dist/source-tracker.js +17 -33
- package/dist/source-tracker.js.map +1 -1
- package/dist/sync-service/index.d.ts.map +1 -1
- package/dist/sync-service/index.js +1 -2
- package/dist/sync-service/index.js.map +1 -1
- package/dist/sync-service/list-dir.js +1 -1
- package/dist/sync-service/list-dir.js.map +1 -1
- package/dist/util/logger.d.ts +17 -13
- package/dist/util/logger.d.ts.map +1 -1
- package/dist/util/logger.js +44 -34
- package/dist/util/logger.js.map +1 -1
- package/dist/util/tracing.d.ts +4 -0
- package/dist/util/tracing.d.ts.map +1 -0
- package/dist/util/tracing.js +56 -0
- package/dist/util/tracing.js.map +1 -0
- package/dist/util.d.ts +0 -1
- package/dist/util.d.ts.map +1 -1
- package/dist/util.js +0 -8
- package/dist/util.js.map +1 -1
- package/package.json +6 -15
- package/dist/ai-service/prompts/generated/library-typedefs/TextStyleWithVariant.d.ts +0 -2
- package/dist/ai-service/prompts/generated/library-typedefs/TextStyleWithVariant.d.ts.map +0 -1
- package/dist/ai-service/prompts/generated/library-typedefs/TextStyleWithVariant.js +0 -6
- package/dist/ai-service/prompts/generated/library-typedefs/TextStyleWithVariant.js.map +0 -1
- package/dist/ai-service/state-machine/helpers/rate-limiting.d.ts +0 -6
- package/dist/ai-service/state-machine/helpers/rate-limiting.d.ts.map +0 -1
- package/dist/ai-service/state-machine/helpers/rate-limiting.js +0 -26
- package/dist/ai-service/state-machine/helpers/rate-limiting.js.map +0 -1
- package/dist/ai-service/test-utils/app-generation-mocks/orders-app.d.ts +0 -3
- package/dist/ai-service/test-utils/app-generation-mocks/orders-app.d.ts.map +0 -1
- package/dist/ai-service/test-utils/app-generation-mocks/orders-app.js +0 -851
- package/dist/ai-service/test-utils/app-generation-mocks/orders-app.js.map +0 -1
- package/dist/ai-service/test-utils/app-generation-mocks/smoketest.d.ts +0 -3
- package/dist/ai-service/test-utils/app-generation-mocks/smoketest.d.ts.map +0 -1
- package/dist/ai-service/test-utils/app-generation-mocks/smoketest.js +0 -111
- package/dist/ai-service/test-utils/app-generation-mocks/smoketest.js.map +0 -1
- package/dist/ai-service/test-utils/mock-utils.d.ts +0 -22
- package/dist/ai-service/test-utils/mock-utils.d.ts.map +0 -1
- package/dist/ai-service/test-utils/mock-utils.js +0 -46
- package/dist/ai-service/test-utils/mock-utils.js.map +0 -1
- package/dist/binding-extraction/index.d.ts +0 -2
- package/dist/binding-extraction/index.d.ts.map +0 -1
- package/dist/binding-extraction/index.js +0 -2
- package/dist/binding-extraction/index.js.map +0 -1
- package/dist/operations/operation-processor.d.ts +0 -24
- package/dist/operations/operation-processor.d.ts.map +0 -1
- package/dist/operations/operation-processor.js +0 -80
- package/dist/operations/operation-processor.js.map +0 -1
- package/dist/operations/types.d.ts +0 -8
- package/dist/operations/types.d.ts.map +0 -1
- package/dist/operations/types.js +0 -2
- package/dist/operations/types.js.map +0 -1
- package/dist/parsing/index.d.ts +0 -3
- package/dist/parsing/index.d.ts.map +0 -1
- package/dist/parsing/index.js +0 -3
- package/dist/parsing/index.js.map +0 -1
- package/dist/refactor/entities.d.ts +0 -6
- package/dist/refactor/entities.d.ts.map +0 -1
- package/dist/refactor/entities.js +0 -62
- package/dist/refactor/entities.js.map +0 -1
|
@@ -9,12 +9,10 @@ import { glob } from "glob";
|
|
|
9
9
|
import { isEqual } from "lodash-es";
|
|
10
10
|
import yaml from "yaml";
|
|
11
11
|
import { generateJSXAttribute } from "./codegen.js";
|
|
12
|
-
import { ComponentsManager } from "./components-manager.js";
|
|
13
12
|
import { addLegacyCustomComponentVariables, modifyLegacyCustomComponentElements, modifyLegacyCustomComponentImports, } from "./custom-components.js";
|
|
14
13
|
import { applyErrorHandling, } from "./errors/error-handler.js";
|
|
15
|
-
import {
|
|
14
|
+
import { getPageFolder, PAGES_DIRECTORY, ROUTES_FILE, SCOPE_FILE, } from "./file-system-helpers.js";
|
|
16
15
|
import { generate } from "./generate.js";
|
|
17
|
-
import { OperationProcessor } from "./operations/operation-processor.js";
|
|
18
16
|
import { doesElementHaveBinding } from "./parsing/bindings.js";
|
|
19
17
|
import { getSbElementId } from "./parsing/ids.js";
|
|
20
18
|
import { makeJSXAttribute } from "./parsing/jsx.js";
|
|
@@ -25,7 +23,6 @@ import { RenameManager } from "./rename-manager.js";
|
|
|
25
23
|
import { SourceTracker } from "./source-tracker.js";
|
|
26
24
|
import { traverse } from "./traverse.js";
|
|
27
25
|
import { getErrorMeta, getLogger } from "./util/logger.js";
|
|
28
|
-
import { getPageName } from "./util.js";
|
|
29
26
|
const SUPPORTED_FILETYPES = [
|
|
30
27
|
{
|
|
31
28
|
type: "tsx",
|
|
@@ -47,41 +44,26 @@ const SUPPORTED_FILETYPES = [
|
|
|
47
44
|
type: "js-api-step",
|
|
48
45
|
extension: ".js",
|
|
49
46
|
},
|
|
50
|
-
{
|
|
51
|
-
type: "json",
|
|
52
|
-
extension: ".json",
|
|
53
|
-
},
|
|
54
47
|
];
|
|
55
48
|
const APP_THEME_FILE_NAME = "appTheme.ts";
|
|
56
|
-
export class
|
|
49
|
+
export class FileSyncManager extends TracedEventEmitter {
|
|
57
50
|
rootDir;
|
|
58
51
|
tsFiles = {};
|
|
59
52
|
apiFiles = {};
|
|
60
53
|
sourceTracker;
|
|
61
54
|
fsOperationQueue;
|
|
62
|
-
operationProcessor;
|
|
63
55
|
generationNumberSequence;
|
|
64
56
|
routes = {};
|
|
65
|
-
routeChangesQueue = [];
|
|
66
57
|
watcher;
|
|
67
58
|
registeredComponentPaths = {};
|
|
68
59
|
renameManager = new RenameManager();
|
|
69
60
|
_tracer;
|
|
70
|
-
transactionNonce = Date.now();
|
|
71
|
-
pendingTransactions = new Set();
|
|
72
|
-
processedTransactions = [];
|
|
73
61
|
constructor(fsOperationQueue, generationNumberSequence, tracer) {
|
|
74
62
|
super(tracer, { captureRejections: true });
|
|
75
63
|
this.rootDir = "/";
|
|
76
64
|
this.fsOperationQueue = fsOperationQueue;
|
|
77
65
|
this.generationNumberSequence = generationNumberSequence;
|
|
78
66
|
this._tracer = tracer;
|
|
79
|
-
// intentionally a new queue here, we don't want to share the queue with the fsOperationQueue
|
|
80
|
-
this.operationProcessor = new OperationProcessor({
|
|
81
|
-
batchWindowMs: 50,
|
|
82
|
-
maxBatchSize: 100,
|
|
83
|
-
});
|
|
84
|
-
this.operationProcessor.disable();
|
|
85
67
|
applyErrorHandling(this, {
|
|
86
68
|
watch: { operation: "editing from code" },
|
|
87
69
|
handleCreatePage: { operation: "creating a page" },
|
|
@@ -127,7 +109,27 @@ export class FileSystemManager extends TracedEventEmitter {
|
|
|
127
109
|
}
|
|
128
110
|
return path.join(this.rootDir, "App.tsx");
|
|
129
111
|
}
|
|
130
|
-
|
|
112
|
+
updateApi = (content, path) => {
|
|
113
|
+
if (!this.rootDir) {
|
|
114
|
+
throw new Error("Root directory not set");
|
|
115
|
+
}
|
|
116
|
+
const { api: apiContents, stepPathMap } = content;
|
|
117
|
+
const pageName = getPageName(path);
|
|
118
|
+
let scopeId = this.sourceTracker?.getScopeDefinitionForPage(pageName)?.id;
|
|
119
|
+
if (!scopeId) {
|
|
120
|
+
console.warn("Scope ID not found for API", apiContents.metadata.name);
|
|
121
|
+
scopeId = "";
|
|
122
|
+
}
|
|
123
|
+
const updatedApi = {
|
|
124
|
+
apiPb: yaml.parse(JSON.stringify(apiContents)),
|
|
125
|
+
pageName,
|
|
126
|
+
stepPathMap,
|
|
127
|
+
scopeId,
|
|
128
|
+
};
|
|
129
|
+
const isNewApi = !this.apiFiles[path];
|
|
130
|
+
this.apiFiles[path] = updatedApi;
|
|
131
|
+
return { updatedApi, pageName, isNewApi };
|
|
132
|
+
};
|
|
131
133
|
async watch(watcher, rootPath) {
|
|
132
134
|
const logger = getLogger();
|
|
133
135
|
this.rootDir = rootPath;
|
|
@@ -213,10 +215,178 @@ export class FileSystemManager extends TracedEventEmitter {
|
|
|
213
215
|
return;
|
|
214
216
|
const { type, path } = file;
|
|
215
217
|
if (type === "api") {
|
|
216
|
-
this.
|
|
218
|
+
this.updateApi(content, path);
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
const handleFileChange = async (event, filePath) => {
|
|
222
|
+
logger.info(`File changed: ${filePath}, event: ${event}`);
|
|
223
|
+
switch (event) {
|
|
224
|
+
case "add": {
|
|
225
|
+
const fileType = SUPPORTED_FILETYPES.find((f) => filePath.endsWith(f.extension));
|
|
226
|
+
if (!fileType || !filePath.startsWith(rootPath)) {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
const data = await readFile(filePath);
|
|
230
|
+
if (typeof data !== "string")
|
|
231
|
+
return;
|
|
232
|
+
// if we match pages/*/index.tsx, we want to emit an addPage event
|
|
233
|
+
if (/pages\/.*\/index\.tsx/.test(filePath)) {
|
|
234
|
+
const file = await readFile(filePath);
|
|
235
|
+
if (!file) {
|
|
236
|
+
logger.error(`Failed to read file: ${filePath}`);
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
if (!(filePath in this.tsFiles)) {
|
|
240
|
+
this.tsFiles[filePath] = file;
|
|
241
|
+
this.handleNonVisualChangeByDeletingIds(filePath, file);
|
|
242
|
+
this.emit("addPage", filePath);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
switch (fileType.type) {
|
|
246
|
+
case "api":
|
|
247
|
+
case "python-api-step":
|
|
248
|
+
case "js-api-step": {
|
|
249
|
+
await this.processApiUpdates(filePath, fileType);
|
|
250
|
+
break;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
break;
|
|
254
|
+
}
|
|
255
|
+
case "change": {
|
|
256
|
+
// Special routes file handling
|
|
257
|
+
if (filePath === routePath) {
|
|
258
|
+
const data = await readFile(filePath);
|
|
259
|
+
try {
|
|
260
|
+
this.routes = JSON.parse(data);
|
|
261
|
+
this.emit("routesChanged", this.routes);
|
|
262
|
+
}
|
|
263
|
+
catch (e) {
|
|
264
|
+
logger.error("Error parsing routes file", getErrorMeta(e));
|
|
265
|
+
}
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
const fileType = SUPPORTED_FILETYPES.find((f) => filePath.endsWith(f.extension));
|
|
269
|
+
if (!fileType || !filePath.startsWith(rootPath)) {
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
const data = await readFile(filePath);
|
|
273
|
+
if (typeof data !== "string")
|
|
274
|
+
return;
|
|
275
|
+
switch (fileType.type) {
|
|
276
|
+
case "tsx":
|
|
277
|
+
case "scope":
|
|
278
|
+
{
|
|
279
|
+
if (!(filePath in this.tsFiles && this.tsFiles[filePath] === data)) {
|
|
280
|
+
logger.info(`File changed: ${filePath} updating AST tracker`);
|
|
281
|
+
this.tsFiles[filePath] = data;
|
|
282
|
+
// only update the source tracker if the file is different
|
|
283
|
+
this.handleNonVisualChangeByDeletingIds(filePath, data);
|
|
284
|
+
this.emit("fileChanged", filePath, data, true);
|
|
285
|
+
}
|
|
286
|
+
else {
|
|
287
|
+
logger.info(`File unchanged from last tracked state: ${filePath}`);
|
|
288
|
+
this.emit("fileChanged", filePath, data, false);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
break;
|
|
292
|
+
case "api":
|
|
293
|
+
case "python-api-step":
|
|
294
|
+
case "js-api-step":
|
|
295
|
+
{
|
|
296
|
+
await this.processApiUpdates(filePath, fileType);
|
|
297
|
+
}
|
|
298
|
+
break;
|
|
299
|
+
}
|
|
300
|
+
break;
|
|
301
|
+
}
|
|
302
|
+
case "unlink": {
|
|
303
|
+
if (filePath in this.apiFiles) {
|
|
304
|
+
const api = this.apiFiles[filePath];
|
|
305
|
+
delete this.apiFiles[filePath];
|
|
306
|
+
if (!api || !this.sourceTracker)
|
|
307
|
+
break;
|
|
308
|
+
const scopeId = await this.sourceTracker.deleteApi({
|
|
309
|
+
pageName: api.pageName,
|
|
310
|
+
apiName: api.apiPb.metadata.name,
|
|
311
|
+
});
|
|
312
|
+
const changes = (await this.sourceTracker?.getAndFlushChanges()) ?? [];
|
|
313
|
+
await this.writeChanges(changes);
|
|
314
|
+
this.emit("apiManualDelete", {
|
|
315
|
+
api: {
|
|
316
|
+
id: getClientApiId(api.apiPb.metadata.name, api.pageName),
|
|
317
|
+
apiName: api.apiPb.metadata.name,
|
|
318
|
+
// TODO(saksham): get pagename more defensively
|
|
319
|
+
pageName: getPageName(filePath),
|
|
320
|
+
scopeId,
|
|
321
|
+
},
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
if (filePath in this.tsFiles) {
|
|
325
|
+
delete this.tsFiles[filePath];
|
|
326
|
+
this.sourceTracker?.removeFile(filePath);
|
|
327
|
+
this.emit("deletePage", filePath);
|
|
328
|
+
}
|
|
329
|
+
break;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
};
|
|
333
|
+
watcher.on("all", async (event, filePath) => {
|
|
334
|
+
const fileType = SUPPORTED_FILETYPES.find((f) => filePath.endsWith(f.extension));
|
|
335
|
+
switch (fileType?.type) {
|
|
336
|
+
case "tsx":
|
|
337
|
+
case "scope":
|
|
338
|
+
// these can be awaited to ensure sequential execution
|
|
339
|
+
this.fsOperationQueue.enqueue(async () => {
|
|
340
|
+
return await handleFileChange(event, filePath);
|
|
341
|
+
});
|
|
342
|
+
break;
|
|
343
|
+
default:
|
|
344
|
+
// some files including APIs cannot be awaited currently
|
|
345
|
+
this.fsOperationQueue.enqueue(async () => {
|
|
346
|
+
void handleFileChange(event, filePath);
|
|
347
|
+
});
|
|
217
348
|
}
|
|
218
349
|
});
|
|
219
|
-
|
|
350
|
+
}
|
|
351
|
+
getApiFiles() {
|
|
352
|
+
return this.formatApisToClientApis(this.apiFiles);
|
|
353
|
+
}
|
|
354
|
+
getTsFilePaths() {
|
|
355
|
+
return Object.keys(this.tsFiles);
|
|
356
|
+
}
|
|
357
|
+
getSourceTracker() {
|
|
358
|
+
return this.sourceTracker;
|
|
359
|
+
}
|
|
360
|
+
async writeFile(path, content, kind) {
|
|
361
|
+
if (kind === "ts") {
|
|
362
|
+
// happens eagerly regardless of error - possible desync
|
|
363
|
+
this.tsFiles[path] = content;
|
|
364
|
+
// TODO(george): as an optimization, we could update the memory snapshot of the file that SyncService is holding
|
|
365
|
+
await this.fsOperationQueue.enqueue(async () => {
|
|
366
|
+
await fs.writeFile(path, content);
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
else if (kind === "api") {
|
|
370
|
+
const currentApiFile = this.apiFiles[path];
|
|
371
|
+
const apiPb = yaml.parse(content);
|
|
372
|
+
// Client APIs have id, but server APIs don't
|
|
373
|
+
if (apiPb.metadata.id) {
|
|
374
|
+
delete apiPb.metadata.id;
|
|
375
|
+
}
|
|
376
|
+
const stepPathMap = currentApiFile?.stepPathMap ?? {};
|
|
377
|
+
this.updateApi({ api: apiPb, stepPathMap }, path);
|
|
378
|
+
// TODO(george): as an optimization, we could update the memory snapshot of the file that SyncService is holding
|
|
379
|
+
await this.fsOperationQueue.enqueue(async () => {
|
|
380
|
+
await writeApiFiles({ apiPb }, "api", path.split("/").slice(0, -1).join("/"), false, [], [], { extractLargeSourceFiles: true, minLinesForExtraction: 1 }, new Set(Object.keys(this.apiFiles)), stepPathMap);
|
|
381
|
+
// stepPathMap will be generated after the write when the file doesn't exist
|
|
382
|
+
if (this.apiFiles[path]) {
|
|
383
|
+
this.apiFiles[path].stepPathMap = stepPathMap;
|
|
384
|
+
}
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
readFile(path) {
|
|
389
|
+
return this.tsFiles[path];
|
|
220
390
|
}
|
|
221
391
|
initializeSourceTracker() {
|
|
222
392
|
this.sourceTracker = new SourceTracker(this._tracer);
|
|
@@ -230,56 +400,49 @@ export class FileSystemManager extends TracedEventEmitter {
|
|
|
230
400
|
}
|
|
231
401
|
return this.sourceTracker.handleNonVisualChangeByDeletingIds(path, content);
|
|
232
402
|
}
|
|
233
|
-
async getFileWithIds(
|
|
403
|
+
async getFileWithIds(path) {
|
|
234
404
|
const logger = getLogger();
|
|
235
405
|
if (!this.sourceTracker) {
|
|
236
406
|
throw new Error("Source tracker not initialized");
|
|
237
407
|
}
|
|
238
408
|
const files = this.sourceTracker.getCurrentFiles();
|
|
239
|
-
if (!files[
|
|
240
|
-
throw new Error("File not found in source tracker " +
|
|
409
|
+
if (!files[path]) {
|
|
410
|
+
throw new Error("File not found in source tracker " + path);
|
|
241
411
|
}
|
|
242
|
-
const ast = files[
|
|
412
|
+
const ast = files[path].ast;
|
|
243
413
|
if (!ast) {
|
|
244
|
-
throw new Error("No AST found for file in source tracker " +
|
|
414
|
+
throw new Error("No AST found for file in source tracker " + path);
|
|
245
415
|
}
|
|
246
416
|
const clonedAst = await transformFromAstAsync(ast, undefined, {
|
|
247
417
|
ast: true,
|
|
248
418
|
});
|
|
249
419
|
if (!clonedAst?.ast) {
|
|
250
|
-
throw new Error("Failed to clone AST for file " +
|
|
420
|
+
throw new Error("Failed to clone AST for file " + path);
|
|
251
421
|
}
|
|
252
|
-
const componentsManager = ComponentsManager.getInstance();
|
|
253
422
|
const processedTransactions = this.getProcessedTransactionsWithNonce();
|
|
254
423
|
const customImports = new Set();
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
addLegacyCustomComponentVariables(nodePath, customImports);
|
|
260
|
-
}
|
|
261
|
-
// we want to inject the component name into the file so we can use it for hot reloading purposes
|
|
262
|
-
const name = componentsManager.getComponentName(filePath);
|
|
263
|
-
if (name) {
|
|
264
|
-
nodePath.pushContainer("body", t.variableDeclaration("const", [
|
|
265
|
-
t.variableDeclarator(t.identifier("componentName"), t.stringLiteral(name)),
|
|
266
|
-
]));
|
|
267
|
-
}
|
|
268
|
-
},
|
|
269
|
-
},
|
|
270
|
-
ImportDeclaration(path) {
|
|
271
|
-
if (isCustomBuildEnabled()) {
|
|
424
|
+
let customBuildTraversal = {};
|
|
425
|
+
if (isCustomBuildEnabled()) {
|
|
426
|
+
customBuildTraversal = {
|
|
427
|
+
ImportDeclaration(path) {
|
|
272
428
|
// when we see a custom component, we want to track the import and modify it to point
|
|
273
429
|
// to the built source
|
|
274
430
|
modifyLegacyCustomComponentImports(path, customImports);
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
JSXElement(path) {
|
|
278
|
-
if (isCustomBuildEnabled()) {
|
|
431
|
+
},
|
|
432
|
+
JSXElement(path) {
|
|
279
433
|
// based on the imports we see, we want to modify the JSXElement that uses the custom component
|
|
280
434
|
modifyLegacyCustomComponentElements(path, customImports);
|
|
281
|
-
}
|
|
282
|
-
|
|
435
|
+
},
|
|
436
|
+
Program: {
|
|
437
|
+
// we want to write the wrapped custom elements as local variables to the file
|
|
438
|
+
exit(path) {
|
|
439
|
+
addLegacyCustomComponentVariables(path, customImports);
|
|
440
|
+
},
|
|
441
|
+
},
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
traverse(clonedAst.ast, {
|
|
445
|
+
...customBuildTraversal,
|
|
283
446
|
ReturnStatement(path) {
|
|
284
447
|
const argument = path.get("argument");
|
|
285
448
|
if (!argument.isJSXElement()) {
|
|
@@ -325,246 +488,6 @@ export class FileSystemManager extends TracedEventEmitter {
|
|
|
325
488
|
return null;
|
|
326
489
|
}
|
|
327
490
|
}
|
|
328
|
-
enableOperationsQueue() {
|
|
329
|
-
this.operationProcessor.enable();
|
|
330
|
-
}
|
|
331
|
-
disableOperationsQueue() {
|
|
332
|
-
this.operationProcessor.disable();
|
|
333
|
-
}
|
|
334
|
-
async flushOperations() {
|
|
335
|
-
await this.operationProcessor.flush();
|
|
336
|
-
}
|
|
337
|
-
// MARK: file change handling
|
|
338
|
-
handleFileChange = async (event, filePath) => {
|
|
339
|
-
const logger = getLogger();
|
|
340
|
-
logger.info(`File changed: ${filePath}, event: ${event}`);
|
|
341
|
-
const rootPath = this.rootDir;
|
|
342
|
-
if (!rootPath) {
|
|
343
|
-
throw new Error("Root directory not set");
|
|
344
|
-
}
|
|
345
|
-
// Skip directory events
|
|
346
|
-
if (event === "addDir" || event === "unlinkDir") {
|
|
347
|
-
return;
|
|
348
|
-
}
|
|
349
|
-
const routePath = path.join(rootPath, ROUTES_FILE);
|
|
350
|
-
const fileType = SUPPORTED_FILETYPES.find((f) => filePath.endsWith(f.extension));
|
|
351
|
-
// Only handle files we care about and that are in our root path
|
|
352
|
-
if (!fileType || !filePath.startsWith(rootPath)) {
|
|
353
|
-
return;
|
|
354
|
-
}
|
|
355
|
-
// Queue the operation based on the event type
|
|
356
|
-
switch (event) {
|
|
357
|
-
case "add": {
|
|
358
|
-
const data = await readFile(filePath);
|
|
359
|
-
if (typeof data !== "string")
|
|
360
|
-
return;
|
|
361
|
-
const isPage = isPageFilePath(filePath);
|
|
362
|
-
if (isPage) {
|
|
363
|
-
void this.operationProcessor.addOperation({
|
|
364
|
-
metadata: {
|
|
365
|
-
filePath,
|
|
366
|
-
},
|
|
367
|
-
execute: async () => {
|
|
368
|
-
const file = await readFile(filePath);
|
|
369
|
-
if (!file) {
|
|
370
|
-
logger.error(`Failed to read file: ${filePath}`);
|
|
371
|
-
return;
|
|
372
|
-
}
|
|
373
|
-
if (!(filePath in this.tsFiles)) {
|
|
374
|
-
this.tsFiles[filePath] = file;
|
|
375
|
-
this.handleNonVisualChangeByDeletingIds(filePath, file);
|
|
376
|
-
this.emit("addPage", filePath);
|
|
377
|
-
}
|
|
378
|
-
},
|
|
379
|
-
});
|
|
380
|
-
}
|
|
381
|
-
else {
|
|
382
|
-
void this.operationProcessor.addOperation({
|
|
383
|
-
metadata: {
|
|
384
|
-
filePath,
|
|
385
|
-
},
|
|
386
|
-
execute: async () => {
|
|
387
|
-
switch (fileType.type) {
|
|
388
|
-
case "api":
|
|
389
|
-
case "python-api-step":
|
|
390
|
-
case "js-api-step": {
|
|
391
|
-
await this.processApiFileUpdates(filePath, fileType);
|
|
392
|
-
break;
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
},
|
|
396
|
-
});
|
|
397
|
-
}
|
|
398
|
-
break;
|
|
399
|
-
}
|
|
400
|
-
case "change": {
|
|
401
|
-
if (filePath === routePath) {
|
|
402
|
-
void this.operationProcessor.addOperation({
|
|
403
|
-
metadata: {
|
|
404
|
-
filePath,
|
|
405
|
-
},
|
|
406
|
-
priority: true,
|
|
407
|
-
execute: async () => {
|
|
408
|
-
try {
|
|
409
|
-
const data = JSON.parse((await readFile(filePath)) ?? "{}");
|
|
410
|
-
if (!isEqual(this.routes, data))
|
|
411
|
-
this.routes = data;
|
|
412
|
-
// this.addRoute assigns this.routes itself, causing this.routes === data
|
|
413
|
-
// but we still want to emit the event to HMR root.tsx
|
|
414
|
-
this.emit("routesChanged", this.routes);
|
|
415
|
-
}
|
|
416
|
-
catch (e) {
|
|
417
|
-
logger.error("Error parsing routes file", getErrorMeta(e));
|
|
418
|
-
}
|
|
419
|
-
},
|
|
420
|
-
});
|
|
421
|
-
return;
|
|
422
|
-
}
|
|
423
|
-
else {
|
|
424
|
-
void this.operationProcessor.addOperation({
|
|
425
|
-
metadata: {
|
|
426
|
-
filePath,
|
|
427
|
-
},
|
|
428
|
-
execute: async () => {
|
|
429
|
-
const fileType = SUPPORTED_FILETYPES.find((f) => filePath.endsWith(f.extension));
|
|
430
|
-
if (!fileType || !filePath.startsWith(rootPath)) {
|
|
431
|
-
return;
|
|
432
|
-
}
|
|
433
|
-
const data = await readFile(filePath);
|
|
434
|
-
if (typeof data !== "string")
|
|
435
|
-
return;
|
|
436
|
-
switch (fileType.type) {
|
|
437
|
-
case "tsx":
|
|
438
|
-
case "scope":
|
|
439
|
-
{
|
|
440
|
-
if (!(filePath in this.tsFiles &&
|
|
441
|
-
this.tsFiles[filePath] === data)) {
|
|
442
|
-
logger.info(`File changed: ${filePath} updating AST tracker`);
|
|
443
|
-
this.tsFiles[filePath] = data;
|
|
444
|
-
// only update the source tracker if the file is different
|
|
445
|
-
this.handleNonVisualChangeByDeletingIds(filePath, data);
|
|
446
|
-
this.emit("fileChanged", filePath, data, true);
|
|
447
|
-
}
|
|
448
|
-
else {
|
|
449
|
-
logger.info(`File unchanged from last tracked state: ${filePath}`);
|
|
450
|
-
this.emit("fileChanged", filePath, data, false);
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
break;
|
|
454
|
-
case "api":
|
|
455
|
-
case "python-api-step":
|
|
456
|
-
case "js-api-step":
|
|
457
|
-
{
|
|
458
|
-
await this.processApiFileUpdates(filePath, fileType);
|
|
459
|
-
}
|
|
460
|
-
break;
|
|
461
|
-
}
|
|
462
|
-
},
|
|
463
|
-
});
|
|
464
|
-
}
|
|
465
|
-
break;
|
|
466
|
-
}
|
|
467
|
-
case "unlink": {
|
|
468
|
-
if (filePath in this.tsFiles) {
|
|
469
|
-
void this.operationProcessor.addOperation({
|
|
470
|
-
metadata: {
|
|
471
|
-
filePath,
|
|
472
|
-
},
|
|
473
|
-
execute: async () => {
|
|
474
|
-
await this.deleteTsFile(filePath);
|
|
475
|
-
},
|
|
476
|
-
});
|
|
477
|
-
}
|
|
478
|
-
else if (filePath in this.apiFiles) {
|
|
479
|
-
void this.operationProcessor.addOperation({
|
|
480
|
-
metadata: {
|
|
481
|
-
filePath,
|
|
482
|
-
},
|
|
483
|
-
execute: async () => {
|
|
484
|
-
await this.removeApiData(filePath);
|
|
485
|
-
},
|
|
486
|
-
});
|
|
487
|
-
}
|
|
488
|
-
break;
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
};
|
|
492
|
-
async deleteTsFile(filePath) {
|
|
493
|
-
delete this.tsFiles[filePath];
|
|
494
|
-
this.sourceTracker?.removeFile(filePath);
|
|
495
|
-
if (isPageFilePath(filePath)) {
|
|
496
|
-
this.emit("deletePage", filePath);
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
getTsFilePaths() {
|
|
500
|
-
return Object.keys(this.tsFiles);
|
|
501
|
-
}
|
|
502
|
-
getSourceTracker() {
|
|
503
|
-
return this.sourceTracker;
|
|
504
|
-
}
|
|
505
|
-
// MARK: fs read/write
|
|
506
|
-
async writeFile(path, content, kind) {
|
|
507
|
-
switch (kind) {
|
|
508
|
-
case "ts": {
|
|
509
|
-
// happens eagerly regardless of error - possible desync
|
|
510
|
-
this.tsFiles[path] = content;
|
|
511
|
-
// TODO(george): as an optimization, we could update the memory snapshot of the file that SyncService is holding
|
|
512
|
-
await this.fsOperationQueue.enqueue(async () => {
|
|
513
|
-
await fs.writeFile(path, content);
|
|
514
|
-
});
|
|
515
|
-
break;
|
|
516
|
-
}
|
|
517
|
-
case "api": {
|
|
518
|
-
const currentApiFile = this.apiFiles[path];
|
|
519
|
-
const apiPb = yaml.parse(content);
|
|
520
|
-
// Client APIs have id, but server APIs don't
|
|
521
|
-
if (apiPb.metadata.id) {
|
|
522
|
-
delete apiPb.metadata.id;
|
|
523
|
-
}
|
|
524
|
-
const stepPathMap = currentApiFile?.stepPathMap ?? {};
|
|
525
|
-
this.updateInternalApiData({ api: apiPb, stepPathMap }, path);
|
|
526
|
-
// TODO(george): as an optimization, we could update the memory snapshot of the file that SyncService is holding
|
|
527
|
-
await this.fsOperationQueue.enqueue(async () => {
|
|
528
|
-
await writeApiFiles({ apiPb }, "api", path.split("/").slice(0, -1).join("/"), false, [], [], { extractLargeSourceFiles: true, minLinesForExtraction: 1 }, new Set(Object.keys(this.apiFiles)), stepPathMap);
|
|
529
|
-
// stepPathMap will be generated after the write when the file doesn't exist
|
|
530
|
-
if (this.apiFiles[path]) {
|
|
531
|
-
this.apiFiles[path].stepPathMap = stepPathMap;
|
|
532
|
-
}
|
|
533
|
-
});
|
|
534
|
-
break;
|
|
535
|
-
}
|
|
536
|
-
default: {
|
|
537
|
-
// TODO(george): as an optimization, we could update the memory snapshot of the file that SyncService is holding
|
|
538
|
-
await this.fsOperationQueue.enqueue(async () => {
|
|
539
|
-
await fs.writeFile(path, content);
|
|
540
|
-
});
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
readFile(path) {
|
|
545
|
-
return this.tsFiles[path];
|
|
546
|
-
}
|
|
547
|
-
async addRoute(route, filePath) {
|
|
548
|
-
if (!this.rootDir) {
|
|
549
|
-
throw new Error("Root directory not set");
|
|
550
|
-
}
|
|
551
|
-
this.routes[route] = { file: this.getRelativeRoutePath(filePath) };
|
|
552
|
-
await this.writeFile(path.join(this.rootDir, ROUTES_FILE), JSON.stringify(this.routes, null, 2));
|
|
553
|
-
}
|
|
554
|
-
async removeRoute(filePath) {
|
|
555
|
-
if (!this.rootDir) {
|
|
556
|
-
throw new Error("Root directory not set");
|
|
557
|
-
}
|
|
558
|
-
const relativeFilePath = this.getRelativeRoutePath(filePath);
|
|
559
|
-
this.routes = Object.fromEntries(Object.entries(this.routes).filter(([_, value]) => value.file !== relativeFilePath));
|
|
560
|
-
await this.writeFile(path.join(this.rootDir, ROUTES_FILE), JSON.stringify(this.routes, null, 2));
|
|
561
|
-
}
|
|
562
|
-
async writeChanges(changes, callback) {
|
|
563
|
-
return Promise.all(changes.map(async ({ fileName, source, kind }) => {
|
|
564
|
-
await this.writeFile(fileName, source, kind);
|
|
565
|
-
return callback?.(fileName, source);
|
|
566
|
-
}));
|
|
567
|
-
}
|
|
568
491
|
getLocalBindingEntities(path) {
|
|
569
492
|
const logger = getLogger();
|
|
570
493
|
const files = this.sourceTracker?.getCurrentFiles();
|
|
@@ -592,36 +515,8 @@ export class FileSystemManager extends TracedEventEmitter {
|
|
|
592
515
|
});
|
|
593
516
|
return Array.from(localBindingEntities);
|
|
594
517
|
}
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
const currentFile = this.sourceTracker?.getCurrentFiles()[scopeFilePath];
|
|
598
|
-
if (!currentFile) {
|
|
599
|
-
return null;
|
|
600
|
-
}
|
|
601
|
-
return getPageRoots(filePath, currentFile);
|
|
602
|
-
}
|
|
603
|
-
getScope(filePath) {
|
|
604
|
-
// get the scope.ts file in the same directory as path
|
|
605
|
-
const scopeFilePath = path.join(path.dirname(filePath), SCOPE_FILE);
|
|
606
|
-
const currentFile = this.sourceTracker?.getCurrentFiles()[scopeFilePath];
|
|
607
|
-
if (!currentFile) {
|
|
608
|
-
console.log("File not found", scopeFilePath);
|
|
609
|
-
return null;
|
|
610
|
-
}
|
|
611
|
-
const scope = getScope(scopeFilePath, currentFile);
|
|
612
|
-
return scope;
|
|
613
|
-
}
|
|
614
|
-
getRoutes() {
|
|
615
|
-
const routes = [];
|
|
616
|
-
for (const [path, { file }] of Object.entries(this.routes)) {
|
|
617
|
-
routes.push({
|
|
618
|
-
path,
|
|
619
|
-
component: file,
|
|
620
|
-
});
|
|
621
|
-
}
|
|
622
|
-
return routes;
|
|
623
|
-
}
|
|
624
|
-
// MARK: transaction handling
|
|
518
|
+
pendingTransactions = new Set();
|
|
519
|
+
processedTransactions = [];
|
|
625
520
|
flushTransactions = () => {
|
|
626
521
|
// TODO do something more sophisticated than this.
|
|
627
522
|
this.processedTransactions = this.processedTransactions.slice(-20);
|
|
@@ -634,92 +529,42 @@ export class FileSystemManager extends TracedEventEmitter {
|
|
|
634
529
|
}
|
|
635
530
|
this.pendingTransactions.add(transactionId);
|
|
636
531
|
};
|
|
532
|
+
transactionNonce = Date.now();
|
|
637
533
|
// Until we have a sophisticated way to track ALL operations, including non-UI initiated ops, we will use a nonce
|
|
638
534
|
// TODO https://github.com/superblocksteam/superblocks/pull/11788
|
|
639
535
|
getProcessedTransactionsWithNonce() {
|
|
640
536
|
const nonce = `t-${this.transactionNonce++}`;
|
|
641
537
|
return this.processedTransactions.concat(nonce);
|
|
642
538
|
}
|
|
643
|
-
// MARK: editor operations
|
|
644
539
|
handleCreatePage = async (payload) => {
|
|
645
|
-
|
|
646
|
-
const { name, route, navigateToRoute, routeTestParams } = payload;
|
|
540
|
+
const { name } = payload;
|
|
647
541
|
if (!this.rootDir) {
|
|
648
542
|
throw new Error("Root directory not set");
|
|
649
543
|
}
|
|
650
|
-
if (!route) {
|
|
651
|
-
throw new Error("Route is required when creating a page");
|
|
652
|
-
}
|
|
653
544
|
const pagePath = getPageFolder(this.rootDir, name);
|
|
654
545
|
const pageIndexPath = path.join(pagePath, "index.tsx");
|
|
655
|
-
const
|
|
656
|
-
const pageContent = /*js*/ `import {
|
|
657
|
-
SbPage,
|
|
658
|
-
Dim,
|
|
659
|
-
SbSection,
|
|
660
|
-
SbColumn,
|
|
661
|
-
registerPage,
|
|
662
|
-
} from "@superblocksteam/library";
|
|
663
|
-
import { ${name}, ${name}Scope } from "./scope";
|
|
664
|
-
|
|
546
|
+
const pageContent = /*js*/ `import { SbPage, SbContainer, registerPage } from "@superblocksteam/library";
|
|
665
547
|
function Page() {
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
<SbSection height={Dim.fill()}>
|
|
670
|
-
<SbColumn width={Dim.fill()}></SbColumn>
|
|
671
|
-
</SbSection>
|
|
672
|
-
</SbPage>
|
|
673
|
-
);
|
|
548
|
+
return <SbPage name="${name}">
|
|
549
|
+
<SbContainer width={Dim.fill()} />
|
|
550
|
+
</SbPage>;
|
|
674
551
|
}
|
|
675
552
|
|
|
676
|
-
export default registerPage(Page, ${name}
|
|
677
|
-
`;
|
|
678
|
-
const scopeContent = /*js*/ `import { createSbScope } from "@superblocksteam/library";
|
|
679
|
-
|
|
680
|
-
export const ${name}Scope = createSbScope<{}>(({ entities }) => ({}), {
|
|
681
|
-
name: "${name}",
|
|
682
|
-
});
|
|
683
|
-
|
|
684
|
-
export const ${name} = ${name}Scope.entities;
|
|
553
|
+
export default registerPage(Page, { name: "${name}" });
|
|
685
554
|
`;
|
|
686
|
-
if (navigateToRoute) {
|
|
687
|
-
this.routeChangesQueue.push({
|
|
688
|
-
route: route,
|
|
689
|
-
routeTestParams: routeTestParams,
|
|
690
|
-
});
|
|
691
|
-
}
|
|
692
|
-
this.watcher?.unwatch(pagePath);
|
|
693
555
|
await fs.mkdir(pagePath, { recursive: true });
|
|
694
556
|
await this.writeFile(pageIndexPath, pageContent, "ts");
|
|
695
|
-
await this.writeFile(scopePath, scopeContent, "ts");
|
|
696
557
|
await this.handleNonVisualChangeByDeletingIds(pageIndexPath, pageContent);
|
|
697
|
-
await this.handleNonVisualChangeByDeletingIds(scopePath, scopeContent);
|
|
698
|
-
this.watcher?.add(pagePath);
|
|
699
|
-
await this.addRoute(route, pageIndexPath);
|
|
700
558
|
this.emit("addPage", pageIndexPath);
|
|
701
559
|
};
|
|
702
|
-
handleDeletePage = async (payload) => {
|
|
703
|
-
const { name } = payload;
|
|
704
|
-
if (!this.rootDir) {
|
|
705
|
-
throw new Error("Root directory not set");
|
|
706
|
-
}
|
|
707
|
-
const pagePath = getPageFolder(this.rootDir, name);
|
|
708
|
-
await fs.rm(pagePath, { recursive: true, force: true });
|
|
709
|
-
await this.removeRoute(name);
|
|
710
|
-
this.emit("deletePage", pagePath);
|
|
711
|
-
};
|
|
712
|
-
get routeChange() {
|
|
713
|
-
return this.routeChangesQueue.shift();
|
|
714
|
-
}
|
|
715
560
|
handleReparent = async (payload, writeFile = true) => {
|
|
716
561
|
const { from, to, changedProps, transaction } = payload;
|
|
717
562
|
this.trackTransaction(transaction?.id);
|
|
718
|
-
this.sourceTracker?.setProperties({
|
|
563
|
+
await this.sourceTracker?.setProperties({
|
|
719
564
|
source: from.source,
|
|
720
565
|
changes: changedProps ?? {},
|
|
721
566
|
});
|
|
722
|
-
this.sourceTracker?.moveElement({
|
|
567
|
+
await this.sourceTracker?.moveElement({
|
|
723
568
|
from,
|
|
724
569
|
to,
|
|
725
570
|
});
|
|
@@ -733,7 +578,7 @@ export const ${name} = ${name}Scope.entities;
|
|
|
733
578
|
};
|
|
734
579
|
handleCreateComponent = async (payload, writeFile = true) => {
|
|
735
580
|
this.trackTransaction(payload.transaction?.id);
|
|
736
|
-
const sourceId = this.sourceTracker?.addElement(payload);
|
|
581
|
+
const sourceId = await this.sourceTracker?.addElement(payload);
|
|
737
582
|
if (writeFile) {
|
|
738
583
|
const changes = (await this.sourceTracker?.getAndFlushChanges()) ?? [];
|
|
739
584
|
await this.writeChanges(changes ?? [], (fileName) => {
|
|
@@ -747,7 +592,7 @@ export const ${name} = ${name}Scope.entities;
|
|
|
747
592
|
const { elements, transaction } = payload;
|
|
748
593
|
this.trackTransaction(transaction?.id);
|
|
749
594
|
for (const element of elements) {
|
|
750
|
-
this.sourceTracker?.deleteElement({
|
|
595
|
+
await this.sourceTracker?.deleteElement({
|
|
751
596
|
source: element.source,
|
|
752
597
|
scopeName: element.scopeName,
|
|
753
598
|
});
|
|
@@ -763,7 +608,7 @@ export const ${name} = ${name}Scope.entities;
|
|
|
763
608
|
handleSetProperty = async (payload, writeFile = true) => {
|
|
764
609
|
const { element: { source }, property, value, transaction, } = payload;
|
|
765
610
|
this.trackTransaction(transaction?.id);
|
|
766
|
-
this.sourceTracker?.setProperty({
|
|
611
|
+
await this.sourceTracker?.setProperty({
|
|
767
612
|
source,
|
|
768
613
|
property,
|
|
769
614
|
info: value,
|
|
@@ -778,7 +623,7 @@ export const ${name} = ${name}Scope.entities;
|
|
|
778
623
|
handleSetProperties = async (payload, writeFile = true) => {
|
|
779
624
|
const { element: { source }, properties, transaction, } = payload;
|
|
780
625
|
this.trackTransaction(transaction?.id);
|
|
781
|
-
this.sourceTracker?.setProperties({
|
|
626
|
+
await this.sourceTracker?.setProperties({
|
|
782
627
|
source,
|
|
783
628
|
changes: properties,
|
|
784
629
|
});
|
|
@@ -834,152 +679,11 @@ export const ${name} = ${name}Scope.entities;
|
|
|
834
679
|
this.flushTransactions();
|
|
835
680
|
return returnValues;
|
|
836
681
|
};
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
this.sourceTracker
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
type: payload.type,
|
|
843
|
-
name: payload.name,
|
|
844
|
-
attributes: payload.attributes,
|
|
845
|
-
},
|
|
846
|
-
});
|
|
847
|
-
const changes = (await this.sourceTracker?.getAndFlushChanges()) ?? [];
|
|
848
|
-
await this.writeChanges(changes, (fileName) => {
|
|
849
|
-
this.emit("addEntity", fileName, payload);
|
|
850
|
-
});
|
|
851
|
-
};
|
|
852
|
-
handleUpdateEntity = async (payload) => {
|
|
853
|
-
this.sourceTracker?.updateEntity({
|
|
854
|
-
entityId: payload.entityId,
|
|
855
|
-
updates: payload.updates,
|
|
856
|
-
});
|
|
857
|
-
const changes = (await this.sourceTracker?.getAndFlushChanges()) ?? [];
|
|
858
|
-
await this.writeChanges(changes, (fileName) => {
|
|
859
|
-
this.emit("updateEntity", fileName, payload);
|
|
860
|
-
});
|
|
861
|
-
};
|
|
862
|
-
handleDeleteEntity = async (payload) => {
|
|
863
|
-
const deletedEntityName = this.sourceTracker?.deleteEntity({
|
|
864
|
-
entityId: payload.entityId,
|
|
865
|
-
});
|
|
866
|
-
const changes = (await this.sourceTracker?.getAndFlushChanges()) ?? [];
|
|
867
|
-
await this.writeChanges(changes, (fileName) => {
|
|
868
|
-
if (deletedEntityName) {
|
|
869
|
-
this.emit("deleteEntity", fileName, deletedEntityName);
|
|
870
|
-
}
|
|
871
|
-
});
|
|
872
|
-
};
|
|
873
|
-
handleUpdateTheme = async (payload) => {
|
|
874
|
-
const { theme } = payload;
|
|
875
|
-
if (!this.rootDir) {
|
|
876
|
-
throw new Error("Root directory not set");
|
|
877
|
-
}
|
|
878
|
-
const filePath = path.join(this.rootDir, APP_THEME_FILE_NAME);
|
|
879
|
-
this.sourceTracker?.updateTheme({
|
|
880
|
-
themeFilePath: filePath,
|
|
881
|
-
theme,
|
|
882
|
-
});
|
|
883
|
-
const changes = (await this.sourceTracker?.getAndFlushChanges()) ?? [];
|
|
884
|
-
await this.writeChanges(changes);
|
|
885
|
-
};
|
|
886
|
-
// MARK: rename operations
|
|
887
|
-
handleRenameElement = async (payload) => {
|
|
888
|
-
if (payload.kind === "component") {
|
|
889
|
-
return this.handleRenameComponent(payload);
|
|
890
|
-
}
|
|
891
|
-
else if (payload.kind === "entity") {
|
|
892
|
-
return this.handleRenameEntity(payload);
|
|
893
|
-
}
|
|
894
|
-
else if (payload.kind === "page") {
|
|
895
|
-
return this.handleRenamePage(payload);
|
|
896
|
-
}
|
|
897
|
-
};
|
|
898
|
-
handleRenameComponent = async (payload) => {
|
|
899
|
-
const { elementId, newName, oldName, scopeName } = payload;
|
|
900
|
-
await this.sourceTracker?.renameComponent({
|
|
901
|
-
widgetSourceId: elementId,
|
|
902
|
-
oldName,
|
|
903
|
-
newName,
|
|
904
|
-
scopeName,
|
|
905
|
-
});
|
|
906
|
-
await this.renameIdentifierInApis({
|
|
907
|
-
elementId,
|
|
908
|
-
oldName,
|
|
909
|
-
newName,
|
|
910
|
-
});
|
|
911
|
-
const changes = (await this.sourceTracker?.getAndFlushChanges()) ?? [];
|
|
912
|
-
await this.writeChanges(changes, (fileName) => {
|
|
913
|
-
this.emit("renameComponent", fileName);
|
|
914
|
-
});
|
|
915
|
-
};
|
|
916
|
-
handleRenameEntity = async (payload) => {
|
|
917
|
-
const { elementId, newName, oldName, scopeName } = payload;
|
|
918
|
-
this.sourceTracker?.renameEntity({
|
|
919
|
-
entityId: elementId,
|
|
920
|
-
oldName,
|
|
921
|
-
newName,
|
|
922
|
-
scopeName,
|
|
923
|
-
});
|
|
924
|
-
await this.renameIdentifierInApis(payload);
|
|
925
|
-
const changes = (await this.sourceTracker?.getAndFlushChanges()) ?? [];
|
|
926
|
-
await this.writeChanges(changes, (fileName) => {
|
|
927
|
-
this.emit("renameEntity", fileName);
|
|
928
|
-
});
|
|
929
|
-
};
|
|
930
|
-
handleRenamePage = async (payload) => {
|
|
931
|
-
const { newName, oldName } = payload;
|
|
932
|
-
if (!this.rootDir) {
|
|
933
|
-
throw new Error("Root directory not set");
|
|
934
|
-
}
|
|
935
|
-
const newPageFolder = getPageFolder(this.rootDir, newName);
|
|
936
|
-
const newIndexFilePath = path.join(newPageFolder, "index.tsx");
|
|
937
|
-
const oldIndexFilePath = path.join(this.rootDir, "pages", oldName, "index.tsx");
|
|
938
|
-
const oldPageFolder = getPageFolder(this.rootDir, oldName);
|
|
939
|
-
this.watcher?.unwatch(newPageFolder);
|
|
940
|
-
this.watcher?.unwatch(oldPageFolder);
|
|
941
|
-
const existingRoute = Object.keys(this.routes).find((route) => this.routes[route]?.file ===
|
|
942
|
-
this.getRelativeRoutePath(oldIndexFilePath));
|
|
943
|
-
if (!existingRoute) {
|
|
944
|
-
throw new Error(`Route for ${oldName} not found`);
|
|
945
|
-
}
|
|
946
|
-
// Write the name attribute to the page file
|
|
947
|
-
await this.sourceTracker?.renamePage({
|
|
948
|
-
newName,
|
|
949
|
-
filePath: oldIndexFilePath,
|
|
950
|
-
});
|
|
951
|
-
const changes = (await this.sourceTracker?.getAndFlushChanges()) ?? [];
|
|
952
|
-
await this.writeChanges(changes);
|
|
953
|
-
// A rename of a folder is an unlink followed by an add, so the file watcher will take over initializing this
|
|
954
|
-
// "new" page
|
|
955
|
-
await fs.rename(oldPageFolder, newPageFolder);
|
|
956
|
-
// Now we just clean up the routes
|
|
957
|
-
await this.removeRoute(oldIndexFilePath);
|
|
958
|
-
await this.addRoute(existingRoute, newIndexFilePath);
|
|
959
|
-
const newIndexFile = await readFile(newIndexFilePath);
|
|
960
|
-
if (!newIndexFile) {
|
|
961
|
-
throw new Error(`New index file ${newIndexFilePath} not found`);
|
|
962
|
-
}
|
|
963
|
-
this.tsFiles[newIndexFilePath] = newIndexFile;
|
|
964
|
-
await this.handleNonVisualChangeByDeletingIds(newIndexFilePath, newIndexFile);
|
|
965
|
-
this.emit("renamePage", newIndexFilePath);
|
|
966
|
-
// Re-add the watcher
|
|
967
|
-
this.watcher?.add(newPageFolder);
|
|
968
|
-
this.watcher?.add(oldPageFolder);
|
|
969
|
-
};
|
|
970
|
-
getRelativeRoutePath(filePath) {
|
|
971
|
-
if (!this.rootDir) {
|
|
972
|
-
throw new Error("Root directory not set");
|
|
973
|
-
}
|
|
974
|
-
// no leading slash
|
|
975
|
-
return path.relative(path.join(this.rootDir, PAGES_DIRECTORY), filePath);
|
|
976
|
-
}
|
|
977
|
-
// MARK: API operations
|
|
978
|
-
handleUpdateApi = async (payload) => {
|
|
979
|
-
const { api } = payload;
|
|
980
|
-
if (!this.sourceTracker) {
|
|
981
|
-
throw new Error("Source tracker not initialized");
|
|
982
|
-
}
|
|
682
|
+
handleUpdateApi = async (payload) => {
|
|
683
|
+
const { api } = payload;
|
|
684
|
+
if (!this.sourceTracker) {
|
|
685
|
+
throw new Error("Source tracker not initialized");
|
|
686
|
+
}
|
|
983
687
|
if (!this.rootDir) {
|
|
984
688
|
throw new Error("Root directory not set");
|
|
985
689
|
}
|
|
@@ -990,24 +694,24 @@ export const ${name} = ${name}Scope.entities;
|
|
|
990
694
|
if (!apiName) {
|
|
991
695
|
throw new Error("API name is not set");
|
|
992
696
|
}
|
|
993
|
-
const
|
|
994
|
-
const
|
|
995
|
-
const isNewApi = !this.getApiFiles()[
|
|
697
|
+
const apiDir = path.join(this.rootDir, "pages", api.pageName, "apis", apiName);
|
|
698
|
+
const apiPath = path.join(apiDir, "api.yaml");
|
|
699
|
+
const isNewApi = !this.getApiFiles()[apiPath];
|
|
996
700
|
try {
|
|
997
701
|
const stats = await fs.stat(apiDir);
|
|
998
702
|
if (!stats.isDirectory()) {
|
|
999
|
-
await
|
|
703
|
+
await fs.mkdir(apiDir, { recursive: true });
|
|
1000
704
|
}
|
|
1001
705
|
}
|
|
1002
706
|
catch {
|
|
1003
|
-
await
|
|
707
|
+
await fs.mkdir(apiDir, { recursive: true });
|
|
1004
708
|
}
|
|
1005
|
-
await this.writeFile(
|
|
709
|
+
await this.writeFile(apiPath, yaml.stringify(api.apiPb), "api");
|
|
1006
710
|
const generationNumber = this.generationNumberSequence.next();
|
|
1007
711
|
const apiDef = this.createClientApi(api);
|
|
1008
712
|
let scopeId = "";
|
|
1009
713
|
if (isNewApi) {
|
|
1010
|
-
scopeId = await this.
|
|
714
|
+
scopeId = await this.createScopedApi(api);
|
|
1011
715
|
}
|
|
1012
716
|
else {
|
|
1013
717
|
const scopeDef = this.sourceTracker.getScopeDefinitionForPage(api.pageName);
|
|
@@ -1016,6 +720,20 @@ export const ${name} = ${name}Scope.entities;
|
|
|
1016
720
|
this.emit("apiUpdate", { api: apiDef, scopeId });
|
|
1017
721
|
return { api: apiDef, scopeId, generationNumber };
|
|
1018
722
|
};
|
|
723
|
+
createScopedApi = async (api) => {
|
|
724
|
+
if (!this.sourceTracker) {
|
|
725
|
+
throw new Error("Source tracker not initialized");
|
|
726
|
+
}
|
|
727
|
+
// We want to add the API entity to our scope, but we do not want to emit entity events, because
|
|
728
|
+
// the API update event handles this particular side effect.
|
|
729
|
+
const scopeId = await this.sourceTracker.addApi({
|
|
730
|
+
pageName: api.pageName,
|
|
731
|
+
apiName: api.apiPb.metadata.name,
|
|
732
|
+
});
|
|
733
|
+
const changes = (await this.sourceTracker?.getAndFlushChanges()) ?? [];
|
|
734
|
+
await this.writeChanges(changes);
|
|
735
|
+
return scopeId;
|
|
736
|
+
};
|
|
1019
737
|
handleDeleteApi = async (payload) => {
|
|
1020
738
|
const logger = getLogger();
|
|
1021
739
|
const { apis } = payload;
|
|
@@ -1023,34 +741,40 @@ export const ${name} = ${name}Scope.entities;
|
|
|
1023
741
|
throw new Error("Root directory not set");
|
|
1024
742
|
}
|
|
1025
743
|
const rootDir = this.rootDir;
|
|
1026
|
-
const
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
try {
|
|
1035
|
-
const stats = await fs.stat(apiDir);
|
|
1036
|
-
if (stats.isDirectory()) {
|
|
1037
|
-
await fs.rmdir(apiDir, { recursive: true });
|
|
744
|
+
const executeDeleteApis = Promise.all(apis.map(({ apiName, pageName }) => {
|
|
745
|
+
return new Promise(
|
|
746
|
+
// eslint-disable-next-line no-async-promise-executor
|
|
747
|
+
async (resolve) => {
|
|
748
|
+
const apiFilePath = path.join(rootDir, "pages", pageName, "apis", apiName, "api.yaml");
|
|
749
|
+
const api = this.apiFiles[apiFilePath];
|
|
750
|
+
if (!api || !this.sourceTracker) {
|
|
751
|
+
return resolve(undefined);
|
|
1038
752
|
}
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
753
|
+
const apiDir = path.join(rootDir, "pages", pageName, "apis", apiName);
|
|
754
|
+
try {
|
|
755
|
+
const stats = await fs.stat(apiDir);
|
|
756
|
+
if (stats.isDirectory()) {
|
|
757
|
+
await fs.rmdir(apiDir, { recursive: true });
|
|
758
|
+
}
|
|
759
|
+
delete this.apiFiles[apiFilePath];
|
|
760
|
+
const scopeId = await this.sourceTracker.deleteApi({
|
|
761
|
+
pageName,
|
|
762
|
+
apiName,
|
|
763
|
+
});
|
|
764
|
+
resolve({
|
|
765
|
+
apiName,
|
|
766
|
+
pageName,
|
|
767
|
+
scopeId,
|
|
768
|
+
});
|
|
769
|
+
}
|
|
770
|
+
catch (e) {
|
|
771
|
+
logger.warn(`Could not delete api ${apiFilePath} ${JSON.stringify(e)}`);
|
|
772
|
+
resolve(undefined);
|
|
773
|
+
}
|
|
774
|
+
resolve(undefined);
|
|
775
|
+
});
|
|
776
|
+
}));
|
|
777
|
+
const deletedApis = (await executeDeleteApis).filter((api) => api !== undefined);
|
|
1054
778
|
const changes = (await this.sourceTracker?.getAndFlushChanges()) ?? [];
|
|
1055
779
|
await this.writeChanges(changes);
|
|
1056
780
|
this.emit("apiDelete", {
|
|
@@ -1131,40 +855,229 @@ export const ${name} = ${name}Scope.entities;
|
|
|
1131
855
|
// TODO: Should I delete here?
|
|
1132
856
|
}));
|
|
1133
857
|
};
|
|
1134
|
-
async
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
858
|
+
handleAddEntity = async (payload) => {
|
|
859
|
+
await this.sourceTracker?.addEntity({
|
|
860
|
+
scopeId: payload.scopeId,
|
|
861
|
+
entity: {
|
|
862
|
+
type: payload.type,
|
|
863
|
+
name: payload.name,
|
|
864
|
+
attributes: payload.attributes,
|
|
865
|
+
},
|
|
866
|
+
});
|
|
867
|
+
const changes = (await this.sourceTracker?.getAndFlushChanges()) ?? [];
|
|
868
|
+
await this.writeChanges(changes, (fileName) => {
|
|
869
|
+
this.emit("addEntity", fileName, payload);
|
|
870
|
+
});
|
|
871
|
+
};
|
|
872
|
+
handleUpdateEntity = async (payload) => {
|
|
873
|
+
await this.sourceTracker?.updateEntity({
|
|
874
|
+
entityId: payload.entityId,
|
|
875
|
+
updates: payload.updates,
|
|
876
|
+
});
|
|
877
|
+
const changes = (await this.sourceTracker?.getAndFlushChanges()) ?? [];
|
|
878
|
+
await this.writeChanges(changes, (fileName) => {
|
|
879
|
+
this.emit("updateEntity", fileName, payload);
|
|
880
|
+
});
|
|
881
|
+
};
|
|
882
|
+
handleDeleteEntity = async (payload) => {
|
|
883
|
+
const deletedEntityName = await this.sourceTracker?.deleteEntity({
|
|
884
|
+
entityId: payload.entityId,
|
|
885
|
+
});
|
|
886
|
+
const changes = (await this.sourceTracker?.getAndFlushChanges()) ?? [];
|
|
887
|
+
await this.writeChanges(changes, (fileName) => {
|
|
888
|
+
if (deletedEntityName) {
|
|
889
|
+
this.emit("deleteEntity", fileName, deletedEntityName);
|
|
890
|
+
}
|
|
891
|
+
});
|
|
892
|
+
};
|
|
893
|
+
handleUpdateTheme = async (payload) => {
|
|
894
|
+
const { theme } = payload;
|
|
895
|
+
if (!this.rootDir) {
|
|
896
|
+
throw new Error("Root directory not set");
|
|
1138
897
|
}
|
|
1139
|
-
|
|
1140
|
-
this.sourceTracker?.
|
|
1141
|
-
|
|
1142
|
-
|
|
898
|
+
const filePath = path.join(this.rootDir, APP_THEME_FILE_NAME);
|
|
899
|
+
await this.sourceTracker?.updateTheme({
|
|
900
|
+
themeFilePath: filePath,
|
|
901
|
+
theme,
|
|
1143
902
|
});
|
|
1144
903
|
const changes = (await this.sourceTracker?.getAndFlushChanges()) ?? [];
|
|
1145
904
|
await this.writeChanges(changes);
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
905
|
+
};
|
|
906
|
+
handleRenameElement = async (payload) => {
|
|
907
|
+
if (payload.kind === "component") {
|
|
908
|
+
return this.handleRenameComponent(payload);
|
|
909
|
+
}
|
|
910
|
+
else if (payload.kind === "entity") {
|
|
911
|
+
return this.handleRenameEntity(payload);
|
|
912
|
+
}
|
|
913
|
+
else if (payload.kind === "page") {
|
|
914
|
+
return this.handleRenamePage(payload);
|
|
915
|
+
}
|
|
916
|
+
};
|
|
917
|
+
handleRenameComponent = async (payload) => {
|
|
918
|
+
const { elementId, newName, oldName, scopeName } = payload;
|
|
919
|
+
await this.sourceTracker?.renameComponent({
|
|
920
|
+
widgetSourceId: elementId,
|
|
921
|
+
oldName,
|
|
922
|
+
newName,
|
|
923
|
+
scopeName,
|
|
1155
924
|
});
|
|
925
|
+
await this.renameIdentifierInApis({
|
|
926
|
+
elementId,
|
|
927
|
+
oldName,
|
|
928
|
+
newName,
|
|
929
|
+
});
|
|
930
|
+
const changes = (await this.sourceTracker?.getAndFlushChanges()) ?? [];
|
|
931
|
+
await this.writeChanges(changes, (fileName) => {
|
|
932
|
+
this.emit("renameComponent", fileName);
|
|
933
|
+
});
|
|
934
|
+
};
|
|
935
|
+
handleRenameEntity = async (payload) => {
|
|
936
|
+
const { elementId, newName, oldName, scopeName } = payload;
|
|
937
|
+
await this.sourceTracker?.renameEntity({
|
|
938
|
+
entityId: elementId,
|
|
939
|
+
oldName,
|
|
940
|
+
newName,
|
|
941
|
+
scopeName,
|
|
942
|
+
});
|
|
943
|
+
await this.renameIdentifierInApis(payload);
|
|
944
|
+
const changes = (await this.sourceTracker?.getAndFlushChanges()) ?? [];
|
|
945
|
+
await this.writeChanges(changes, (fileName) => {
|
|
946
|
+
this.emit("renameEntity", fileName);
|
|
947
|
+
});
|
|
948
|
+
};
|
|
949
|
+
handleRenamePage = async (payload) => {
|
|
950
|
+
const { newName, oldName } = payload;
|
|
951
|
+
if (!this.rootDir) {
|
|
952
|
+
throw new Error("Root directory not set");
|
|
953
|
+
}
|
|
954
|
+
const newPageFolder = getPageFolder(this.rootDir, newName);
|
|
955
|
+
const newIndexFilePath = path.join(newPageFolder, "index.tsx");
|
|
956
|
+
const oldIndexFilePath = path.join(this.rootDir, "pages", oldName, "index.tsx");
|
|
957
|
+
const oldPageFolder = getPageFolder(this.rootDir, oldName);
|
|
958
|
+
this.watcher?.unwatch(newPageFolder);
|
|
959
|
+
this.watcher?.unwatch(oldPageFolder);
|
|
960
|
+
const existingRoute = Object.keys(this.routes).find((route) => this.routes[route]?.file ===
|
|
961
|
+
this.getRelativeRoutePath(oldIndexFilePath));
|
|
962
|
+
if (!existingRoute) {
|
|
963
|
+
throw new Error(`Route for ${oldName} not found`);
|
|
964
|
+
}
|
|
965
|
+
// Write the name attribute to the page file
|
|
966
|
+
await this.sourceTracker?.renamePage({
|
|
967
|
+
newName,
|
|
968
|
+
filePath: oldIndexFilePath,
|
|
969
|
+
});
|
|
970
|
+
const changes = (await this.sourceTracker?.getAndFlushChanges()) ?? [];
|
|
971
|
+
await this.writeChanges(changes);
|
|
972
|
+
// A rename of a folder is an unlink followed by an add, so the file watcher will take over initializing this
|
|
973
|
+
// "new" page
|
|
974
|
+
await fs.rename(oldPageFolder, newPageFolder);
|
|
975
|
+
// Now we just clean up the routes
|
|
976
|
+
await this.removeRoute(oldIndexFilePath);
|
|
977
|
+
await this.addRoute(existingRoute, newIndexFilePath);
|
|
978
|
+
const newIndexFile = await readFile(newIndexFilePath);
|
|
979
|
+
if (!newIndexFile) {
|
|
980
|
+
throw new Error(`New index file ${newIndexFilePath} not found`);
|
|
981
|
+
}
|
|
982
|
+
this.tsFiles[newIndexFilePath] = newIndexFile;
|
|
983
|
+
await this.handleNonVisualChangeByDeletingIds(newIndexFilePath, newIndexFile);
|
|
984
|
+
this.emit("renamePage", newIndexFilePath);
|
|
985
|
+
// Re-add the watcher
|
|
986
|
+
this.watcher?.add(newPageFolder);
|
|
987
|
+
this.watcher?.add(oldPageFolder);
|
|
988
|
+
};
|
|
989
|
+
getPageRoots(filePath) {
|
|
990
|
+
const scopeFilePath = path.join(path.dirname(filePath), "index.tsx");
|
|
991
|
+
const currentFile = this.sourceTracker?.getCurrentFiles()[scopeFilePath];
|
|
992
|
+
if (!currentFile) {
|
|
993
|
+
return null;
|
|
994
|
+
}
|
|
995
|
+
return getPageRoots(filePath, currentFile);
|
|
1156
996
|
}
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
997
|
+
getScope(filePath) {
|
|
998
|
+
// get the scope.ts file in the same directory as path
|
|
999
|
+
const scopeFilePath = path.join(path.dirname(filePath), SCOPE_FILE);
|
|
1000
|
+
const currentFile = this.sourceTracker?.getCurrentFiles()[scopeFilePath];
|
|
1001
|
+
if (!currentFile) {
|
|
1002
|
+
console.log("File not found", scopeFilePath);
|
|
1003
|
+
return null;
|
|
1004
|
+
}
|
|
1005
|
+
const scope = getScope(scopeFilePath, currentFile);
|
|
1006
|
+
return scope;
|
|
1007
|
+
}
|
|
1008
|
+
getRoutes() {
|
|
1009
|
+
const routes = [];
|
|
1010
|
+
for (const [path, { file }] of Object.entries(this.routes)) {
|
|
1011
|
+
routes.push({
|
|
1012
|
+
path,
|
|
1013
|
+
component: file,
|
|
1014
|
+
});
|
|
1015
|
+
}
|
|
1016
|
+
return routes;
|
|
1017
|
+
}
|
|
1018
|
+
async addRoute(route, filePath) {
|
|
1019
|
+
if (!this.rootDir) {
|
|
1020
|
+
throw new Error("Root directory not set");
|
|
1021
|
+
}
|
|
1022
|
+
this.routes[route] = { file: this.getRelativeRoutePath(filePath) };
|
|
1023
|
+
await this.writeFile(path.join(this.rootDir, ROUTES_FILE), JSON.stringify(this.routes, null, 2));
|
|
1024
|
+
}
|
|
1025
|
+
async removeRoute(filePath) {
|
|
1026
|
+
if (!this.rootDir) {
|
|
1027
|
+
throw new Error("Root directory not set");
|
|
1028
|
+
}
|
|
1029
|
+
const relativeFilePath = this.getRelativeRoutePath(filePath);
|
|
1030
|
+
this.routes = Object.fromEntries(Object.entries(this.routes).filter(([_, value]) => value.file !== relativeFilePath));
|
|
1031
|
+
await this.writeFile(path.join(this.rootDir, ROUTES_FILE), JSON.stringify(this.routes, null, 2));
|
|
1032
|
+
}
|
|
1033
|
+
async writeChanges(changes, callback) {
|
|
1034
|
+
return Promise.all(changes.map(async ({ fileName, source, kind }) => {
|
|
1035
|
+
await this.writeFile(fileName, source, kind);
|
|
1036
|
+
return callback?.(fileName, source);
|
|
1037
|
+
}));
|
|
1038
|
+
}
|
|
1039
|
+
getNodeForWidgetSourceId(id) {
|
|
1040
|
+
return this.sourceTracker?.getElementToLocation(id);
|
|
1041
|
+
}
|
|
1042
|
+
getAstForWidgetSourceId(id) {
|
|
1043
|
+
const filePath = this.sourceTracker?.getElementToFilePath(id);
|
|
1044
|
+
if (!filePath) {
|
|
1045
|
+
return null;
|
|
1046
|
+
}
|
|
1047
|
+
return this.sourceTracker?.getCurrentFiles()[filePath]?.ast;
|
|
1048
|
+
}
|
|
1049
|
+
renameIdentifierInApis = async ({ elementId, oldName, newName, parentBinding, }) => {
|
|
1050
|
+
const apisInScope = structuredClone(this.getApisInScope(elementId));
|
|
1051
|
+
await this.renameManager.renameEntityInApis({
|
|
1052
|
+
oldName,
|
|
1053
|
+
newName,
|
|
1054
|
+
parentBinding,
|
|
1055
|
+
apis: apisInScope,
|
|
1056
|
+
});
|
|
1057
|
+
// only save the APIs that have changed
|
|
1058
|
+
await Promise.all(apisInScope.map(({ api, filePath }) => {
|
|
1059
|
+
if (isEqual(api?.apiPb, this.apiFiles[filePath]?.apiPb)) {
|
|
1060
|
+
return Promise.resolve();
|
|
1161
1061
|
}
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1062
|
+
return this.writeFile(filePath, yaml.stringify(api?.apiPb), "api");
|
|
1063
|
+
}));
|
|
1064
|
+
};
|
|
1065
|
+
getApisInScope = (elementId) => {
|
|
1066
|
+
const filePath = this.sourceTracker?.getElementToFilePath(elementId);
|
|
1067
|
+
if (!filePath) {
|
|
1068
|
+
return [];
|
|
1069
|
+
}
|
|
1070
|
+
const pagePath = path.dirname(filePath);
|
|
1071
|
+
return Object.entries(this.apiFiles)
|
|
1072
|
+
.filter(([filePath]) => filePath.includes(pagePath))
|
|
1073
|
+
.map(([filePath, api]) => ({ api, filePath }));
|
|
1074
|
+
};
|
|
1075
|
+
getRelativeRoutePath(filePath) {
|
|
1076
|
+
if (!this.rootDir) {
|
|
1077
|
+
throw new Error("Root directory not set");
|
|
1078
|
+
}
|
|
1079
|
+
// no leading slash
|
|
1080
|
+
return path.relative(path.join(this.rootDir, PAGES_DIRECTORY), filePath);
|
|
1168
1081
|
}
|
|
1169
1082
|
// Utilities for converting server API format to Client API format
|
|
1170
1083
|
// We internally save the API as the server does, but we return should always return it
|
|
@@ -1181,21 +1094,19 @@ export const ${name} = ${name}Scope.entities;
|
|
|
1181
1094
|
},
|
|
1182
1095
|
};
|
|
1183
1096
|
}
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
};
|
|
1198
|
-
async processApiFileUpdates(filePath, fileType) {
|
|
1097
|
+
formatApisToClientApis(serverApis) {
|
|
1098
|
+
return Object.keys(serverApis).reduce((acc, key) => {
|
|
1099
|
+
if (!serverApis[key]) {
|
|
1100
|
+
return acc;
|
|
1101
|
+
}
|
|
1102
|
+
acc[key] = {
|
|
1103
|
+
api: this.createClientApi(serverApis[key]),
|
|
1104
|
+
scopeId: serverApis[key].scopeId,
|
|
1105
|
+
};
|
|
1106
|
+
return acc;
|
|
1107
|
+
}, {});
|
|
1108
|
+
}
|
|
1109
|
+
async processApiUpdates(filePath, fileType) {
|
|
1199
1110
|
let yamlPath = filePath;
|
|
1200
1111
|
if (fileType.type === "python-api-step" ||
|
|
1201
1112
|
fileType.type === "js-api-step") {
|
|
@@ -1213,7 +1124,7 @@ export const ${name} = ${name}Scope.entities;
|
|
|
1213
1124
|
const parsedData = { apiPb: apiContent?.api };
|
|
1214
1125
|
if (!(yamlPath in this.apiFiles &&
|
|
1215
1126
|
isEqual(this.apiFiles[yamlPath]?.apiPb, parsedData.apiPb))) {
|
|
1216
|
-
const { updatedApi, pageName, isNewApi } = this.
|
|
1127
|
+
const { updatedApi, pageName, isNewApi } = this.updateApi({
|
|
1217
1128
|
api: parsedData.apiPb,
|
|
1218
1129
|
stepPathMap: apiContent.stepPathMap,
|
|
1219
1130
|
}, yamlPath);
|
|
@@ -1224,7 +1135,7 @@ export const ${name} = ${name}Scope.entities;
|
|
|
1224
1135
|
return;
|
|
1225
1136
|
}
|
|
1226
1137
|
if (isNewApi) {
|
|
1227
|
-
await this.
|
|
1138
|
+
await this.createScopedApi(updatedApi);
|
|
1228
1139
|
}
|
|
1229
1140
|
this.emit("apiManualUpdate", {
|
|
1230
1141
|
api: this.createClientApi(updatedApi),
|
|
@@ -1240,56 +1151,9 @@ export const ${name} = ${name}Scope.entities;
|
|
|
1240
1151
|
logger.error(`Error updating API: ${yamlPath}, error: ${error}`);
|
|
1241
1152
|
}
|
|
1242
1153
|
}
|
|
1243
|
-
updateInternalApiData = (content, path) => {
|
|
1244
|
-
if (!this.rootDir) {
|
|
1245
|
-
throw new Error("Root directory not set");
|
|
1246
|
-
}
|
|
1247
|
-
const { api: apiContents, stepPathMap } = content;
|
|
1248
|
-
const pageName = getPageName(path);
|
|
1249
|
-
let scopeId = this.sourceTracker?.getScopeDefinitionForPage(pageName)?.id;
|
|
1250
|
-
if (!scopeId) {
|
|
1251
|
-
console.warn("Scope ID not found for API", apiContents.metadata.name);
|
|
1252
|
-
scopeId = "";
|
|
1253
|
-
}
|
|
1254
|
-
const updatedApi = {
|
|
1255
|
-
apiPb: yaml.parse(JSON.stringify(apiContents)),
|
|
1256
|
-
pageName,
|
|
1257
|
-
stepPathMap,
|
|
1258
|
-
scopeId,
|
|
1259
|
-
};
|
|
1260
|
-
const isNewApi = !this.apiFiles[path];
|
|
1261
|
-
this.apiFiles[path] = updatedApi;
|
|
1262
|
-
return { updatedApi, pageName, isNewApi };
|
|
1263
|
-
};
|
|
1264
|
-
renameIdentifierInApis = async ({ elementId, oldName, newName, parentBinding, }) => {
|
|
1265
|
-
const apisInScope = structuredClone(this.getApisInScope(elementId));
|
|
1266
|
-
await this.renameManager.renameEntityInApis({
|
|
1267
|
-
oldName,
|
|
1268
|
-
newName,
|
|
1269
|
-
parentBinding,
|
|
1270
|
-
apis: apisInScope,
|
|
1271
|
-
});
|
|
1272
|
-
// only save the APIs that have changed
|
|
1273
|
-
await Promise.all(apisInScope.map(({ api, filePath }) => {
|
|
1274
|
-
if (isEqual(api?.apiPb, this.apiFiles[filePath]?.apiPb)) {
|
|
1275
|
-
return Promise.resolve();
|
|
1276
|
-
}
|
|
1277
|
-
return this.writeFile(filePath, yaml.stringify(api?.apiPb), "api");
|
|
1278
|
-
}));
|
|
1279
|
-
};
|
|
1280
|
-
getApisInScope = (elementId) => {
|
|
1281
|
-
const filePath = this.sourceTracker?.getElementToFilePath(elementId);
|
|
1282
|
-
if (!filePath) {
|
|
1283
|
-
return [];
|
|
1284
|
-
}
|
|
1285
|
-
const pagePath = path.dirname(filePath);
|
|
1286
|
-
return Object.entries(this.apiFiles)
|
|
1287
|
-
.filter(([filePath]) => filePath.includes(pagePath))
|
|
1288
|
-
.map(([filePath, api]) => ({ api, filePath }));
|
|
1289
|
-
};
|
|
1290
1154
|
}
|
|
1291
1155
|
// Add new mock implementation
|
|
1292
|
-
export class MockFileSyncManager extends
|
|
1156
|
+
export class MockFileSyncManager extends FileSyncManager {
|
|
1293
1157
|
tsFiles = {};
|
|
1294
1158
|
apiFiles = {};
|
|
1295
1159
|
async watch(_watcher, _path) {
|
|
@@ -1343,7 +1207,8 @@ async function readFile(path) {
|
|
|
1343
1207
|
// in order for try-catch to work, we need to intentionally await the readFile call,
|
|
1344
1208
|
// otherwise the error won't be be caught
|
|
1345
1209
|
// see: https://github.com/nodejs/node/issues/51894#issuecomment-1974017737
|
|
1346
|
-
|
|
1210
|
+
const content = await fs.readFile(path, "utf-8");
|
|
1211
|
+
return content;
|
|
1347
1212
|
}
|
|
1348
1213
|
catch (e) {
|
|
1349
1214
|
getLogger().error(`error reading file: ${path}`, getErrorMeta(e));
|
|
@@ -1364,4 +1229,12 @@ async function readFiles(dir) {
|
|
|
1364
1229
|
const getMergedApiContent = async (path) => {
|
|
1365
1230
|
return readAppApiYamlFile(path.split("/").slice(0, -1).join("/"));
|
|
1366
1231
|
};
|
|
1232
|
+
const getPageName = (path) => {
|
|
1233
|
+
const parts = path.split("/");
|
|
1234
|
+
const pagesIndex = parts.findIndex((part) => part === "pages");
|
|
1235
|
+
if (pagesIndex !== -1 && parts[pagesIndex + 1]) {
|
|
1236
|
+
return parts[pagesIndex + 1] ?? "";
|
|
1237
|
+
}
|
|
1238
|
+
return "";
|
|
1239
|
+
};
|
|
1367
1240
|
//# sourceMappingURL=file-system-manager.js.map
|